Wargame: Natas
Level: 27
Category: Web Exploitation
Description
A login/registration form stores users in MySQL. The natas28 user already exists. The goal is to log in as natas28 and retrieve their password, without knowing it.
Overview
- MySQL
VARCHAR(64)silently truncates strings that exceed 64 characters on insertion. - MySQL also ignores trailing spaces in
VARCHARcomparisons by default. - We register a username of
"natas28" + 57 spaces + "d"(65 chars); MySQL truncates it to"natas28" + 57 spaces(64 chars) and stores it with our empty password. - The registration existence check is bypassed because the 65-char string (with the trailing “d”) does not match the stored 7-char
"natas28"row. - Logging in with
"natas28" + 57 spacescauses MySQL’s trailing-space-insensitive comparison to match the realnatas28row, and the application returns that user’s password.
Background: SQL Truncation Attack
- MySQL’s
PAD_CHAR_TO_FULL_LENGTHSQL mode is disabled by default;VARCHARcomparisons strip trailing spaces. - The
STRICT_TRANS_TABLESmode controls whether over-length inserts are truncated silently or rejected; many older deployments disable it. - When two rows can match the same lookup key due to trailing-space normalisation, the application may return the wrong user’s data.
- The fix is to enable
STRICT_TRANS_TABLES, enforce unique constraints after normalising usernames, and trim input before storage.
Solution
Step 1: Register the Padded Username
- Submit a registration with username
"natas28" + " "*57 + "d"(65 chars) and an empty password. - MySQL truncates the username to 64 chars; the existence check does not match
"natas28"because the 65-char input does not equal the stored 7-char value.
padded = "natas28" + " " * 57 + "d"
requests.post(url, auth=auth, data={"username": padded, "password": ""})
Step 2: Log In with the Truncated Username
- Submit a login with username
"natas28" + " "*57(exactly 64 chars, no trailing “d”) and an empty password. - MySQL’s comparison ignores trailing spaces; this matches the real
natas28row. - The application returns the password stored for that user.
login = "natas28" + " " * 57
r = requests.post(url, auth=auth, data={"username": login, "password": ""})
Step 3: Extract the Password
- Parse the response body; the natas28 password is in the line containing
password.
Password
skrwxciAe6Dnb0VfFDzDEHcCzQmv3Gd4