passcode

Posted by rk700 on November 16, 2014

代码如下

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

问题出在login()中,scanf的第二个参数给的不是变量的地址。如果passcode1passcode2的初始值指向不可写的地方,而且scanf成功,那么就会segfault

于是思路就是,在login()执行前,在welcome()中把栈上的值设好。所以这道题和OverTheWire上的Manpage里一道题思路有相似的地方

具体地,namewelcome()中的地址是ebp-0x70passcode1passcode2login()中的地址是ebp-0x10ebp-0xc。正好我们只能改写passcode1,改不了passcode2。于是单纯地想把两个都修改为检查值的方法行不通了

但是我们发现,passcode1那里由于scanf,可以做到写4 bytes到任意地址。所以我们可以修改程序流程,让他跳到system读flag那里。

而由于ASLR,想修改返回地址不靠谱,于是我们可以修改exit@got。这样检查失败后,调用exit(0)就成了system("/bin/cat flag");

readelf -r passcode可知exit@got0x0804a018,于是把passcode1设为它;然后通过scanf将其改为0x080485e3=134514147。注意我们要让passcode2scanf时不能segfault,于是输入非数字让scanf失败

$ perl -e 'print "A"x96 . "\x18\xa0\x04\x08" . "134514147\n" . "f\n"' | ./passcode