linux 堆溢出学习之house of spirit(1) malloc maleficarum hos翻译

版权声明:转载请保留出处 https://blog.csdn.net/qq_29343201/article/details/59477082

综述

house of spirit是一种常用的堆溢出技术,而在如今的malloc实现中依然没有对这种方法进行保护,所以在目前还是一种有效的堆溢出技术。下面我们先从这种方法的来源之本讲起,即2005 Malloc Maleficarum

原文

 The House of Spirit

The House of Spirit is primarily interesting because of the nature
of the circumstances leading to its application. It is the only
House in the Malloc Maleficarum that can be used to leverage both a
heap and stack overflow. This is because the first step is not to
control the header information of a chunk, but to control a pointer
that is passed to free(). Whether this pointer is on the heap or
not is largely irrelevant.

The general idea involves overwriting a pointer that was previously
returned by a call to malloc(), and that is subsequently passed to
free(). This can lead to the linking of an arbitrary address into a
fastbin. A further call to malloc() can result in this arbitrary
address being used as a chunk of memory by the application. If the
designer can control the applications use of the fake chunk, then
it is possible to overwrite execution control data.

Assume that the designer has overflowed a pointer that is being
passed to free(). The first problem that must be considered is
exactly what the pointer should be overflowed with. Keep in mind
that the ultimate goal of the House of Spirit is to allow the
designer to overwrite some sort of execution control data by
returning an arbitrary chunk to the application. Exactly what
"execution control data" is doesn't particularly matter so long as
overflowing it can result in execution being passed to a designer
controlled memory location. The two most common examples that are
suitable for use with the House of Spirit are function pointers and
pending saved return addresses, which will herein be referred to as
the "target".

In order to successfully apply the House of Spirit it is necessary
to have a designer controlled word value at a lower address than
the target. This word will correspond to the size field of the
chunk header for the fakechunk passed to free(). This means that
the overflowed pointer must be set to the address of the designer
controlled word plus 4. Furthermore, the size of the fakechunk must
be must be located no more than 64 bytes away from the target. This
is because the default maximum data size for a fastbin entry is 64,
and at least the last 4 bytes of data are required to overwrite the
target.

There is one more requirement for the layout of the fakechunk data
which will be described shortly. For the moment, assume that all of
the above conditions have been met, and that a call to free() is
made on the suitable fakechunk. A call to free() is handled by a
wrapper function called public_fREe():

void
public_fREe(Void_t* mem)
{
    mstate ar_ptr;
    mchunkptr p;          /* chunk corresponding to mem */
    ...
    p = mem2chunk(mem);
    if (chunk_is_mmapped(p))
    {
      munmap_chunk(p);
      return;
    }
    ...
    ar_ptr = arena_for_chunk(p);
    ...
    _int_free(ar_ptr, mem);

In this situation mem is the value that was originally overflowed
to point to a fakechunk. This is converted to the "corresponding
chunk" of the fakechunk's data, and passed to arena_for_chunk() in
order to find the corresponding arena. In order to avoid special
treatment as an mmap() chunk, and also to get a sensible arena, the
size field of the fakechunk header must have the IS_MMAPPED and
NON_MAIN_ARENA bits cleared. To do this, the designer can simply
ensure that the fake size is a multiple of 8. This would mean the
internal function _int_free() is reached:

void_int_free(mstate av, Void_t* mem){
    mchunkptr       p;           /* chunk corresponding to mem */
    INTERNAL_SIZE_T size;        /* its size */
    mfastbinptr*    fb;          /* associated fastbin */
    ...
    p = mem2chunk(mem);
    size = chunksize(p);
    ...
    if ((unsigned long)(size) <= (unsigned long)(av->max_fast))
    {
      if (chunk_at_offset (p, size)->size <= 2 * SIZE_SZ
          || __builtin_expect (chunksize (chunk_at_offset (p, size))
                                          >= av->system_mem, 0))
        {
          errstr = "free(): invalid next size (fast)";
          goto errout;
        }
      ...
      fb = &(av->fastbins[fastbin_index(size)]);
      ...
      p->fd = *fb;
      *fb = p;
    }

This is all of the code in free() that concerns the House of
Spirit. The designer controlled value of mem is again converted to
a chunk and the fake size value is extracted. Since size is
designer controlled, the fastbin code can be triggered simply by
ensuring that it is less than av->max_fast, which has a default of
64 + 8. The final point of consideration in the layout of the
fakechunk is the nextsize integrity tests.

Since the size of the fakechunk has to be large enough to encompass
the target, the size of the nextchunk must be at an address higher
than the target. The nextsize integrity tests must be handled for
the fakechunk to be put in a fastbin, which means that there must
be yet another designer controlled value at an address higher than
the target.

The exact location of the designer controlled values directly
depend on the size of the allocation request that will subsequently
be used by the designer to overwrite the target. That is, if an
allocation request of N bytes is made (such that N <= 64), then the
designer's lower value must be within N bytes of the target and
must be equal to (N + 8). This is to ensure that the fakechunk is
put in the right fastbin for the subsequent allocation request.
Furthermore, the designer's upper value must be at (N + 8) bytes
above the lower value to ensure that the nextsize integrity tests
are passed.

If such a memory layout can be achieved, then the address of this
"structure" will be placed in a fastbin. The code for the
subsequent malloc() request that uses this arbitrary fastbin entry
is simple and need not be reproduced here. As far as _int_malloc()
is concerned the fake chunk that it is preparing to return to the
application is perfectly valid. Once this has occurred it is simply
up to the designer to manipulate the application in to overwriting
the target.

翻译

house of spirit因为其应用情况受到广泛关注,他是这篇文章中提到方法里,唯一一种同时可以利用堆和栈溢出的方法。这是因为他第一步不是去控制一个chunk的头信息,而是去控制一个传给free函数的指针,至于这个指针是不是在堆上并没有太大的关系。

他的中心思想主要是重写一个之前由malloc分配然后被放进free里的一个指针,这就会导致一个任意地址被链接进fastbin。之后的某个malloc调用可以导致这个任意地址被分配作为一个chunk,如果攻击者可以控制这个fake chunk的应用,那么就有机会可以重写关于执行控制的数据。

假设攻击者溢出了一个被放入free调用的指针,需要考虑的第一个问题是用什么来溢出后填充这个指针。需记住的是house of spirit的最终目的是允许攻击者通过返回给这个应用一个任意位置的chunk来重写某些执行控制数据,至于执行控制数据具体是什么并不是太重要只要溢出它能够导致攻击者想要的执行内容被传送到攻击者控制的内存地址。两个最为常见最为适合用house of spirit的例子的指针是函数指针和存储的返回地址,
这里我们把他们称作“目标”。

为了成功应用house of spirit,攻击者必须要求能够控制低于目标的地址的一个字值(word value),这个字(word)将会和被放进free的fake chunk的头的size域对应。这意味着被溢出的指针将会被设置为攻击者控制的字的地址再加上4,以及fake chunk必须离目标不到64字节。这是因为fastbin的默认块大小是64,而至少我们需要最后4个字节来重写目标。

另外,对于fake chunk的数据分布还有一个要求,我们马上将会讲到。现在我们就先假设之前提到的所有要求都已经被满足了,然后一个对free的调用将会在合适的fake chunk上应用。一个对free的调用将会被一个包装函数,名为public_fREe处理:

void 
public_fRE(Void_t* mem)
{
    mstate ar_ptr;
    mchunkptr p; // mem相应的chunk
    ...
    p = mem2chunk(mem);
    if (chunk_is_mmapped(p))
    {
        munmap_chunk(p);
        return;
    }
    ...
    ar_ptr = arena_for_chunk(p);
    ...
    _int_free(ar_ptr, mem);
}

在这种情况下,mem是之前已经被溢出并使得指向fake chunk的一个值,然后被转换为fake chunk相应的chunk指针,然后被传仅arena_for_chunk来找到相应的arena,为了避免对于mmap chunk的特殊处理,以及为了得到一个有用的arena,fake chunk头的size域的IS_MMAPPED和NON_MAIN_ARENA位必须为0. 为了做到这个,攻击者只需要确认fake 的size是8的倍数就可以了。这样的话,_int_free函数就会被调用了:

void _int_free(mstate av, Void_t* mem)
{
    mchunkptr p; // mem相应的chunk
    INTERNAL_SIZE_T size; //size,大小
    mfastbinptr* fb; //联系的fast bin
    ...
    p = mem2chunk(mem);
    size = chunksize(p);
    ...
    if ((unsigned long)(size) <= (unsigned long)(av->max_fast))
    {
        if (chunk_at_offset(p, size)->size <= 2 * SIZE_SZ
            || __builtin_expect(chunksize(chunk_at_offset(p, size))
                                            >= av->system_mem, 0))
        {
            errstr = "free(): invalid next size (fast)";
            goto errout;
        }
        ...
        fb = &(av->fastbins[fastbin_index(size)]);
        ...
        p->fd = *fb;
        *fb = p;
    }
}

这里是free对于使用house of spirit所需要了解的全部代码了。攻击者控制的mem值再次被转换为chunk指针,然后fake的size值被提取出来。因为size已经是攻击者控制的了,只需要保证这个值小于av->max_fast,fastbin的代码就会被执行了,这里,av->max_fast的默认值为64+8。最后fake chunk的布局需要考虑的是如何通过nextsize正确性的检测。

因为fake chunk的大小必须要足够大才能包裹住目标,所以nextchunk的size的地址必须高于目标。为了能够使得fake chunk被放进fastbin,nextsize一正确性检验必须被处理一下,这就意味着必须有另外一个攻击者控制的值在高于目标的地址出现。

攻击者控制的值的具体位置依赖于将被用来重写目标的分配请求的大小,这就是说,如果一个分配请求了N个字节(N <= 64),那么这个攻击者可以控制的低于这个目标地址的值必须在离目标的N 字节以内,并且必须等于N + 8。这是为了保证fake chunk被放在了之后分配请求所需要的正确的fastbin里。另外,攻击者能控制的另外一个,高于目标地址的值必须比低于的那个值的地址高出(N + 8)字节来保证nextsize的正确性检测可以通过。

如果满足了这样一个内存布局,那么这个结构的地址将会被放进fastbin里。其后对于这个已经被控制的fastbin块的malloc请求的代码非常简单,这里就不再给出了。只要_int_malloc被调用,那么这个准备被返回的fake chunk就是有效的。只要这种情况发生了,那么操纵应用来重写目标就非常简单了。

没有更多推荐了,返回首页