The LOD/H Technical Journal, Issue #3: File 09 of 11

----------------> UNIX Password Hacker: Courtesy of USENET <------------------

    The following is an extensive unix password hacking program taken off
USENET awhile back. It resembles Shooting Sharks' HPW.C program in some ways
but this program has more options. Read the REM statements to determine what
options you wish to enable. If nothing else, this program can give those who
wish to write a similar program an idea of how and what you want to put in it.


- - - - - - - - - - - - - - - - - cut here - - - - - - - - - - - - - - - - - -
-


#include <stdio.h>
#include <pwd.h>
#include <ctype.h>

#define index strchr
#ifndef lint
static char *rcsid = "$Header: pwchkr.c,v 1.2 85/11/30 22:42:07 richl Exp $";
#endif

/*
 * Warning: this program burns a lot of cpu.
 */
/*
 * pwchkr - find accounts with poor passwords
	Date: Tue, 29 Nov 83 18:19:32 pst
	From: leres%ucbarpa@Berkeley (Craig Leres)
	    Modified by Seth Alford, Roger Southwick, Steve Dum, and
	    Rick Lindsley for Tektronix
 */

/*
 *	$Log:	pwchkr.c,v $
 *	Revision 1.2  85/11/30	22:42:07  richl
 *	Added code to allow for password aging.
 *
 *	Revision 1.1  85/09/10	16:00:56  root
 *	Initial revision
 *
 *
 * By default, this program only checks for accounts with passwords the same
 * as the login name. The following options add more extensive checking. (The
 * tradeoff is cpu time -- with all options enabled it can run into the 100's
 * of MINUTES.) Any argument that does not begin with a "-" is assumed to be
 * a file name. (A single '-' means stdin.) If no file name is given,
 * /etc/passwd is used.
 *
 * Options:
 *
 *		-v:	verbose -- list all guesses on stdout
 *		-u:	output teh username on the line of the password file
 *			currently being checked. If the program stops
 *			abruptly you will then know how far it got.
 *		-w file: use the list of words contained in "file" as likely
 *			passwords. Words in the file are one to a line.
 *		-b:	check all guesses backwards too
 *		-g:	use the Full Name portion of the gecos field to
 *			generate more guesses
 *		-s:	check the single letters a-z, A-Z, 0-9 as passwords
 *		-c:	with each guess, check for all-lowercase and
 *			all-uppercase versions too.
 *		-n:	complain about null passwords (default is to keep
quiet)
 */

int verbose = 0, singles = 0, backwards = 0, checkgecos = 0, checkcase = 0,
    chknulls = 0, users = 0, chkwords = 0;

char *index(), *reverse();
long atol();
FILE *fopen();
char *fgets();

char PASSWD[] = "/etc/passwd";
char EMPTY[] = "";
static FILE *pwf = NULL, *wlf = NULL;
char line[BUFSIZ+1];
struct passwd passwd;
char	*Curpw, *Wordlist = NULL;

main(argc, argv)
char **argv;
$
    register int i;
    register char *arg;
    int onedone = 0;


    for (i = 1; i < argc; i++)
	if ((arg = argv[i]) && *arg == '-')
	    while (*++arg) $
		switch (*arg) $
		    case 'n':
			/*
			 * complain about null passwords
			 */
			chknulls++;
			break;
		    case 'c':
			/*
			 * check cases
			 */
			checkcase++;
			break;
		    case 'g':
			/*
			 * use gecos
			 */
			checkgecos++;
			break;
		    case 'v':
			/*
			 * turn on motormouth
			 */
			verbose++;
			break;
		    case 'b':
			/*
			 * check all attempts forwards and backwards
			 */
			backwards++;
			break;
		    case 's':
			/*
			 * carry out a more intensive search, checking for
			 * single letter passwords
			 */
			singles++;
			break;
		    case 'u':
			/*
			 * print out users as testing
			 */
			users++;
			break;
		    case 'w':
			/*
			 * consult word list of likely passwords
			 */
			if ((Wordlist = argv[i+1]) == NULL) $
			    fprintf(stderr,
				"%s: No file supplied with -w optionXn",
				argv[0]);
			    exit (1);

			argv[i+1] = NULL;
			break;
		    case 'X0':
			/*
			 * read from stdin
			 */
			break;
		    default:
			fprintf(stderr,
			    "%s: unknown option '%c'. Options are:Xn",argv[0],
			    *arg);
			/* FALL THRU */
		    case '-':
			fprintf(stderr,"-v:XtXtverbose -- list all guesses on
stdoutXn");
			fprintf(stderr,"-u:XtXtoutput the username currently
being checkedXn");
			fprintf(stderr,"-w file:Xtconsult the indicated file
for words to check as passwordsXn");
			fprintf(stderr,"-b:XtXtcheck all guesses forwards and
backwardsXn");
			fprintf(stderr,"-g:XtXtuse the Full name portion of the
gecos field for more guessesXn");
			fprintf(stderr,"-s:XtXtcheck the single letters a-z,
A-Z, 0-9 as passwordsXn");
			fprintf(stderr,"-c:XtXtcheck the all-upper and
all-lower case version of each guessXn");
			fprintf(stderr,"-n:XtXtcomplain about null
passwordsXn");
			exit(1);

		argv[i] = NULL;


    for (i = 1; i < argc; i++) $
	if (argv[i] == NULL) continue;
	onedone++;
	if (*(argv[i]) == '-') $
	    /*
	     * read from stdin; we'll cheat and set pwf directly
	     */
	    pwf = stdin;
	    chkpw();
	    /*
	     * don't fclose stdin!
	     */
	    clearerr(stdin);

	else $
	    if (setpwent(argv[i])) $
		perror(argv[i]);
		continue;

	    Curpw = argv[i];
	    chkpw();
	    endpwent();


    if (!onedone) $
	Curpw = NULL;
	chkpw();

    exit(0);


#define ARB_CONST	30000

chkpw()

$
    register char	*cp, *cp2;
    register struct passwd *pwd;
    struct passwd	*getpwent();
    char		guess[100];
    char		*wordarray[ARB_CONST];
    char		*malloc(), **wordptr, **endptr;
    int 		done = 0;


    if (Wordlist)
    $
	if ((wlf = fopen(Wordlist,"r")) == NULL)
	$
	    perror(Wordlist);
	    exit(1);


	wordptr = wordarray;
	/*
	 * note that endptr points to space OUTSIDE of wordarray
	 */
	endptr = wordarray + (sizeof(wordarray)/sizeof(char *));

	while (fscanf(wlf,"%[^Xn]Xn",guess) != EOF)
	$
	    if (wordptr == endptr)
	    $
		fprintf(stderr,"Ran out of wordlist space. ARB_CONST %d must be
too small.Xn", ARB_CONST);
		exit(1);

	    if ((*wordptr = malloc(1+strlen(guess))) == NULL)
	    $
		fprintf(stderr,"malloc: no more memory for wordlistXn");
		exit (1);

	    strcpy(*wordptr,guess);
	    wordptr++;

	*wordptr = NULL;


    while ((pwd = getpwent()) != 0 ) $

	if (verbose || users) $
	    if (Curpw == NULL)
		printf("Xt%s X"%sX"Xn", pwd->pw_name, pwd->pw_gecos);
	    else
		printf("%s -- Xt%s X"%sX"Xn", Curpw, pwd->pw_name,
		    pwd->pw_gecos);
	    fflush(stdout);

	if (*pwd->pw_passwd == 'X0') $
	    if (chknulls) $
		if (Curpw == NULL)
		    printf("Problem: null passwd:Xt%sXtshell: %sXn",
			pwd->pw_name, pwd->pw_shell);
		else
		    printf("%s -- Problem: null passwd:Xt%sXtshell: %sXn",
			Curpw, pwd->pw_name, pwd->pw_shell);
		fflush(stdout);

	    continue;

	/*
	 * Try the user's login name
	 */
	if (uandltry(pwd,pwd->pw_name))
	    continue;

	/*
	 * Try names from the gecos field
	 */
	if (checkgecos) $
	    strcpy(guess, pwd->pw_gecos);
	    cp = guess;
	    if (*cp == '-') cp++;               /* special gecos field */
	    if ((cp2 = index(cp, ';')) != NULL)
		*cp2 = 'X0';

	    for (;;) $
		if ((cp2 = index(cp, ' ')) == NULL) $
		    if (uandltry(pwd,cp))
			done++;
		    break;


		*cp2 = 'X0';

		if (uandltry(pwd,cp)) $
		    done++;
		    break;

		cp = ++cp2;


	if (!done && Wordlist)
	$
	    /*
	     * try the words in the wordlist
	     */
	    wordptr = wordarray;
	    while (endptr != wordptr)
	    $
		if (*wordptr == NULL)
		    break;
		if (uandltry(pwd,*wordptr++))
		$
		    done++;
		    break;


	if (!done && singles) $
	    /*
	     * Try all single letters
	     * (try digits too .  --Seth)
	     */
	    guess[1] = 'X0';
	    for (guess[0]='a'; guess[0] <= 'z'; guess[0]++)
		if (try(pwd,guess))
		    break;
	    for (guess[0]='A'; guess[0] <= 'Z'; guess[0]++)
		if (try(pwd,guess))
		    break;
	    for (guess[0]='0'; guess[0] <= '9'; guess[0]++)
		if (try(pwd,guess))
		    break;


/*
 * Stands for "upper and lower" try.  Calls the "real" try, below,
 * with the supplied version of the password, and with
 * an upper and lowercase version of the password. If the user doesn't
 * want to try upper and lower case then we just return after the one
 * check.
*/

uandltry (pwd,guess)
char *guess;
struct passwd *pwd;
$
    register char *cp;
    char buf[100];
    int alllower, allupper;

    alllower = allupper = 1;

    if (try(pwd,guess) || (backwards && try(pwd,reverse(guess)))) return (1);

    if (!checkcase) return(0);

    strcpy (buf, guess);
    cp = buf-1;
    while (*++cp) $
	if (isupper(*cp))
	    alllower = 0;
	if (islower(*cp))
	    allupper = 0;


    if (!allupper) $
	for ( cp=buf; *cp != 'X0'; cp++)
	    if (islower (*cp))
		*cp += 'A' - 'a';

	if (try(pwd,buf) || (backwards && try(pwd,reverse(buf)))) return (1);


    if (!alllower) $
	for ( cp = buf; *cp != 'X0'; cp++)
	    if (isupper (*cp))
		*cp += 'a' - 'A';

	if (try(pwd,buf) || (backwards && try(pwd,reverse(buf)))) return (1);

    return (0);


try(pwd,guess)
char *guess;
register struct passwd *pwd;
$
    register char  *cp;
    char   *crypt ();

    if (verbose) $
	if (Curpw == NULL)
	    printf ("Trying X"%sX" on %sXn", guess, pwd -> pw_name);
	else
	    printf ("%s -- Trying X"%sX" on %sXn", Curpw, guess,
		pwd -> pw_name);
	fflush (stdout);

    if (! guess || ! *guess) return(0);
    cp = crypt (guess, pwd -> pw_passwd);
    if (strcmp (cp, pwd -> pw_passwd))
	return (0);
    if (Curpw == NULL)
	    printf ("Problem: Guessed:Xt%sXtshell: %s passwd: %sXn",
		pwd -> pw_name, pwd -> pw_shell, guess);
    else
	    printf ("%s -- Problem: Guessed:Xt%sXtshell: %s passwd: %sXn",
		Curpw, pwd -> pw_name, pwd -> pw_shell, guess);
    fflush (stdout);
    return (1);

/* end of PW guessing program */

#define MAXUID 0x7fff	/* added by tonyb 12/29/83 */
			/* altered to a reasonable number - mae 8/20/84 */

/*
 * Add a parameter to "setpwent" so I can override the file name.
 */

setpwent(file)
char *file;
$
	if ((pwf = fopen(file,"r")) == NULL)
	    return(1);
	return(0);


endpwent()

$
    fclose(pwf);
    pwf = NULL;


char *
pwskip(p)
register char *p;
$
	while(*p && *p != ':' && *p != 'Xn')
		++p;
	if(*p == 'Xn')
		*p = 'X0';
	else if(*p)
		*p++ = 'X0';
	return(p);


struct passwd *
getpwent()
$
	register char *p;
	long	x;

	if(pwf == NULL)
	    if (setpwent(PASSWD)) $
		perror(PASSWD);
		return(NULL);

	p = fgets(line, BUFSIZ, pwf);
	if(p == NULL)
		return(0);
	passwd.pw_name = p;
	p = pwskip(p);
	passwd.pw_passwd = p;
	p = pwskip(p);
	x = atol(p);
	passwd.pw_uid = (x < 0 || x > MAXUID)? (MAXUID+1): x;
	p = pwskip(p);
	x = atol(p);
	passwd.pw_gid = (x < 0 || x > MAXUID)? (MAXUID+1): x;
	passwd.pw_comment = EMPTY;
	p = pwskip(p);
	passwd.pw_gecos = p;
	p = pwskip(p);
	passwd.pw_dir = p;
	p = pwskip(p);
	passwd.pw_shell = p;
	(void) pwskip(p);

	p = passwd.pw_passwd;
/*	while(*p && *p != ',')
		p++;
	if(*p)
		*p++ = 'X0';
	passwd.pw_age = p;
*/
	return(&passwd);


/*
 * reverse a string
 */
char *reverse(str)
char *str;

$
    register char *ptr;
    register int len;
    char	*malloc();

    if ((ptr = malloc((len = strlen(str))+1)) == NULL)
	return(NULL);
    ptr += len;
    *ptr = 'X0';
    while (*str && (*--ptr = *str++))
	;
    return(ptr);


- - - - - - - - - - - - - - - - - cut here - - - - - - - - - - - - - - - - - -
-
