fkillrra 2018. 12. 21. 11:57
[heap1.c]

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>

struct internet {
    int priority;
    char *name;
};

void winner()
{
    printf("and we have a winner @ %d\n", time(NULL));
}

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

    struct internet *i1, *i2, *i3;
    
    i1 = malloc(sizeof(struct internet));
    i1->priority = 1;
    i1->name = malloc(8);
    
    i2 = malloc(sizeof(struct internet));
    i2->priority = 2;
    i2->name = malloc(8);

    strcpy(i1->name, argv[1]);
    strcpy(i2->name, argv[2]);

    printf("and that's a wrap folks!\n");
}

heap1도 heap0과 마찬가지로 heap overflow 문제다.


i1 = malloc(sizeof(struct internet));
i1->priority = 1;
i1->name = malloc(8);

i2 = malloc(sizeof(struct internet));
i2->priority = 2;
i2->name = malloc(8);

main()에서 malloc으로 struct internet의 크기 만큼 i1에 할당을 한다.

이후 값을 넣어주는데,

i1->name = malloc(8);을 하는 것을 볼 수 있다.

i2도 똑같이 malloc을 2번해서

총 4개의 chunk를 할당받는다.


(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x080484b9 <main+0>:    push   ebp
0x080484ba <main+1>:    mov    ebp,esp
0x080484bc <main+3>:    and    esp,0xfffffff0
0x080484bf <main+6>:    sub    esp,0x20
0x080484c2 <main+9>:    mov    DWORD PTR [esp],0x8
0x080484c9 <main+16>:    call   0x80483bc <malloc@plt>
0x080484ce <main+21>:    mov    DWORD PTR [esp+0x14],eax
0x080484d2 <main+25>:    mov    eax,DWORD PTR [esp+0x14]
0x080484d6 <main+29>:    mov    DWORD PTR [eax],0x1
0x080484dc <main+35>:    mov    DWORD PTR [esp],0x8
0x080484e3 <main+42>:    call   0x80483bc <malloc@plt>
0x080484e8 <main+47>:    mov    edx,eax
0x080484ea <main+49>:    mov    eax,DWORD PTR [esp+0x14]
0x080484ee <main+53>:    mov    DWORD PTR [eax+0x4],edx
0x080484f1 <main+56>:    mov    DWORD PTR [esp],0x8
0x080484f8 <main+63>:    call   0x80483bc <malloc@plt>
0x080484fd <main+68>:    mov    DWORD PTR [esp+0x18],eax
0x08048501 <main+72>:    mov    eax,DWORD PTR [esp+0x18]
0x08048505 <main+76>:    mov    DWORD PTR [eax],0x2
0x0804850b <main+82>:    mov    DWORD PTR [esp],0x8
0x08048512 <main+89>:    call   0x80483bc <malloc@plt>
0x08048517 <main+94>:    mov    edx,eax
0x08048519 <main+96>:    mov    eax,DWORD PTR [esp+0x18]
0x0804851d <main+100>:    mov    DWORD PTR [eax+0x4],edx
0x08048520 <main+103>:    mov    eax,DWORD PTR [ebp+0xc]
0x08048523 <main+106>:    add    eax,0x4
0x08048526 <main+109>:    mov    eax,DWORD PTR [eax]
0x08048528 <main+111>:    mov    edx,eax
0x0804852a <main+113>:    mov    eax,DWORD PTR [esp+0x14]
0x0804852e <main+117>:    mov    eax,DWORD PTR [eax+0x4]
0x08048531 <main+120>:    mov    DWORD PTR [esp+0x4],edx
0x08048535 <main+124>:    mov    DWORD PTR [esp],eax
0x08048538 <main+127>:    call   0x804838c <strcpy@plt>
0x0804853d <main+132>:    mov    eax,DWORD PTR [ebp+0xc]
0x08048540 <main+135>:    add    eax,0x8
0x08048543 <main+138>:    mov    eax,DWORD PTR [eax]
0x08048545 <main+140>:    mov    edx,eax
0x08048547 <main+142>:    mov    eax,DWORD PTR [esp+0x18]
0x0804854b <main+146>:    mov    eax,DWORD PTR [eax+0x4]
0x0804854e <main+149>:    mov    DWORD PTR [esp+0x4],edx
0x08048552 <main+153>:    mov    DWORD PTR [esp],eax
0x08048555 <main+156>:    call   0x804838c <strcpy@plt>
0x0804855a <main+161>:    mov    DWORD PTR [esp],0x804864b
0x08048561 <main+168>:    call   0x80483cc <puts@plt>
---Type <return> to continue, or q <return> to quit---
0x08048566 <main+173>:    leave  
0x08048567 <main+174>:    ret    
End of assembler dump.

각각의 chunk가 어디에 존재하고, 어떤 구조를 갖고 있는지 해당 위치에 bp를 걸고 확인해봤다.
















다음과 같은 형태를 띈다.

취약점은 단골 함수인 strcpy()를 호출하여 발생하는데

strcpy(i1->name, argv[1]);
strcpy(i2->name, argv[2]);

딱 봐도 got overwrie를 해야한다.

좀만 생각하면 답이 나오는데 strcpy()를 사용한다고 해서 stack 처럼 ret를 건드릴 수 있는 것도 아니고,

heap 은 좀 다르게 chunk라는 애들이 존재하니까...

그럼 이걸로 어떻게 winner()를 띄우냐...

소스코드를 보면 답이 나온다.

strcpy(i1->name, argv[1]);
strcpy(i2->name, argv[2]);

printf("and that's a wrap folks!\n");

strcpy() 호출 이후 printf()가 호출되니까 printf()의 got를 winner()의 주소로 바꾸면 된다.

(strcpy()도 보기 좋게 2개 있겠다!)
















argv[1]에 AAAA argv[2]에 BBBB를 넣고 확인해본 결과

chunk i1에서 마지막 data 부분이 i1->name의 값이 들어가는 주소를 가리킨다.

chunk 2도 마찬가지다.

그럼 printf@got를 overwrite하기 위해서는 먼저 첫번째 strcpy()에서 overflow하여

chunk i2에서 마지막 data 즉 0x0804a038 부분을 printf의 got로 overwrite 하면 된다.

그리면 두 번째 strcpy()가 호출될 때 인자 정보가 다음과 같게 된다.

strcpy(i2->name (= printf@got), argv[2]);

따라서 argv[2]에는 winner()의 주소를 넣어서 다음 printf() 호출 때 winner()를 띄우면 된다.







끗.


반응형