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。
图解
释放堆块时:如果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的输出,分配成功。
使用glibc-2.27的输出,nil说明漏洞已修复。
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。
2.检查保护。
3.试运行。
可见为菜单题。
1-创建一个gundam机器人
2-访问gundamu
3-销毁一个gundam
4-炸毁工厂
5-退出
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)。
blow up后
计算这个栈地址与libc基地址的偏移。
偏移为:0x3dac78
在申请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处的地址减去上面计算出的固定偏移即可得到栈的基地址。
进而可以由libc-2.26.so得到system和__free_hook地址。
(2)利用double free制造tcache poisoning到&__free_hook
依次释放2,1,0,0。此时tcache bin状态如下。
blow up 后
已经形成了double free。此时在申请一个堆块将会把chunk0申请出来,将其内容改为__free_hook的地址。
因为此时chunk0依然在tcache bin(0x110)的链上,所以__free_hook会被挂在tcache bins的链上。
(3)将物理堆块为chunk0,逻辑为chunk1的factory[1]_buf改写为'/bin/sh\x00',修改__free_hook为system地址。
修改factory[1]_buf为'/bin/sh\x00'
此时tcache bin中还剩下__free_hook地址。
再次申请得到__free__hook+0x10处的堆块,此时修改__free_hook为system。
(4)free('/bin/sh\x00');
最后 destory(1),也就是free('/bin/sh\x00')即可getshell
1.修改rpath
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2023-10-14 20:14
被jelasin编辑
,原因: