Pwnable.kr: ‘mistake’ Walkthrough

I skipped “leg” and went to “mistake”, which was a fun puzzle.  Our hint is:

We all make mistakes, let’s move on. (don’t take this too seriously, no fancy hacking skill is required at all)

This task is based on real event Thanks to dhmonkey

hint : operator priority

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

nc

We connect to the site via ssh mistake@pwnable.kr -p2222 and then enter in the password, guest.

Looking around

After connecting, we see the following files:

mistake@prowl:~$ ls -la
total 44
drwxr-x--- 5 root mistake 4096 Oct 23 2016 .
drwxr-xr-x 114 root root 4096 May 19 15:59 ..
d--------- 2 root root 4096 Jul 29 2014 .bash_history
-r-------- 1 mistake_pwn root 51 Jul 29 2014 flag
dr-xr-xr-x 2 root root 4096 Aug 20 2014 .irssi
-r-sr-x--- 1 mistake_pwn mistake 8934 Aug 1 2014 mistake
-rw-r--r-- 1 root root 792 Aug 1 2014 mistake.c
-r-------- 1 mistake_pwn root 10 Jul 29 2014 password
drwxr-xr-x 2 root root 4096 Oct 23 2016 .pwntools-cache

We are user mistake.

mistake@prowl:~$ whoami
mistake

Presumably, the mistake_pwn user will be able to read the root flag, and we can run the mistake file to do so.

mistake.c

Let’s take a look at the mistake.c file.

mistake@prowl:~$ cat mistake.c
#include <stdio.h>
#include <fcntl.h>

#define PW_LEN 10
#define XORKEY 1

void xor(char* s, int len){
    int i;
    for(i=0; i<len; i++){
        s[i] ^= XORKEY;
    }
}

int main(int argc, char* argv[]){

    int fd;
    if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){
        printf("can't open password %d\n", fd);
        return 0;
    }

    printf("do not bruteforce...\n");
    sleep(time(0)%20);

    char pw_buf[PW_LEN+1];
    int len;
    if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
        printf("read error\n");
        close(fd);
        return 0;
    }

    char pw_buf2[PW_LEN+1];
    printf("input password : ");
    scanf("%10s", pw_buf2);

    // xor your input
    xor(pw_buf2, 10);

    if(!strncmp(pw_buf, pw_buf2, PW_LEN)){
        printf("Password OK\n");
        system("/bin/cat flag\n");
    }
    else{
        printf("Wrong Password\n");
    }

    close(fd);
    return 0;
}

Before looking at the code too much, I tried running the mistake executable (./mistake).  I was surprised to find that it seemingly would accept my input twice, despite seeing only one call to scanf.

fd

Let’s dig into this part:

if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){
printf("can't open password %d\n", fd);
return 0;
}

Normally, we’d read this as “if the file descripter returned by open(…) is less than zero (i.e. it’s unsuccessful)", print an error.

However, < happens before =.  You can read more about C’s operator precedence rules here.

What that means is `open("/home/mistake/password”,O_RDONLY,0400)` is called, and returns 1, because the file “password” exists.  You can read more about “open” here.

Then we have if (fd = 1 < 0). Again, < still happens before =, so we evaluate if (fd = (1 < 0)).

1 is not less than 0, so (1 < 0) returns as false, or 0.

This leaves us with if (fd = 0), which is an assignment. And an fd value of 0 is stdin. https://en.wikipedia.org/wiki/File_descriptor

This means that we’re providing the original password value (not the password file), and we also get to provide the second password value.

xor

However, the second input gets xor’d, so whatever we put in the original value, the second value needs to equal xor(original value), because the two values are compared (strncmp) later in the progam.

For example, the xor function will turn A (binary: 01000001) into @ (binary: 01000000). Similarly, it will turn B (binary: 01000010) into C (binary: 01000011). In each case, we’re flipping the least significant bit of each character.

So, a password (length: 10) of AAAAABBBBB gives us xor(AAAAABBBBB) = @@@@@CCCCC. Of course, you could simplify this further to a single character (all A’s) or make a more interesting password. In either case, you’ll need an original password, and a xor'd copy. Because of how XOR works, it doesn’t matter which one you provide first.

Solution

Putting that together:

mistake@prowl:~$ ./mistake
do not bruteforce...
AAAAABBBBB
input password : @@@@@CCCCC
Password OK
Mommy, the operator priority always confuses me :(

Or, flipping the order:

mistake@prowl:~$ ./mistake
do not bruteforce...
@@@@@CCCCC
input password : AAAAABBBBB
Password OK
Mommy, the operator priority always confuses me :(