Pwnable.kr: ‘fd’ Walkthrough

This will be (hopefully!) one of many CFP walkthroughs.  I’m going to start by tackling the “Toddler’s Bottle” challenges from Pwnable.kr.  The first one is ‘fd’.  Here’s our hint:

Mommy! what is a file descriptor in Linux?

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

fd

First of all, what is fd?  It’d probably be a good idea to know before going any further.  Googling “man page fd” gets me this wonderful Linux reference guide for read.  Normal people probably don’t read documentation in their spare time, but I went to school for electrical engineering, so I’m all about that.  🤓

“fd” is short for “file descriptor”, and as you might have guessed by the read man page, is used for reading files.

ssh

The command provided for us is:

ssh fd@pwnable.kr -p 2222

Once again, man pages can be our friend.  Ssh allows us to log into a remote machine, as a specified user, on a given port.

Here, we are connecting to pwnable.kr as the user “fd”, on port (-p) 2222.

We’ll be asked for the password, which is given to us (“guest”):

$ ssh fd@pwnable.kr -p 2222
fd@pwnable.kr's password: guest
 ____  __    __  ____    ____  ____   _        ___      __  _  ____ 
|    \|  |__|  ||    \  /    ||    \ | |      /  _]    |  |/ ]|    \
|  o  )  |  |  ||  _  ||  o  ||  o  )| |     /  [_     |  ' / |  D  )
|   _/|  |  |  ||  |  ||     ||     || |___ |    _]    |    \ |    /
|  |  |  `  '  ||  |  ||  _  ||  O  ||     ||   [_  __ |     \|    \
|  |   \      / |  |  ||  |  ||     ||     ||     ||  ||  .  ||  .  \
|__|    \_/\_/  |__|__||__|__||_____||_____||_____||__||__|\_||__|\_|
                                                                   
- Site admin : daehee87.kr@gmail.com
- IRC : irc.netgarage.org:6667 / #pwnable.kr
- Simply type "irssi" command to join IRC now
- files under /tmp can be erased anytime. make your directory under /tmp
- to use peda, issue `source /usr/share/peda/peda.py` in gdb terminal
fd@ubuntu:~$

Check out that sweet, sweet ASCII art.

Now what?

Okay, [hacker voice] we’re in.  If we type ls to see what’s here, we see:

fd     fd.c     flag

If we type ls -la to get a little bit more information, we see:

fd@ubuntu:~$ ls -la
total 40
drwxr-x--- 5 root fd 4096 Oct 26 2016 .
drwxr-xr-x 87 root root 4096 Dec 27 23:17 ..
d--------- 2 root root 4096 Jun 12 2014 .bash_history
-r-sr-x--- 1 fd_pwn fd 7322 Jun 11 2014 fd
-rw-r--r-- 1 root root 418 Jun 11 2014 fd.c
-r--r----- 1 fd_pwn root 50 Jun 11 2014 flag
-rw------- 1 root root 128 Oct 26 2016 .gdb_history
dr-xr-xr-x 2 root root 4096 Dec 19 2016 .irssi
drwxr-xr-x 2 root root 4096 Oct 23 2016 .pwntools-cache

The file fd is owned by user fd_pwn, who also owns the flag file (which we don’t have permissions to access).  If we can get the fd file to read the flag file for us, we’re golden.  So let’s look at fd.c to see what the program does.

If we use cat or vi or nano to look at fd.c, we see:

#include
#include
#include

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

If we try to run the current executable:

$ ./fd
pass argv[1] a number

Which we expect from the fd.c file.

If we give it a random number:

$ ./fd 2
learn about Linux file IO

… also expected.  So let’s pick through the C file:

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

Atoi  converts a string to an integer.  Then, we subtract 0x1234, and assign that to the variable “fd”.

Then, we see:

len = read(fd, buf, 32)

Back to our read man page, we see that

ssize_t read(int fd, void *buf, size_t count);

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

The count (32) and buffer are already set for us.  If we can pass the string compare (strcmp) check, the program will read the flag file for us:

if(!strcmp("LETMEWIN\n", buf)) {
    printf("good job :)\n");
    system("/bin/cat flag");
    exit(0);
}

To recap:

Pass in a number that, when we subtract 0x1234 from it, reads from a file and copies the string LETMEWIN\n into the buffer.

However… where are we going to find a LETMEWIN\n string lying around?  Can we make our own?

On Linux, file descriptor 0 is standard input, so if we can set the file descriptor to 0, we can type in our LETMEWIN\n string.

0x1234 is hex notation… we need decimal.  A quick Google search will get you “4660” as the decimal equivalent of 0x1234.  We can pass that to fd (the program), which will set fd (the variable) to zero, and let us type in the magic phrase.

fd@ubuntu:~$ ./fd 4660

…which gives me a blank line. I then type (and hit return after):

LETMEWIN

And it prints out:

good job :)
mommy! I think I know what a file descriptor is!!

That last line is our flag! Voilà!