前置知识
Tcache
头插头取,FILO
tcache_perthread_struct
1 | /* There is one of these for each thread, which contains the |
每个 thread 都会维护一个 tcache_perthread_struct
,它是整个 tcache 的管理结构,一共有 TCACHE_MAX_BINS
个计数器和 TCACHE_MAX_BINS
项 tcache_entry,其中
- tcache_entry 用单向链表的方式链接了相同大小的处于空闲状态(free 后)的 chunk,这一点上和 fastbin 很像。
- counts 记录了 tcache_entry 链上空闲 chunk 的数目,每条链上最多可以有 7 个 chunk。
用图表示大概是:
基本工作方式
- 第一次 malloc 时,会先 malloc 一块内存用来存放
tcache_perthread_struct
。 - free 内存,且 size 小于 small bin size 时
- tcache 之前会放到 fastbin 或者 unsorted bin 中
- tcache 后:
- 先放到对应的 tcache 中,直到 tcache 被填满(默认是 7 个)
- tcache 被填满之后,再次 free 的内存和之前一样被放到 fastbin 或者 unsorted bin 中
- tcache 中的 chunk 不会合并(不取消 inuse bit)
- malloc 内存,且 size 在 tcache 范围内
- 先从 tcache 取 chunk,直到 tcache 为空
- tcache 为空后,从 bin 中找
- tcache 为空时,如果
fastbin/smallbin/unsorted bin
中有 size 符合的 chunk,会先把fastbin/smallbin/unsorted bin
中的 chunk 放到 tcache 中,直到填满。之后再从 tcache 中取;因此 chunk 在 bin 中和 tcache 中的顺序会反过来
tcache的大小范围为:0x20~0x410(大小包含堆块结构即: malloc(0~0x408))
Tcache_poison的原理
tcache在申请时不会检查指针指向位置是否合法,并且tcache的fd指针指向的不是堆块结构体的首地址,而是实际malloc出的空闲地址的首地址,例子如下:
1 | A = malloc(0x8) |
例子
函数如下:
add(): 输入大小与内容,会记录大小,最多可以申请16次,索引为0-15依次增加且不会因为free重用
del(): 输入索引,free对应的chunk之后并未置零,有uaf漏洞
show(): 输入索引,打印对应chunk的内容
edit(): 输入索引与内容,根据add()时的大小输入内容,由于uaf可以在free后继续修改
解题思路:
1 | 申请一个大于0x410大小的块 # 0 |
exp:
1 | from Excalibur2 import * |
tips:
- gdb中可以用
p &__free_hook/p &__malloc_hook
打印free_hook/malloc_hook地址 - 安装pwndbg与pwngdb后,可以用
libc
打印libc基地址,或者可以用vmmap手动找