House of Spirit
fake fastbin chunk를 해제(free)하면서 malloc 할당 시 임의의 포인터를 반환하도록 함으로써 원하는 주소에 값을 쓸 수 있게 하는 공격방식이다.
>> 같은 크기만큼을 재할당할 때, 같은 주소를 반환하는 fastbin의 특성을 이용 (fastbin을 공격하는 기법)
stack에 가짜 청크를 쓰고 해당 stack의 주소에서 0x10(64bit기준) 더한 주소로 free()를 호출할 수 있을 때 구현가능하다.
stack에 fastbin에 해당하는 크기의 fake chunk를 작성하고 malloc()으로 메모리를 할당한다. 그리고 fake chunk의 주소+0x10인 주소에 free()를 호출해 해당 chunk가 fastbin에 저장되게 한다. 해당 chunk의 크기로 다시 malloc()을 호출하면 fastbin에 저장된 fake_chunk의 포인터(주소)를 반환한다. 즉, 해당 포인터는 stack 영역의 메모리이다.
Example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(){
unsigned long *ptr;
unsigned long fake_chunk[20];
fprintf(stderr,"fakeChunk : %p\n",fake_chunk);
fprintf(stderr,"ptr : %p\n",&ptr);
fake_chunk[1] = 0x80;
fake_chunk[17] = 0x1000;
malloc(1000);
ptr = fake_chunk + 2;
free(ptr);
char *stack = malloc(0x70);
fprintf(stderr,"Stack : %p\n",stack);
}
△ house_of_spirit.c스택에 fake_chunk를 작성한 후 malloc()로 메모리 할당을 요청한다. // 배열 fake_chunk[]를 만들어 스택변수로 작성하고 malloc()로 메모리를 할당한다.free()에 ptr에 저장된 값에 대해 메모리 해제를 요청한다.다시 malloc()에 크기가 0x70인 메모리 할당을 요청한다.
b1: stack에서 작성된 fake_chunk 확인
b2: heap 공간의 생성과 arena의 값 확인
b3: free()에 fake_chunk의 포인터를 전달한 후 fastbins[]의 변화 확인
b4: malloc()에서 반환되는 포인터 확인
△ stack에 작성된 fake_chunk 확인
fake_chunk의 크기(0x80)를 0x7fffffffde00 - 0xa8(168) [= b1까지 실행했을 때의 rbp에 저장된 값 - 0xa8]에 저장
다음 chunk의 크기(0x1000)를 0x7fffffffde00 - 0x28(40) [= b1까지 실행했을 때의 rbp에 저장된 값 - 0x28]에 저장
▶ stack에 fake_chunk가 생성되었다!
$3 = { mutex = 0, flags = 1, fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x6023f0, last_remainder = 0x0, bins = {0x7ffff7dd1b78 <main_arena+88>, ... ...}, binmap = {0, 0, 0, 0}, next = 0x7ffff7dd1b20 <main_arena>, next_free = 0x0, attached_threads = 1, system_mem = 135168, max_system_mem = 135168 } |
[4] ni (main+119에서 멈춤)(너무 길어서 캡쳐할 수 없었다...)
[3]은 malloc()으로 메모리 할당을 요청하기 전이기 때문에 heap 공간이 생성되지 않아 arena에는 최소한의 정보만 가지고 있다.
[4]는 malloc()으로 메모리 할당을 하고 heap 공간이 생성되어 flags, bins. system_mem, max_system_mem 등의 값들이 초기화된다.
free()에 fake_chunk 포인터(rdi: 0x7fffffffdd60)를 전달하고 메모리를 해제하면 해당 chunk의 포인터는 fastbinsY[6]에 배치된다.
해당 chunk를 재할당받기 위해 malloc()으로 크기가 0x70인 메모리의 할당을 요청하면 fastbinsY[6]에 배치된 chunk를 재할당해 포인터(0x7fffffffdd60)를 반환한다.
_int_free()
doongdangdoongdangdong.tistory.com/78?category=869864 에도 나와있음
- "free(): invalid pointer"
if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())
#if TRIM_FASTBINS
/*
If TRIM_FASTBINS set, don't place chunks
bordering top into fastbins
*/
&& (chunk_at_offset(p, size) != av->top)
#endif
) {
if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
|| __builtin_expect (misaligned_chunk (p), 0))
{
errstr = "free(): invalid pointer";
errout:
메모리가 해제된 chunk의 포인터가 올바르게 정렬(align)된 포인터인지 확인하기 위해 misaligned_chunk () 호출 → 올바르지 않다면 "free(): invalid pointer"라는 오류 메시지와 함께 종료
- "free(): invalid size"
if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
{
errstr = "free(): invalid size";
goto errout;
}
해당 chunk의 size에 저장된 값이 MINSIZE보다 작고 해당 값이 정상적으로 정렬된 값인지 확인 → 정상적이지 않다면 "free(): invalid size" 라는 오류 메시지와 함께 종료
- "free(): invalid next size (fast) "
if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())
#if TRIM_FASTBINS
/*
If TRIM_FASTBINS set, don't place chunks
bordering top into fastbins
*/
&& (chunk_at_offset(p, size) != av->top)
#endif
) {
if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size))
<= 2 * SIZE_SZ, 0)
|| __builtin_expect (chunksize (chunk_at_offset (p, size))
>= av->system_mem, 0))
{
/* We might not have a lock at this point and concurrent modifications
of system_mem might have let to a false positive. Redo the test
after getting the lock. */
if (have_lock
|| ({ assert (locked == 0);
__libc_lock_lock (av->mutex);
locked = 1;
chunksize_nomask (chunk_at_offset (p, size)) <= 2 * SIZE_SZ
|| chunksize (chunk_at_offset (p, size)) >= av->system_mem;
}))
{
errstr = "free(): invalid next size (fast)";
goto errout;
}
if (! have_lock)
{
__libc_lock_unlock (av->mutex);
locked = 0;
}
}
free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);
set_fastchunks(av);
unsigned int idx = fastbin_index(size);
fb = &fastbin (av, idx);
/* Atomically link P to its fastbin: P->FD = *FB; *FB = P; */
mchunkptr old = *fb, old2;
unsigned int old_idx = ~0u;
해당 chunk의 크기가 fastbin에 해당하는지 → chunk의 size에 저장된 값의 조건 검사 → 조건들을 통과하면 정상적인 청크(=fastbin에 저장)
chunk의 size에 저장된 값에 대한 조건
'size'에 저장된 값에서 사용된 flag bit를 모두 제거한 값이 2*SIZE_SZ 보다 작거나 같은지 확인한다.
그리고 chunk의 'size'에 저장된 값이 av->system_mem의 값보다 크거나 같은지 확인한다. 이 때 해당 arena가 잠겨있지 않으면 av->system_mem에 저장된 값이 FALSE일 수 있다. 따라서 해당 arena를 잠근 후에 다시 확인해야 한다.
위의 조건들이 충족되면 다음 청크의 'size'에 저장된 값이 비정상이므로 "free(): invalid next size (fast)" 라는 오류메시지와 함께 종료된다.
'System > System Hacking' 카테고리의 다른 글
[Heap] Memory Leak (0) | 2020.11.16 |
---|---|
[Heap] House of Force (0) | 2020.11.03 |
[Heap] UAF(Use-After-Free) (0) | 2020.09.19 |
[Heap] Double Free 취약점 (0) | 2020.09.17 |
[Heap] Security Check (0) | 2020.09.13 |