glibc 源码网址:https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c
ptmallloc2在libc2.26中引入了Tcache这种无需对arena上锁就可以使用的小堆块。tcache是单链表结构,每条链上最多可以有 7 个 chunk,free 的时候当对应的 tcache bin 满了才放入fastbin,unsorted bin,malloc的时候优先去tcache bin找。
其数据结构如下。
每个线程默认64个单链表结构的bins,每个bins最多存放7个chunk。chunk在64位机器以16字节递增,从24到1032字节。在32位机器上以8字节递增,从12到512字节。因此tcache只能存放non-large的chunk。
图解
![](upload/attach/202307/958172_3VXXSF45THTQ63W.jpg)
释放堆块时:如果chunk是non-large chunk,并且对应bins未满7个,则放入对应bins。
分配堆块时:
(1)如果fastbins或者small bins中成功返回一个需要的chunk,那么对应fastbins或者small bins中的剩余chunk会被放进相应的tcache bin中,直到相应tcache bin填满7个或者对应的fastbins或者small bins为空。chunk在tcache bin中顺序与fastbin相反,与small bin中顺序相同。
(2)unsorted bin 中符合用户要求的的chunk取出时,chunk 合并等其他操作,每一个符合要求的chunk会优先放入tcache,然后从 tcache 中返回其中一个。如果tcache已满则直接返回。
从tcache中取出堆块。
(1)在__libc_malloc()调用_int_malloc()前,如果tcache bin中有符合要求的chunk,则直接返回。
(2)(默认不执行)。在unsorted bin最后如果找到了可以返回的块,并且 mp_.tcache_unsorted_limit(默认为0) 次数小于处理 unsorted count(即tcache中装满了对应的chunk)那么就会从其中拉出一个chunk出来返回。
(3)在unsorted bin的遍历之后 如果unsorted bin中存在可以返回的chunk 那么在遍历unsorted bin之后,则调用一次tcache_get返回给用户使用。
tcache中的chunk不会合并。chunk的prev_inuse=1。
__libc_malloc()使用request2size()转换堆块为实际大小时,不会进行整数溢出检查。请求一个接近(SIZE_MAX)的堆块将导致溢出,使malloc错误返回tcache bin中的堆块。
源码
使用glibc-2.26的输出,分配成功。
![](src)
![](upload/attach/202307/958172_J423U7BS3AVPUMN.jpg)
使用glibc-2.27的输出,nil说明漏洞已修复。
![](upload/attach/202307/958172_GYTCUQJPMZ3ZT9S.jpg)
libc-2.29新增加double free检查,方法是在tcache_entry结构体中新增加标志位key来检查chunk是否在tcache bin中。当 free 掉一个堆块进入 tcache 时,假如堆块的 bk 位存放的key
== tcache_key
, 就会遍历这个大小的 Tcache ,假如发现同地址的堆块,则触发 double Free 报错。因为chunk的key保存在bk位置,只需将其修改即可绕过double free检查。
说明:附件中的赛题已经用patchelf改好环境。
1.修改rpath。
![](upload/attach/202307/958172_5UPXC4BVAGDJUG8.jpg)
2.检查保护。
![](upload/attach/202307/958172_7UJ686BG6C4DDVJ.jpg)
3.试运行。
可见为菜单题。
1-创建一个gundam机器人
2-访问gundamu
3-销毁一个gundam
4-炸毁工厂
5-退出
![](upload/attach/202307/958172_YJXK6788EQ5JJG9.jpg)
4.逆向分析。
1-分析Build函数
不难分析出gundam结构体
2-Visit函数
3-Destroy函数
4-BlowUp函数
5.漏洞利用
(1)利用unsorted bin attack泄露main_arean地址进而泄露libc基址。
申请9个chunk,释放7个填满tcache,在释放一个进入unsorted bin,剩下一个阻隔top chunk防止合并。
可以看到unsorted bin中的chunk的fd和bk指向了一个栈地址(main_arena+88)。
![](upload/attach/202307/958172_ZEASEE9NZU44C5S.jpg)
blow up后
![](upload/attach/202307/958172_79F5P5RSTUVS4CX.jpg)
计算这个栈地址与libc基地址的偏移。
![](upload/attach/202307/958172_H876K6X845YEQTM.jpg)
偏移为:0x3dac78
![](upload/attach/202307/958172_BMEDZ8WHXWHZ6GA.jpg)
在申请8个chunk,将unsorted bin中的chunk申请出来,再利用visit()函数泄露main_arena+88处的栈地址。
此时需要注意,chunk优先从tcache取出,然后Type[7]才是unsorted bin中的chunk。由于第8个chunk的fd指向main_arena+88处的地址,
所以此时只需要接收6个字节(因为64位栈地址前2字节为'\x00',并且用%s打印地址)然后用'\x00'补齐即可。
再用main_arena+88处的地址减去上面计算出的固定偏移即可得到栈的基地址。
![](upload/attach/202307/958172_82AMDYGDZBJYGEX.jpg)
进而可以由libc-2.26.so得到system和__free_hook地址。
(2)利用double free制造tcache poisoning到&__free_hook
依次释放2,1,0,0。此时tcache bin状态如下。
![](upload/attach/202307/958172_R5CPWZN2CM9D8X6.jpg)
![](upload/attach/202307/958172_5NNQRGXBK3K8GET.jpg)
![](upload/attach/202307/958172_4FZWRZH9EK2D29U.jpg)
![](upload/attach/202307/958172_XQ7DM265PKATFMY.jpg)
blow up 后
![](upload/attach/202307/958172_AKAUSN3ZH6Q75K6.jpg)
已经形成了double free。此时在申请一个堆块将会把chunk0申请出来,将其内容改为__free_hook的地址。
因为此时chunk0依然在tcache bin(0x110)的链上,所以__free_hook会被挂在tcache bins的链上。
![](upload/attach/202307/958172_VRCZDXZ7HZPPG3A.jpg)
(3)将物理堆块为chunk0,逻辑为chunk1的factory[1]_buf改写为'/bin/sh\x00',修改__free_hook为system地址。
修改factory[1]_buf为'/bin/sh\x00'
![](upload/attach/202307/958172_GBGYY98JXSD8Z27.jpg)
![](upload/attach/202307/958172_DYS48TUVAJY4QZ2.jpg)
此时tcache bin中还剩下__free_hook地址。
![](upload/attach/202307/958172_YDCJ64QZPUGV3WK.jpg)
再次申请得到__free__hook+0x10处的堆块,此时修改__free_hook为system。
![](upload/attach/202307/958172_VRQR9A9XJBAS7JG.jpg)
(4)free('/bin/sh\x00');
最后 destory(1),也就是free('/bin/sh\x00')即可getshell
![](upload/attach/202307/958172_SMCEP4CKXCCRSW4.jpg)
1.修改rpath
![](upload/attach/202307/958172_PFF4YXVDNYUVCXK.jpg)
[注意]看雪招聘,专注安全领域的专业人才平台!
最后于 2023-10-14 20:14
被jelasin编辑
,原因: