본문 바로가기
Security/pwnable.kr

[pwnable.kr] Toddler's Bottle - passcode

by 단월໒꒱ 2022. 6. 19.

이번에는 passcode 문제를 풀어보았다.

 

 

 

 

문제를 읽어보니 passcode 기반의 로그인 시스템을 만들었는데 무언가 컴파일 에러가 난 것 같다.

딱히 얻을만한 건 없는 것 같으니 넘어가자.

 

문제에 있는 명령어를 통해 접속해준다.

 

 

 

 

접속 완료했다.

 

 

 

 

파일은 이렇게 있었고 당연히 flag의 내용은 확인할 수 없었다.

 

passcode 바이너리 파일을 실행해서 다음과 같이 임의의 값을 넣어줘봤다.

 

 

 

 

대충 입력하니 segmentation fault가 뜬다.

원인을 모르겠으니 cat으로 passcode의 c 소스코드를 확인해보았다. (너무 길어서 코드를 긁어왔다.)

 

 

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

 

 

좀 길어서 부분부분 살펴보았다.

 

먼저 main 함수를 보았다.

 

 

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 함수를 호출하고 있다.

 

 

다음으로 welcome 함수를 살펴보았다.

 

 

void welcome(){
	char name[100];
	printf("enter you name : ");
	scanf("%100s", name);
	printf("Welcome %s!\n", name);
}

 

 

크기가 100인 name 배열을 선언하고 여기에 입력받은 것을 저장한 뒤 출력하고 있다.

 

 

마지막으로 login 함수를 살펴보았다.

 

 

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

 

 

입력 받은 수를 passcode1에 저장하고 fflush(stdin)을 해주는데, 이때 fflush(stdin) 함수는 입력 버퍼를 지워주는 함수이다.

그런 뒤에 또 수를 입력 받아서 passcode2에 저장해준다.

passcode1이 338150이고 passcode2가 13371337이면 flag를 출력하고 틀린 경우에는 프로그램이 종료된다.

 

그런데 여기서 조금 이상한 점은 scanf 함수를 사용할 때 & 참조 연산자가 없다는 것이다.

이렇기 때문에 이건 입력 값을 각 변수에 저장하는 게 아니라 그 변수의 주소에 입력 받은 값을 저장하겠다는 의미이다.

 

 

 

 

그래도 한번 단순하게 passcode1에 338150을 입력해보니 역시나 에러가 뜬다. 

 

 

자세히 살펴보기 위해 디버깅을 해보았다. (main 함수는 건너뛰고....)

 

 

먼저 welcome 함수이다.

 

 

 

 

아까 위에서 name을 입력받는다고 했는데 그 입력받은 내용을 ebp-70에 저장하고 있다.

저장 위치 외에는 딱히 알아낼 게 없어서 여기서 멈추었다.

 

 

다음으로 login 함수를 살펴보았다.

 

 

 

 

이것도 마찬가지로 각 변수들의 저장 위치를 살펴보았다.

일단 passcode1은 ebp-10에 저장된다.

아까 welcome에서는 name이 ebp-70이라고 했는데 둘의 간격을 보면 60으로 96byte 떨어져있는 것을 알 수 있다.

그렇게 되면 끝의 4byte는 겹치게 되므로 이 4byte를 passcode1이 덮어쓸 수 있게 된다.

 

fflush는 got 함수를 이용해서 호출되므로 이 부분을 system 코드를 호출하는 주소로 바꿔주면 fflush 함수 대신 system 함수를 실행시킬 것 같았다.

그러므로 아까 위에서 얘기한 4byte를 fflush의 got주소로 채워주고 system의 주소를 passcode1을 입력할 때 넣어주면 될 것 같다.

 

 

먼저 fflush의 got 주소를 찾아보자.

 

 

 

 

p fflush로 함수의 plt 주소를 찾고 그 주소의 plt가 jmp하는 곳을 보니 0x804a004인 것을 확인할 수 있다.

따라서 fflush의 got 주소는 0x804a004이다.

 

 

다음으로 system의 주소를 찾아보자.

 

 

 

 

system의 시작 주소는 0x80485e3이라고 바로 있다.

하지만 이 주소를 정수로 줘야하므로 이를 10진수로 바꿔주면 134514147이다.

 

 

 

 

이제 96만큼 더미값을 주고 fflush의 got주소를 넘겨준 후에 134514147을 직접 입력해주면 된다.

 

 

파이썬 인터프리터를 이용해서 값을 넘겨주면

 

 

 

 

성공적으로 flag를 획득할 수 있다.

 

 

 

 

 

댓글