본문으로 바로가기

[pwnable.kr] passcode ( write - up )

category 카테고리 없음 2019. 4. 7. 20:30

처음써보는 pwnable.kr 라이트업 :)


[1] 코드분석

코드를 분석해보자. 일단 이 코드는 최대 100바이트까지의 이름을 입력받고, int형 패스워드 1,2를 입력받는다.

그 후 패스워드1과 2를 비교하여, 조건에 맞으면 /bin/cat flag로 플래그를 보여준다.

그래서 일단 맘편하게 바로 패스워드를 입력해보았는데 오류가 뜨는것을 볼수있다.

띠용! 왜일까?

일단 구글링해보니 fflush함수는 리눅스내에서 제 기능을 하지 못한다고 한다. 

그래서 오류가 났을 상황에서는 passcode1 = 1234 / passcode2 = \n 이 들어있을 것이다.

그래서 우리는 일단 passcode2는 이용할수없고, 이름을 입력받는 name과 passcode1만을 이용해서 플래그를 찾아야한다.

너무 막막하다.. 

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
#include <stdio.h>
#include <stdlib.h>
 
void login(){
        int passcode1;
        int passcode2;
 
        printf("enter passcode1 : ");
        scanf("%d", passcode1);
        fflush(stdin);
 
        // ha! mommy told me that 32bit is vulnerable to bruteforcing :)
        printf("enter passcode2 : ");
        scanf("%d", passcode2);
 
        printf("checking...\n");
        if(passcode1==338150 && passcode2==13371337){
                printf("Login OK!\n");
                system("/bin/cat flag");
        }
        else{
                printf("Login Failed!\n");
                exit(0);
        }
}
 
void welcome(){
        char name[100];
        printf("enter you name : ");
        scanf("%100s", name);
        printf("Welcome %s!\n", name);
}
 
int main(){
        printf("Toddler's Secure Login System 1.0 beta.\n");
 
        welcome();
        login();
 
        // something after login...
        printf("Now I can safely trust you that you have credential :)\n");
        return 0;
}
 
cs

다시 처음부터 코드를 쭉 살펴보자.

문제점은 바로          printf("enter passcode1 : ");

        scanf("%d", passcode1);
        fflush(stdin);                                                        기에 있다.

왜냐하면 scanf문에서 & 를 붙히지 않았기때문이다. &는 참조연산자로, 메모리의 주소를 받아오는 역할을 하는데, 우리가 평상시에
사용하는 scanf는 다음와 같다.                           
 " scanf("%d",&a); "

이 의미는 a라는 변수의 메모리주소에 우리가 값을 입력하겠다는 의미인데, 위의 코드는 그렇지가 않다.
passcode1를 선언할때 값을 초기화 하지않았으므로 passcode1에는 쓰레기값이 들어있을것이고, 위의 코드에서 우리가
하던것처럼 입력을 받는것은 쓰레기주소에 값을 입력하는것과 다름이 없다.

자, 일단 scanf에 오류가 있다는것을 기억한 상태로 gdb로 파일을 뜯어보자.






[2] GDB로 뜯어보기

일단 위의 welcome 함수부터 차근차근 분석해보자.



일단 welcome 함수에서 가장 중요한부분은 welcome+38 ~ +48 부분이다.

lea edx, ebp-0x70 으로 edx에 ebp-0x70 이라는 주소값을 넣어주었고, 이값이 mov되면서 scanf함수에 그대로 전달된다.

이를통해서 우리는 ebp-0x70이 name[100]의 시작주소라는것을 유추할수있다.

자, 그리고 login 함수로 가보자.

login 함수에서 우리가 주의깊게 봐야할 부분은 당연히 passcode1의 흐름이다.

우리가 사용할수있는 부분은 name[100]과 passcode1뿐이기 때문이다.

일단 welcome부분에서 name의 시작주소를 알아냈으니 이제 passcode1의 시작주소를 찾아보고, 값을 어떻게 써야할지 찾아보자.


login함수에서의 첫번째 scanf문은 login+34 부분이다. 그리고 여기서 조금만 위로 가보면

login+24 부분에서 *(ebp-0x10)을 edx에 복사해주고, 이 값을 아랫부분에서 계속 다루면서 내려가는것을 볼수있다.

이부분에서 우리는 *(ebp-0x10)이 passcode1의 시작주소라는것을 유추할수있다.

( 추가로, 두번째 scanf문은 login+80 부분이다. 위와 같은방법으로 passcode2의 시작주소를 찾으면 ebp-0xc가 시작주소라는것을 알수있다.

정리해보면,    name[100] = 0x70 부터 100byte 

        passcode = 0x10 부터 4byte             를 입력받는다.


이 상황을 그림으로 그려보자.


열심히 그린 그림의 화질이 심상치않다. 슬프다.

위의 그림에서 색칠해둔 ??? 부분인 name에서 100바이트중 4바이트가 passcode1을 덮어쓸수있다.

그럼 여기 4바이트에는 무엇을 넣어야 할까? 




[3] 페이로드 작성

*PLT, GOT를 이해하기위해서는 https://sunrinjuntae.tistory.com/26 여기로!


name이 passcode1을 4바이트만큼 침범하므로, 여기다가 값을 넣어주면 위에서 설명했던 scanf의 오류로 인해서 4바이트 덮어씌운 값이 passode1의 값이 될것이다. 

자, 여기까지 페이로드를 작성해보자.

[dummy 96] + [ ??? 4]

그리고 여기서 우리는 scanf와 scanf사이에 있는 fflush함수의 GOT를 ??? 에 입력할것이다.

[dummy 96] + [ fflush@got 4]

그리고 passcode2를 입력받는 두번째 scanf에서 system함수의 시작주소를 입력하면 위의 코드처럼 "/bin/cat flag" 가 실행될것이다.

[dummy 96] + [ fflush@got 4] + [ &system 4] = exploit !!!!!

이제 우리가 필요한 주소들을 찾아보자.

먼저 fflush의 got이다.

노랑색으로 칠한부분이 fflush의 got이고...

노랑색칠한부분에서 인자("/bin/cat flag) 를 mov해주고 system함수를 call 하는것이니 노랑색으로 칠한 부분을 주소로 넣어준다!

[dummy 96] + [ 0x0804a004 ] + [ 0x080485e3] = exploit !!!!!

여기서 주의해야할점은, scanf로 입력받는 형식이 10진수 ( %d ) 이므로, 16진수값을 10진수로 변환해준다.

진짜로 다알아냈다. 이제 공격해보자.

우린해냈다.

(진짜개어렵다.)