passcode

{"author": ["ret2basic"]}

Challenge

Mommy told me to make a passcode based login system. My initial C code was compiled without any error! Well, there was some compiler warning, but who cares about that?

ssh passcode@pwnable.kr -p2222 (pw:guest)

Code Review

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

Solution

There is an error in the code: instead of scanf("%d", passcode1), it should be scanf("%d", *passcode1). Because of this error, passcode1 and passcode2 are considered addresses instead of values. In fact, there are three erros in the code:

scanf("%100s", name);
...
scanf("%d", passcode1);
...
scanf("%d", passcode2);

For each scanf(), we are able to write some data directly to the address of the variables name, passcode1, and passcode2. Let's read through the assembly to figure out the exact location of each variable:

The location for each variable:

name => ebp-0x70
passcode1 => ebp-0x10
passcode2 => ebp-0xc

The distance between name and passcode1 is 0x60, which is 96 in decimal. We are able to input at most 100 bytes for name, so it is possible to overwrite passcode1.

Note that the function fflush() is called right after scanf("%d", passcode1):

void login(){
        ...
        printf("enter passcode1 : ");
        scanf("%d", passcode1);
        fflush(stdin);
        ...
}

Let's see its disassembly:

This fflush() is fflush@PLT, and the first jmp instruction jumps to fflush@GOT. The idea is:

  1. Send 96 bytes of junk data to fill the buffer.

  2. Overwrite password1 with 0x804a004, then the scanf() statement becomes scanf("%100s", 0x804a004). This would allow us to input arbitrary data to the memory location 0x804a004, which is fflush@GOT.

  3. Once the scanf() function is called, send the integer representation (because of "%100s") of system("/bin/cat"). Once fflush@PLT is called, the jmp instruction will jump to system("/bin/cat") and execute it.

Essentially, this is a GOT overwrite attack. The last task is finding the address of system("/bin/cat"). Take another look at the disassembly of login():

Here 0x80485d7 is the address of system("/bin/cat"). To get its integer representation, use str(0x80485d7) in Python.

Get Flag

Exploit

Last updated