ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CHAPTER 1. Nymph's_fault MakingFilm
    0x.STORY_TELLER/MakingFilm 2018. 2. 28. 16:27

    CHAPTER 1. Nymph's_fault MakingFilm



    문제 링크 : https://github.com/pwnwiz/STORY_TELLER/tree/master/STORY_0x00



    이 문제를 기획하게 된 계기는 가장 먼저 동아리 내부 CTF 문제를 만들어라는 특명에서 비롯되었다. 풀어본 문제라고는 몇 가지 오버플로우 문제와 간단한 리버싱 문제가 전부였기에, 그나마 풀어본 버퍼 오버플로우 문제를 만들어보는 것이 어떨가라는 생각에서 제작하기 시작하였다.


    ropasaurusrex가 32bit 버퍼오버플로우 문제였다면, 가장 최근에 출제된 CODEGATE 2018의 baskinrobins31이 64bit 오버플로우 문제였는데, 페이로드가 많이 달라져서 상당히 당황했었다. 그래서 이 참에 64bit 체제의 문제를 만들고 익스를 하면서 개념을 잡아보자는 생각을 했다. 또한 최근 랜섬웨어에서 봤던 Anti-IDA를 실전에서 사용했을 때 어떻게 될지를 실험하기 위한 목적도 존재했다. 


    내가 만드는 CTF 문제는 먼가 딱딱한 문제가 아니라 한편의 스토리였으면 좋겠다는 바램으로부터 Nymph's_fault라는 내용의 간단한 필름동화 형식으로 제작하였고, 한 편의 책처럼 읽을 수 있도록 배치하였다. 물론 간단한 문제이므로 내용이 길진 않지만 그래도 혹시나 모르기에 영어로 번역하여 제작했다 ㅋㅋ. 이 문제를 누군가가 풀 수도 있으니까..


    처음 구성에서 사용자로부터 입력을 받는 부분은 3부분이다. 가장 첫 부분은 main()함수 부분인데 


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    int main()
    {
            int choice;
     
            intro();
            choice=select();
     
            switch (choice)
            {
                    case 1:
                            pray(); break;
                    case 2:
                            potion(); break;
                    case 3:
                            persuade(); break;
                    default:
                            printf("Wrong input!!!\n");
            }
     
            return 0;
    }

    cs




    단순히 switch문에서 입력값을 받아서 함수를 호출해주는 역할을 하는 부분이다.


    2번째 입력을 받는 곳은 potion 부분인데, 


    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
    void potion() // choice 2
    {
            char str[7];
     
            printf("You have decided to go to Poseion's palace to steal the potion\n\n");
            printf("Oh!! There are various potions. Red, Blue, Purple, Yellow....\n\n");
            printf("Which one will you choose???\n\n");
     
            // 여기서 버퍼 오버플로우 취약점 안터짐 (크기는 yellow까지..)
            // 만약에 Red, Blue, Purple, Yellow 가 입력되면 아래 실행
     
            int potionStrLen = read(0, str, sizeof(str));
            str[potionStrLen - 1= '\0';
     
            if(canStealPotion(str)){
                    printf("I have chosen ( %s )!\n\n", str);
                    printf("Let me get out quickly before being caught.\n\n");
              printf("YOU! THIEF!!! ROP!!!\n\n"); // hint
                    ending();
            }
            else{
                    printf("YOU! THIEF!!!\n\n");
                    printf("You have been put into the jail\n\n");
                    producer();
            }
     
    }
    cs


    이 부분은 read를 sizeof(str)만큼만 받기 때문에 버퍼 오버플로우 취약점이 터지지 않는다. 굳이 이 부분을 만든 이유는 그냥 버퍼 오버플로우라는 개념만 아는 스크립트키드들이 삽질을 할 수도 있지 않을까하는 생각에서 비롯되었다. 어찌됬든 이 함수를 통해 ROP라는 힌트가 주어진다.


    3번째로 입력을 받는 부분이 진짜 버퍼 오버플로우 취약점이 터지는 부분인데, 코드는 아래와 같다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    void persuade() // choice 3
    {
            char str[256];
     
            printf("Hey Human! What the hell are you doing here?\n\n");
            printf("Your highness. Please save us.\n\n");
            printf("Only with your help, we can solve this disaster.\n\n");
            printf("Hmm.. I have to give up my beautiful eyes if I help you. That's not what I want.\n\n");
            printf("If I do that, What will you give?\n\n");
            // 자유 입력 (여기서 버퍼 오버플로우 취약점 터짐) 
     
            read(0, str, 500);
     
            write(1, str, strlen(str + 1));
     
            printf("\nHMM... Nope\n\n");
            producer();
    }
    cs


    str의 크기가 256바이트 임에도 불구하고 500만큼을 입력 받는다. 그로인해 RET 주소가 변조가 될 것이고 ROP를 통해 익스를 할 수 있게 된다. 


    위와 같이 대략적인 설정을 마치고 gcc 컴파일러를 사용하여 -fno-stack-protector로 SSP 기능을 해제해주었다. SSP는 다음과 같은 작업을 한다. 


    1. 로컬 변수 재배치

    2. 로컬 변수 전에 포인터 배치

    3. 스택 카나리 삽입


    자세한 내용은 http://bbolmin.tistory.com/65 블로그에서 확인할 수 있다.


    실행을 하니 정상적으로 작동을 하였지만 익스 과정에서 문제점이 발생했다. 바로 64bit는 32bit와 인자값 전달 방식이 달랐기 때문이었다. 32bit의 경우 스택에서 바로 전달이 되는 반면 64bit는 첫 번째 인자부터 순서대로 rdi, rsi, rdx등의 레지스터들에 저장을 했다가 사용되는 것이었다. 그래서 기존의 objdump -d 를 통해 찾은 pppr 가젯이 정상적으로 작동을 하지않았던 것이었고, 아래와 같은 인라인 어셈코드를 소스코드에 추가해주어야 했다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void hint()
    {
            __asm__ __volatile__
            (
            "pop %rdi \n\t"
            "pop %rsi \n\t"
            "pop %rdx \n\t"
            "ret \n\t"
            );
     
    }
    cs



    코드 추가후에 익스를 다시 시도해보니 성공하였다. 


    다만 동아리원들의 수준을 고려할 때 스택 오버플로우를 ROP만으로 내기에는 너무 난이도가 쉽다는 판단을 하였고, IDA에서 hex-rays를 통해 분석하기 조금이라도 까다롭게 하기위해서 anti-IDA코드를 추가하기로 하였다.


    1
    2
    3
    4
    5
    __asm__ __volatile__
    (
        "add $0x50, %rsp \n\t"
        "sub $0x50, %rsp \n\t"
    );

    cs


     

    위의 인라인 어셈코드는 랜섬웨어 분석글에서 봤던 건데 스택을 저렇게 add sub를 통해 할당했다가 해제를 하면 프로그램의 작동에는 문제가 없지만 IDA가 잘 분석을 하지 못하는 특징이 있다. 그 점을 활용해서 몇 가지 함수를 c코드로 보지 못하도록 하였다. 


    이렇게 하여 문제에 대한 제작을 완료하였다. 아래의 깃허브에서 풀 코드를 확인할 수 있다.


    https://github.com/pwnwiz/STORY_TELLER/tree/master/STORY_0x00

    '0x.STORY_TELLER > MakingFilm' 카테고리의 다른 글

    CHAPTER 3.Viva_La_Vida MakingFilm  (0) 2018.09.03
    CHAPTER 2.HalloweenDay MakingFilm  (0) 2018.04.11

    댓글

Designed by Tistory.