本来不想再弄glibc
的东西了,但是最近的2.29
的题越来越多。。。考虑到以后去给人培训的话,会有知识盲区,不太好,别人来问题的话,不会这方面也很没面子,还是决定花几天时间调一下。也算复习一下tcache
相关的东西了。
2.26~2.28
都没有double free
与tcache->counts[tc_idx]
的检测,从2.29
起加了double free
和 调用tcache_get()
之前检测tcache->counts[tc_idx]
是否大于0这两个检测。
从2.28
起对unsortedbin
解链进行了检查,2.23~2.27
中unsortedbin_attack
都适用。
从2.30
起tcache_perthread_struct
结构体中的counts
数组的成员类型由字符转变为uint16_t
(两个字符)。所以2.30
之前的tcache_perthread_struct
结构体大小为0x240(0x40+0x200)
,2.30
之后变为0x290(0x40*2+0x200)
。
以下内容都基于GLIBC_2.30
:
一些结构体:
1 |
|
一些相关函数:
1 | /* Caller must ensure that we know tc_idx is valid and there's room |
在_int_free()
中:
1 | static void |
在__libc_malloc
中:
1 | void * |
进入到_int_malloc
之后,有多处用到了tcache
:
1 |
|
在heap
的最开始处,会申请一个0x290
的chunk来管理所有tcache
,也就是tcache_perthread_struct
:
1 | typedef struct tcache_perthread_struct |
可以放入tcache
中的size(包含chunk_header)
范围:0x20(tcache_entry[0])~0x410(tcache_entry[63])
的闭区间。
twochunk
思路
calloc
有两个地方需要注意:
- 不从
tcache
中取。 - 取出后内容会清0,不会留下脏数据。
题目限制:
- 一次
malloc(0xE9)
的机会 - 一次
malloc(0x88)
且可以向其中输入东西的机会 - 两个放chunk的位置
- 不限制次数,size限制在
0x80~0x3FF
之间的calloc
的机会 - 一次打印申请出来chunk前8字节的机会
- 一次溢出的机会
- 一次打印
name
和message
的机会 - 一个函数指针后门
漏洞就是为堆溢出,所以猜测思路应该是篡改next_chunk
的fd && bk
,然后在解链时进行攻击。
利用点为:从smallbin
中取出一个chunk后,会将剩余chunk解链进入tcache
,这个过程的解链是没有自闭检测的,且不用担心因为smallbin
被破坏而程序崩掉,因为当tcache
满了会自动退出循环。
构造出0x90
的tcache
中有5个chunk,smallbin
中有两个chunk的情形,之后通过溢出改后放入smallbin
的chunk的fd && bk
,fd
不能动,bk
改为指向fake_chunk
,fake_chunk
的bk
需要指向可写的地址,循环结束后的情形为:
0x90
的tcache
已满,且头部的chunk为fake_chunk
- 在
fake_chunk->bk
处留下了libc
的脏数据
泄露+任意地址写即可。
exp
1 | #coding:utf-8 |
one_punch_man
思路
有UAF
的话可以直接伪造next_chunk
与fake_chunk
,需要注意的是fake_chunk->bk
必须是个可写的地址,所以我这里找的是__malloc_hook
附近的一个地址。最后改__malloc_hook
为gadget
,抬栈进行rop
。
exp
1 | #coding:utf-8 |
Buu_RedPacket
思路
当时amain9
大佬发给我的时候对2.29
不是很感兴趣就没有去花时间研究,现在重新做了一下。。。看了一下之后发现不就是抄的one_punch_man
么。。稍微改了改,加了几个吓唬人的检测和限制罢了。
exp
1 | #coding:utf-8 |
PlainNote
其他
fastbin && unsortedbin && topchunk
在main_arena
中的位置。
1 | gdb-peda$ heapinfo |
unsortedbin && smallbin
的连接和进出方式: