← back

fd

pwnable.kr·pwn·easy

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!