본문 바로가기
pwnable

[Toddler's Bottle] passcode

by 미스터 J 2024. 5. 11.
반응형

오랜만에 putty로 진입할 수 있는 문제가 나왔다. 컴파일 에러 어쩌구 하는 거 보니까 컴파일에 관한 문제인듯?


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

 


welcome함수 다음에 login함수가 오고 338150과 13371337이 동시에 일치하면 로그인이 된다.

그래서 다음과 같이 해봤는데 Segmentation fault가 나왔다. 주요 키워드는 아마도

ha! mommy told me that 32bit is vulnerable to bruteforcing :)

이거 인것 같은데.....

 

우선 모르는것을 한번 정리해 보면.

int fflush (FILE *fp) : 파일 스트림 버퍼를 비우는 함수, 성공시 0, 에러시 EOF ex)fflush(stdin)

scanf로 입력을 받지만 제대로 입력이 되지 않는 경우가 있음. 이 때 버퍼를 지워주기 위해서 사용.

현재는 버퍼 초기화를 표준입력으로 해주기 때문에 이부분이 치명적일 것이라고 예상함.

 

우선 다양한 시도를 해보았을때 passcode1 값에 숫자가 들어가면 seg flaut가 뜨는 것을 확인 할 수 있었다.

 

GDB조사를 하기 전에 모르는 것을 정리해보자. GDB 옵션이다.

o : 8진법 표기

x : 16진법 표기

u : 10진법 표기

t : 2진법 표기

b : 1 byte 단위 표기(byte)

h : 2 byte 단위 표기(half word)

w: 4 byte 단위 표기(word) - 난 2byte로 알고 있지만 여기선 4바이트로 쓰이나 보다....

g : 8 byte 단위 표기(giant)

i : 어셈블리 형식으로 출력

s : 문자열로 출력

ex) x/4wx $ebp : ebp를 기준으로 16진법(x)으로 4바이트 단위로(w) 4개 보여준다.

x/s : 문자열로 출력

set {타입} [주소] = [값] : p명령 대신에 set을 통해 메모리의 특정 주소에 저장하는 것이 더 일반적이다.

lea : 좌변에 우변의 주소값을 입력하는 것이다. (좌변은 레지스터만 올수있다)

mov : 좌변에 우변의 값을 입력하는 것이다.

sub : 좌변의 값을 우변의 값과 -(마이너스)연산하여 그 결과를 좌변에 저장. 따라서 좌번에는 reg, mem되어야 함

add : 좌변의 값을 우변의 값과 +(플러스) 연산하여 그 결과를 좌변에 저장. 따라서 좌변에는 reg, mem되어야 함

PTR : 피연산자의 크기를 재설정함 ex) WORD PRT value - value의 크기를 WORD의 크기로 재설정함

 

또 다시 write-up을 참고한다.

문제1. passcode 부분이 선언만 해주고 초기화를 해주지 않았음.

문제2. scanf()함수로 각 passcode를 받을 때 & 주소 연산자를 붙이지 않았다.

따라서 passcode를 주소로 한 곳에 저장. 주소 자리에 입력이 그대로 들어가고, 초기화를 해주지 않았기 때문에 쓰레기값이 그대로 들어있는 것이다.

해당 부분은 welcome()함수의 char name[100]을 저장하는 부분이다. [ebp-0x70]에 입력값을 저장한다.

그리고 cmp(비교문) 부분(ebp-0x10)에 password1이 저장된 것이다.

ebp-0x70 ~ ebp-0x10 = 0x60 이 차이나므로, 

96byte가 차이나게 되는것이다. char name[100]이므로 4byte정도 passcode1에 간섭할 수 있을 것이다.

하지만 fflush()함수로 인해서 버퍼가 표준입력(stdin)으로 초기화 되므로 stdin에 system()를 입력해줘야 한다.

 

PLT (Procedure Linkage Table) : 외부 프로시저를 연결해주는 테이블. PLT를 통해 다른 라이브러리에 있는 프로시저를 호출해 사용할 수 있다.
GOT (Global Offset Table) : PLT가 참조하는 테이블. 프로시저들의 주소가 들어있다.
PLT와 GOT에 대해서는 대부분 이렇게 알고 있을 것입니다.

” 함수를 호출하면(PLT를 호출하면) GOT로 점프하는데 GOT에는 함수의 실제 주소가 쓰여있다.
첫 번째 호출이라면 GOT는 함수의 주소를 가지고 있지 않고 ‘어떤 과정’을 거쳐 주소를 알아낸다.
두 번째 호출 부터는 첫 번째 호출 때 알아낸 주소로 바로 점프한다. “

fflush()함수가 GOT로 이동할 때 실제 주소가 적혀있다. 따라서 system()의 got를 4byte에 넣어주면 된다.

https://bpsecblog.wordpress.com/2016/03/07/about_got_plt_1/ 자세한건 여기 나와있다.(햐... 어떻게 이런걸 설계하고 구현하지.... 배울게 많다. 나중에 더 공부해야된다.)

 

0x08048593 <+47>:    call   0x8048430 <fflush@plt>

0x8048430 <fflush@plt>:      jmp    DWORD PTR ds:0x804a004 (또 뭔가 모르는게 한껏 나오기 시작한....)

0x804a004 <fflush@got.plt>:  test    BYTE PTR ss: [eax+ecx*1], al

 

모르는 것을 또 정리하고 가자.

jmp : 특정 지역으로 이동[점프]를 하게 해줌. 가르키는 값을 NULL과 비교한다. 아니면 갯수를 세고 NULL을 만나면 갯수를 리턴한다.

* CS : code segment, 명령어들 있는 곳,

DS : data segment, 변수들 저장하는 곳,

SS : stack segment, 함수들 있는 곳,

ES : extra segment, 프로그래머가 정해서 쓰는 곳,

FS,GS : 80386(32bit) CPU 때 생겨난 용처가 정해지지 않는 영역.

TEST : 좌변과 우변을 AND 시킴. ZF(zero flag)에만 영향을 미치고, 결과값은 저장하지 않음.(나중에 flag정리 필요)

 

한마디로 fflush 함수로 이동하여 ds(변수들이 저장되는 곳)으로 이동 후, got값을 TEST해본 것으로 추정된다.

 

Q. 왜 system()함수의 시작부분이 0x080485e3지?

인자 전달 후 , call이 기본 순서인거 같다.

그래서 0x080485e3 부분으로 진행한다. scanf()가 정수형으로 받기 때문에 10진수로 바꿔주면 134514147이다.

표준입력으로 system()함수가 진입하기 때문에 밑에 입력 가능하게 변한다.

Sorry mom.. I got confused about scanf usage :(

 

지렸다.... 어떻게 이런 생각들을 하고 살 수 있는 걸까. 이번 문제는 2일정도 걸린만큼 많이 배운거 같다.

 

 

 

dddddds ddddddd

반응형

'pwnable' 카테고리의 다른 글

[Toddler's Bottle] bof  (0) 2024.05.11
[Toddler's Bottle] flag  (0) 2024.05.11
[Toddler's Bottle] random  (0) 2024.05.11
[Toddler's Bottle] mistake  (0) 2024.05.11
[Toddler's Bottle] shellshock  (0) 2024.05.11