ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ Rookiss ] Alloca
    System/Pwnable.kr 2019. 3. 24. 17:58

    " Buffer Overflow를 예방하는 법에 대해서 알려주겠다. "고 한다.


    [ Alloca.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
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
     
    void callme(){
        system("/bin/sh");
    }
     
    void clear_newlines(){
        int c;
        do{
            c = getchar();
        }while (c != '\n' && c != EOF);
    }
     
    int g_canary;
    int check_canary(int canary){
        int result = canary ^ g_canary;
        int canary_after = canary;
        int canary_before = g_canary;
        printf("canary before using buffer : %d\n", canary_before);
        printf("canary after using buffer : %d\n\n", canary_after);
        if(result != 0){
            printf("what the ....??? how did you messed this buffer????\n");
        }
        else{
            printf("I told you so. its trivially easy to prevent BOF :)\n");
            printf("therefore as you can see, it is easy to make secure software\n");
        }
        return result;
    }
     
    int size;
    char* buffer;
    int main(){
     
        printf("- BOF(buffer overflow) is very easy to prevent. here is how to.\n\n");
        sleep(1);
        printf("   1. allocate the buffer size only as you need it\n");
        printf("   2. know your buffer size and limit the input length\n\n");
     
        printf("- simple right?. let me show you.\n\n");
        sleep(1);
     
        printf("- whats the maximum length of your buffer?(byte) : ");
        scanf("%d"&size);
        clear_newlines();
     
            printf("- give me your random canary number to prove there is no BOF : ");
            scanf("%d"&g_canary);
            clear_newlines();
     
        printf("- ok lets allocate a buffer of length %d\n\n"size);
        sleep(1);
     
        buffer = alloca( size + 4 );    // 4 is for canary
     
        printf("- now, lets put canary at the end of the buffer and get your data\n");
        printf("- don't worry! fgets() securely limits your input after %d bytes :)\n"size);
        printf("- if canary is not changed, we can prove there is no BOF :)\n");
        printf("$ ");
     
        memcpy(buffer+size&g_canary, 4);    // canary will detect overflow.
        fgets(buffer, size, stdin);        // there is no way you can exploit this.
     
        printf("\n");
        printf("- now lets check canary to see if there was overflow\n\n");
     
        check_canary( *((int*)(buffer+size)) );
        return 0;
    }
     
     
    cs

    서버에 접속하면 'Alloca.c' 소스파일을 제공해준다.

    코드 내용이 다소 길지만, 중요한 부분만 보면 아래와 같다.


    [ alloca(size_t size ) ]

    우선 문제 타이틀인 alloca( )에 대해서 알아보도록 하자

    이 함수는 Heap이 아닌 Stack에 동적할당을 하고자할 때 사용하는 함수이다.


    그때 그때 필요한 만큼 스택을 할당하고 해제하니까 나름 효율적일 것만 같지만, 

    alloca( )에 대해서 조금만 찾아보면 다들 사용을 자제하라고 한다.


    그 이유는 신뢰할 수 없는 입력으로 부터 '큰 값'이나 '음수 값'이 입력될 수 있기 때문이다.

    큰 값이나 음수 값이 입력되면 해당 값을 할당하다가 Stack Frame이 깨져버린다.

    해당 문제도 이러한 취약점을 이용해서 Exploit을 진행하면 된다.


    [ memcpy ]

       63  memcpy(buffer+size&g_canary, 4);    // canary will detect overflow.

    63번째 코드를 보면, alloca( )로 할당한 buffer뒤에 4Byte를 우리가 입력한 'g_canary'로 덮는다.

    만약 우리가 Stack Frame을 조작할 수 있다면, EIP가 참조하는 [ESP]부분을 'g_canary'로 덮을 수 있을 것이다.


    [ Callme( ) ]

    Exploit하기 편하게 One_shot함수도 제공해주고 있다.


    [ How to Exploit? ]

    우선 정적 분석보다는 GDB를 이용한 동적분석을 추천한다.

    alloca( )가 어떤식으로 동적할당을 하는지 흐름을 알아야 정확하게 EIP를 핸들링할 수 있다.


    [ Alloca( ) in GDB ]

    GDB에서 alloca( )코드에 해당하는 코드는 위 Assemble들이다.


    [ Alloca( ) in IDA ]

    IDA에서는 이렇게 두줄을 참고하면 된다.

    정적분석으로 알고 넘어가야 할점은 우리가 Input으로 넣어주는 'size'값에 +34가 더해진다는 점이다.


    자 그럼 동적분석으로 Alloca( )가 어떻게 동작하는지 알아보도록 하자


    [ 1. mov eax, ds:0x804a048 ]

    먼저 나는 size값에 '-70'을 넣어줬다.

    [mov eax, ds:0x804a048]에서는 입력한 size값을 EAX에 가져오는데 어떤값이 넘어오는지 보자

    우리는 분명 '-70'을 입력했는데, size값에는 [0xFFFF FFBA]가 저장되어 있다.

    어찌보면 당연한 결과이기도 하다.


    사이즈를 DWORD로 설정하고, -70을 입력해보면 Hex값으로는 저 값이 맞다.

    따라서, 음수값을 입력하게 되면 size에 비정상적으로 큰 값이 들어가게 되고

    이 때문에 Stack Frame이 깨지게 되는 것 이다.


    [ 2. div ecx ]

    다음으로 볼 코드는 [div ecx]다.

    ECX에는 0x10으로 저장되어 있고, 이 값으로 EAX와 div 연산을 하게 되면

    EAX의 마지막 값인 [0xC]가 잘리게 되면서, [0xFFFF FFD]으로 EAX값이 세팅된다.


    그 다음에 [imul eax, eax,0x10] 연산을 하는 것을 볼 수 있다.

    imul은 두번째와 세번째 인자를 곱한 값을 첫번째 인자에 저장하는 연산이며, 결과 값은 [0xFFFF FFD0]이 된다.

    따라서, [div ecx][imul eax, eax,0x10] 코드는 할당 Size를 16의 배수로 맞춰주기 위한 연산으로 보면 된다.


    [ 3. sub esp, eax ]

    그 후, [sub esp, eax]로 입력한 만큼 Stack을 할당해준다.

    여기가 제일 중요한 부분이다.

    해당 Instruction이 실행되면, ESP는 +48만큼 줄어들게 된다.

    왜 이런 결과가 나오는지 계산을 천천히 해보자


    [ ESP ]

    ESP는 [0xFFFF CE00]으로 10진수로 [-12,800]이다.


    [ EAX ]

    EAX는 [0xFFFF FFD0]으로 10진수로 [-48]이다.

    우리가 입력한 값은 -70이였고, 이 값에 +34가 더해졌으니 -36, 이 값을 16배수로 맞추니 -48로 바뀐 것이다.


    아무튼 ESP와 EAX 두 값을 sub연산을 하게 되면 아래와 같은 연산식이 만들어 질 것이다.

    -12,800 - (-48) == -12,800 + 48


    [ ESP-EAX ]

    따라서, 연산결과는 -12,752 Hex값으로는 [0xFFFF CE30]이라는 값이 나온다.


    GDB에서도 확인해보면 ESP가 [0xFFFF CE30]으로 바뀐것을 확인할 수 있다.

    이러한 흐름으로 우리는 원하는 위치로 Stack를 맞춰줄 수 있다.


    [ memcpy ]

    이제 원하는 위치에 값을 쓸 차례다.

    위에서 말한 memcpy(buffer+size,g_canary,4)를 이용하면 된다.

    우선 [mov eax, ds:0x804a050]으로 buffer의 시작주소를 EAX로 가져온다.


    [ buffer+size ]

    그 다음 EDX(Size값)에 EAX를 더 해주면서 g_canary가 저장될 위치를 잡아준다.


    [ Save g_canary ]

    그 후, [mov eax, ds:0x804A04C]로 우리가 입력한 g_canary값을 가져온다.

    나는 '-1431655766'(0xaaaa aaaa)를 g_canary값으로 입력해놨다.

    그 다음 [mov DWORD PTR [edx], eax]로 g_canary값을 [edx]에 4Byte만큼 저장한다.

    자 이제 우리는 원하는 곳에 원하는 값을 4Byte만큼 쓸 수 있게 됐다.


    [ Func Epilog ]

    함수 에필로그를 보면 [mov ecx, DWORD PTR [ebp-0x4]]로 ECX에 값을 가져온 후,

    [lea esp, [ecx-0x4]]로 EIP포인터를 설정해주는 것을 확인할 수 있다.


    우리는 이 흐름을 이용해서 [EBP-4]에 'Callme( )의 주소가 저장된 주소+4'를 저장해놓게 되면

    에필로그 때, EIP가 해당 주소를 참조하게 되면서 Callme( )가 실행되게 될 것이다.


    자 그럼 문제는 &Callme( )가 저장된 주소가 필요한 건데...

    이 부분에서 많이 헤맸다.


    처음 접근은 size와 buffer가 전역변수로 선언되어 있어서 이 부분을 이용해서

    &callme( )가 저장된 주소를 넘기면 되겠거니 했는데, 불가능했다..


    내 머리로는 도저히 방법이 안나와서 Writeup을 참고했는데, Intended한 방법이 BruteForce라니..

    이 부분보고서 딱 질렸다.

    문제에서 의도한 방법이 무작정 때려박는 BruteForce라니..ㅋㅋ 분석을 통해서 익스하는 것도 아니고


    그냥 환경변수에 Callme( ) 겁나 때려박고 환경변수 부분으로 계속 Bruteforce하다 보면 쉘이 따진다.


    BruteForce만 아니면 정말 괜찮은 문젠데, 흠.. 말을 아끼겠다.


    [ Exploit ]


    'System > Pwnable.kr' 카테고리의 다른 글

    [ Rookiss ] Ascii_easy  (2) 2019.04.05
    [ Toddler's Bottle ] asm  (4) 2019.03.09
    [ Rookiss ] otp  (1) 2019.03.06
    [ Rookiss ] Simple Login  (2) 2019.01.17
    [ Rookiss ] loveletter  (0) 2018.08.26

    댓글

Designed by Tistory.