http://pwnable.kr Pwnable.kr is an awesome wargame to learning PWNning, reverse engineering and a bunch of other scary beasts like these. Let’s begin with challenge #1.

Mommy! what is a file descriptor in Linux?

* try to play the wargame your self but if you are ABSOLUTE beginner, follow this tutorial link: https://www.youtube.com/watch?v=blAxTfcW9VU

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

The first level of Pwnable.kr wargame is not exaclty difficult once you realize we are dealing with exploitation - something hard by definition.

Before we start, let’s copy the files to our local environment:

$ scp -P 2222 fd@pwnable.kr:./* .

Once we have fd and fd.c we can start to play. Let’s run our baby:

$ ./fd 123
learn about Linux file IO

Ok, something related to input/output in linux. Let’s move on. Analysing the code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
    if(argc<2){
        printf("pass argv[1] a number\n");
        return 0;
    }
    int fd = atoi( argv[1] ) - 0x1234;
    int len = 0;
    len = read(fd, buf, 32);
    if(!strcmp("LETMEWIN\n", buf)){
        printf("good job :)\n");
        system("/bin/cat flag");
        exit(0);
    }
    printf("learn about Linux file IO\n");
    return 0;

}

First thing to notice here is line 13:

len = read(fd, buf, 32);

According to man read:

read()  attempts  to read up to count bytes from file descriptor fd into the buffer starting at *buf*.

Ok, so what about our inputs. How is it read? Before copying it to buf, the code first does

int fd = atoi( argv[1] ) - 0x1234;

atoi basically converts the given string to an int. So if you input “1234” (string) it will output 1234 as an int. Later in this line it subtracts this int from 0x1234, which is nothing but an int represented in hex.

Let us first see what happens if we can zero fd. For this we would need to pass 0x1234 to the program. BUT, as we have just seen, our input goes to atoi(), so it must be in base10. Fair enough, we must then convert 0x1234 to decimal. Since I have been running lots of python code in the last weeks, here it goes :)

$ python
Python 3.5.2 (default, Nov 17 2016, 17:05:23) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> i = 0x1234
>>> i
4660

Very stupid, very good. Let’s pass 4660 to the program!

$ ./fd 4660

Aaaand, the program stalls, expecting more input. Hm, let’s see what happens if we give it LETMEWIN:

$ ./fd 4660
LETMEWIN
good job :)
/bin/cat: flag: No such file or directory

Oh boy! That was easier than expected.

What happened?

The condition we are looking for is the content of buf to be “LETMEIN\n”. Why? Jbecause of

if(!strcmp("LETMEWIN\n", buf)){

strcmp(s1, s2) compares s1 and s2 and returns 0 iff s1 is equal to s2. So we will only enter this if in case !strcmp(“LETMEWIN\n”, buf) is 1 (true), which means strcmp(“LETMEWIN\n”, buf) must be 0, which means buf must be equal to “LETMEWIN\n”. Not that complicated, just read again without fear :)

This still does not explain why when we enter “LETMEWIN\n” after the stall solves our problem. Or does it?

When we passed 4660 as input, the result of fd in int fd = atoi( argv[1] ) - 0x1234; was 0. The code then feeds read() with this value. And then it stalls!

What happens here is that read() is acutally a system call, i.e. a call into the kernel code. Once this function is called, the kernel’s implementation of read will check the file descriptor and dispatch it. Normally, any program in an Unix environment has 3 file descriptors:

  • Standard input = 0
  • Standard output = 1
  • Standard error = 2

If the file descriptor (fd) is 0, the process read from stdin. If 1, from stdout and if 2 from sterr.In our case, fd is 0, which means to the kernel ot should look for the terminal for inputs util we have either typed 32 characters, a newline or an EOF marker (Ctrl+D). Then it is simply a matter of passing the input we want o/

Of course, once we have understood the solution, we must go back in the server to run it.

fd@ubuntu:~$ ./fd 4660
LETMEWIN
good job :)
mommy! I think I know what a file descriptor is!!

Marcos Valle

Born to kill bugs. Live by them.