Pwnable.kr - 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 | [hg8@archbook ~]$ ssh [email protected] -p2222 |
Let’s a take a look to understand what it’s doing:
1 |
|
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 | if(argc<2){ |
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 | if(!strcmp("LETMEWIN\n", buf)){ |
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 | fd@pwnable:~$ ls -l 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:
- Create a file containing
LETMEWIN\n
- Get the file descriptor of this file
- Subtract
4660
from this file descriptor - 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 | fd@pwnable:~$ printf "LETMEWIN\n" > /tmp/.hg8 |
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 | fd@pwnable:~$ ./fd 4670 |
Method 2 (Recommended)
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 | fd@pwnable:~$ ./fd 4660 |
We get a blank line with no error messages, let’s type LETMEWIN
then enter:
1 | fd@pwnable:~$ ./fd 4660 |
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 | from pwn import * |
Pop a shell and run ./fd
process with 4660
as argument, and then send the LETMEIN
secret:
1 | shell = server.process(['./fd', str(4660)]) |
After closing the connection and printing the result the final script looks like this:
1 | #!/usr/bin/env python |
Running it:
1 | [hg8@archbook ~]$ python exploit.py |
That’s it folks! As always do not hesitate to contact me for any questions or feedbacks!
See you next time ;)
-hg8