|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T c
Length: 22280 (0x5708)
Types: TextFile
Names: »crack-pwc.c«
└─⟦4f9d7c866⟧ Bits:30007245 EUUGD6: Sikkerheds distributionen
└─⟦this⟧ »./crack/Sources/crack-pwc.c«
/*
* This program is copyright Alec Muffett 1991 except for some portions of
* code in "crack-fcrypt.c" which are copyright Robert Baldwin, Icarus Sparry
* and Alec Muffett. The author(s) disclaims all responsibility or liability
* with respect to it's usage or its effect upon hardware or computer
* systems, and maintain copyright as set out in the "LICENCE" document which
* accompanies distributions of Crack v4.0 and upwards.
*/
#include "crack.h"
#define DOTFILESIZE 1024
#define WORDSTACKSIZE 512
/*
* crack-pwc.c - an optimised password cracker. (c) ADE Muffett, Feb 1992. If
* this won't break your password file, it's unlikely that anything else
* will.
*/
/* trap a signal on shutdown */
void
CatchTERM ()
{
/* bury magnets */
Log ("Caught a SIGTERM! Commiting suicide...\n");
/* swallow the rapture */
Log ("<argh!>\n");
/* let's gather feathers */
sync ();
/* don't fall on me */
exit (0);
/* 'Fall on Me' by R.E.M. */
}
/* jump ':' separated fields in an input */
char *
PWSkip (p)
register char *p;
{
while (*p && *p != ':')
{
p++;
}
if (*p)
{
*p++ = '\0';
}
return (p);
}
char *
Archive (myword)
register char *myword;
{
register int i;
register struct DICT *ptr;
static struct DICT *arch_root;
for (ptr = arch_root; ptr; ptr = ptr -> next)
{
if (!STRCMP (ptr -> word, myword))
{
return (ptr -> word);
}
}
i = strlen (myword);
ptr = (struct DICT *) malloc (sizeof (struct DICT) + i);
if (ptr)
{
strcpy (ptr -> word, myword);
ptr -> word[i] = '\0';
ptr -> next = arch_root;
arch_root = ptr;
} else
{
Log ("Archive/malloc() failed! Fatal lack of memory!\n");
exit (1);
}
return (ptr -> word);
}
/* parse and store a password entry */
struct USER *
Parse (buffer)
register char *buffer;
{
register char *p;
register struct USER *retval;
retval = (struct USER *) malloc (sizeof (struct USER));
if (!retval)
{
Log ("Parse/malloc() failed! Fatal lack of memory!\n");
exit (1);
}
retval -> next = retval -> across = NULL;
retval -> passwd_txt = NULL;
retval -> done = 0;
Trim (buffer);
p = PWSkip (buffer);
retval -> filename = Archive (buffer);
p = Clone (p);
if (!p)
{
Log ("Parse/Clone() failed! Fatal lack of memory!\n");
exit (1);
}
retval -> passwd.pw_name = p;
p = PWSkip (p);
retval -> passwd.pw_passwd = p;
p = PWSkip (p);
retval -> passwd.pw_uid = atoi (p);
p = PWSkip (p);
retval -> passwd.pw_gid = atoi (p);
p = PWSkip (p);
retval -> passwd.pw_gecos = p;
p = PWSkip (p);
retval -> passwd.pw_dir = p;
p = PWSkip (p);
retval -> passwd.pw_shell = p;
return (retval);
}
/* load pre-formatted password entries off stdin into linked list */
int
LoadData ()
{
int i;
char *ptr;
char salt[2];
char buffer[STRINGSIZE];
long int numlines;
long int numentries;
register struct USER *new_element;
register struct USER *current_line;
numlines = 0L;
numentries = 0L;
current_line = NULL;
salt[0] = salt[1] = '*';
while (fgets (buffer, STRINGSIZE, stdin))
{
if (!*buffer || isspace (*buffer))
{
continue;
}
new_element = Parse (buffer);
ptr = new_element -> passwd.pw_passwd;
if (!ptr[0])
{
Log ("Warning! %s (%s in %s) has a NULL password!\n",
new_element -> passwd.pw_name,
new_element -> passwd.pw_shell,
new_element -> filename);
continue;
}
if (strchr (ptr, '*') ||
strchr (ptr, '!') ||
strchr (ptr, ' '))
{
Log ("User %s (in %s) has a locked password:- %s\n",
new_element -> passwd.pw_name,
new_element -> filename,
new_element -> passwd.pw_passwd);
continue;
}
i = strlen (ptr);
if (i < 13)
{
Log ("User %s (in %s) has a short pw_passwd field - skipping.\n",
new_element -> passwd.pw_name,
new_element -> filename);
continue;
}
if (i > 13)
{
Log ("User %s (in %s) has a long pw_passwd field - truncating.\n",
new_element -> passwd.pw_name,
new_element -> filename);
ptr[13] = '\0';
}
numentries++;
if (ptr[0] == salt[0] && ptr[1] == salt[1])
{
new_element -> across = current_line;
current_line = new_element;
} else
{
if (current_line)
{
current_line -> next = userroot;
}
userroot = current_line;
current_line = new_element;
numlines++;
salt[0] = ptr[0];
salt[1] = ptr[1];
}
}
if (current_line) /* last one tends to hang about */
{
current_line -> next = userroot;
userroot = current_line;
numlines++;
}
--numlines;
if (numentries)
{
Log ("Loaded %ld password entries with %ld different salts: %d%%\n",
numentries,
numlines,
((numlines * 100) / numentries));
} else
{
Log ("No input supplied: everything removed by feedback ?\n");
}
return (numentries);
}
/* and load rules from a standard file into a similar list */
int
LoadRules (file, rootpos)
char *file;
struct RULE **rootpos;
{
FILE *fp;
int numrules;
struct RULE fencepost;
register struct RULE *addinto;
register struct RULE *scratch;
char buffer[STRINGSIZE];
if (!(fp = fopen (file, "r")))
{
Log ("cannot open rulefile %s\n", file);
perror (file);
return (-1);
}
numrules = 0;
addinto = &fencepost;
addinto -> next = (struct RULE *) 0;
while (fgets (buffer, STRINGSIZE, fp))
{
Trim (buffer);
if (!buffer[0] || buffer[0] == '#')
{
continue;
}
scratch = (struct RULE *) malloc (sizeof (struct RULE));
if (!scratch)
{
Log ("LoadRules/malloc() failed! Fatal lack of memory!\n");
exit (1);
}
scratch -> rule = Clone (buffer);
if (!scratch -> rule)
{
Log ("LoadRules/Clone() failed! Fatal lack of memory!\n");
exit (1);
}
scratch -> next = (struct RULE *) 0;
addinto -> next = scratch;
addinto = scratch;
numrules++;
}
fclose (fp);
Log ("Loaded %d rules from '%s'.\n", numrules, file);
*rootpos = fencepost.next;
return (numrules);
}
/* load a dictionary into a linked list, and sort it */
long int
LoadDict (file, rule, contdict)
char *file;
char *rule;
int contdict;
{
int i;
int memfilled;
long int nelem;
long int rejected;
register char *mangle;
register struct DICT *scratch;
char pipebuff[STRINGSIZE];
static FILE *fp;
char buffer[STRINGSIZE];
if (contdict && fp)
{
goto files_open;
}
#ifdef COMPRESSION
if (!Suffix (file, ".Z"))
{
sprintf (pipebuff, "%s %s", zcat, file);
if (!(fp = (FILE *) popen (pipebuff, "r")))
{
perror (pipebuff);
return (0);
}
} else if (!Suffix (file, ".z"))
{
sprintf (pipebuff, "%s %s", pcat, file);
if (!(fp = (FILE *) popen (pipebuff, "r")))
{
perror (pipebuff);
return (0);
}
} else
#endif /* COMPRESSION */
{
pipebuff[0] = '\0';
if (!(fp = fopen (file, "r")))
{
perror (file);
return (0);
}
}
files_open:
nelem = 0;
rejected = 0;
memfilled = 0;
dictroot = (struct DICT *) 0;
Log ("%s rule '%s' to file '%s'\n",
contdict ? "Continuing" : "Applying",
rule,
file);
while (fgets (buffer, STRINGSIZE, fp))
{
Trim (buffer);
if (!buffer[0] || buffer[0] == '#')
{
continue;
}
mangle = Mangle (buffer, rule);
if (!mangle)
{
rejected++;
if (verbose_bool)
{
Log ("Rejected '%s' due to rule specs.\n", buffer);
}
continue;
}
if (dictroot && !strncmp (mangle, dictroot -> word, pwlength))
{
rejected++;
if (verbose_bool)
{
Log ("Rejected '%s'; duplicated to %d chars.\n", buffer,
pwlength);
}
continue;
}
i = strlen (mangle);
if (i > pwlength)
{
i = pwlength;
}
scratch = (struct DICT *) malloc (sizeof (struct DICT) + i);
if (!scratch)
{
Log ("LoadDict/malloc() failed! Shameful lack of memory!\n");
memfilled = 1;
goto words_loaded;
}
strncpy (scratch -> word, mangle, i);
scratch -> word[i] = '\0';
scratch -> next = dictroot;
dictroot = scratch;
nelem++;
if (verbose_bool)
{
Log ("Loaded '%s' as '%s' using '%s'\n", buffer,
scratch -> word, rule);
}
}
if (pipebuff[0])
{
pclose (fp);
} else
{
fclose (fp);
}
fp = (FILE *) 0;
words_loaded:
if (nelem == 0)
{
return (0);
}
Log ("Rejected %ld words on loading, %ld words left to sort\n",
rejected, nelem);
dictroot = (struct DICT *) SortDict (dictroot, nelem);
if (memfilled)
{
nelem = -nelem;
}
return (nelem); /* not strict number anymore... */
}
/* lose the current dictionary */
int
DropDict ()
{
register struct DICT *scratch1;
register struct DICT *scratch2;
scratch1 = dictroot;
while (scratch1)
{
scratch2 = scratch1 -> next;
free (scratch1);
scratch1 = scratch2;
}
return (0);
}
/*
* write a feedback file if there is anything to save - return number
* uncracked users
*/
int
FeedBack (log_notdone)
int log_notdone;
{
register FILE *fp;
static char fmt[] = "%s:%s:%s:%s\n";
register struct USER *head;
register struct USER *arm;
int done;
int notdone;
notdone = done = 0;
if (verbose_bool)
{
Log ("Sweeping data looking for feedback.\n");
}
fp = (FILE *) 0;
for (head = userroot; head; head = head -> next)
{
for (arm = head; arm; arm = arm -> across)
{
if (arm -> done)
{
done++;
/* horrible little hack, vile, sick, I love it */
if (!fp)
{
if (!(fp = fopen (feedbackfile, "w")))
{
perror (feedbackfile);
return (-1);
}
if (verbose_bool)
{
Log ("Feedback file opened for writing.\n");
}
}
fprintf (fp, fmt, feedback_string,
arm -> passwd.pw_passwd, "Y", arm -> passwd_txt);
} else
{
notdone++;
if (log_notdone)
{
if (!fp) /* and again !!! heheheheheheh */
{
if (!(fp = fopen (feedbackfile, "w")))
{
perror (feedbackfile);
return (-1);
}
if (verbose_bool)
{
Log ("Feedback file opened for writing.\n");
}
}
/* I think I'm going slightly warped */
fprintf (fp, fmt, feedback_string,
arm -> passwd.pw_passwd, "N", "");
}
}
}
}
if (fp)
{
fclose (fp);
Log ("Closing feedback file.\n");
}
Log ("FeedBack: %d users done, %d users left to crack.\n", done, notdone);
return (notdone);
}
/* try a chain of users with the same salt */
int
TryManyUsers (eptr, guess) /* returns 0 if all done this chain */
register struct USER *eptr;
char *guess;
{
register int retval;
char guess_crypted[STRINGSIZE];
if (eptr -> done && !eptr -> across)
{
return (0);
}
strcpy (guess_crypted, crypt (guess, eptr -> passwd.pw_passwd));
retval = 0;
while (eptr)
{
if (verbose_bool)
{
Log ("Trying '%s' on %s from line %s\n",
guess,
eptr -> passwd.pw_name,
eptr -> filename);
}
if (!eptr -> done && !STRCMP (guess_crypted, eptr -> passwd.pw_passwd))
{
PrintGuess (eptr, guess);
}
retval += (!(eptr -> done));
eptr = eptr -> across;
}
return (retval);
}
/* try a word on an individual */
int
TryOneUser (eptr, guess) /* returns non-null on guessed user */
register struct USER *eptr;
register char *guess;
{
if (!guess || !*guess || eptr -> done)
{
return (0);
}
if (verbose_bool)
{
Log ("Trying '%s' on %s from %s\n",
guess,
eptr -> passwd.pw_name,
eptr -> filename);
}
if (strcmp (crypt (guess, eptr -> passwd.pw_passwd),
eptr -> passwd.pw_passwd))
{
return (0);
}
PrintGuess (eptr, guess);
return (1);
}
/* frontend to TryOneUser() to save hassle */
int
WordTry (entry_ptr, guess)
register struct USER *entry_ptr;
register char *guess;
{
struct RULE *ruleptr;
register char *mangle;
if (!guess[0] || !guess[1])
{
return (0);
}
for (ruleptr = gecosroot; ruleptr; ruleptr = ruleptr -> next)
{
if (mangle = Mangle (guess, ruleptr -> rule))
{
if (TryOneUser (entry_ptr, mangle))
{
return (1);
}
}
}
return (0);
}
/* Special manipulations for the GECOS field and dotfiles */
int
ParseBuffer (entry_ptr, buffer, advanced)
register struct USER *entry_ptr;
char *buffer;
int advanced;
{
int wordcount;
register int i;
register int j;
register char *ptr;
char junk[STRINGSIZE];
char *words[WORDSTACKSIZE];
/* zap all punctuation */
for (ptr = buffer; *ptr; ptr++)
{
if (ispunct (*ptr) || isspace (*ptr))
{
*ptr = ' ';
}
}
/* break up all individual words */
wordcount = 0;
ptr = buffer;
while (*ptr)
{
while (*ptr && isspace (*ptr))
{
ptr++;
}
if (*ptr)
{
words[wordcount++] = ptr;
if (wordcount >= WORDSTACKSIZE)
{
Log ("ParseBuffer: Abort: Stack Full !\n");
return (0);
}
}
while (*ptr && !isspace (*ptr))
{
ptr++;
}
if (*ptr)
{
*(ptr++) = '\0';
}
}
words[wordcount] = (char *) 0;
/* try all the words individually */
if (verbose_bool)
{
Log ("Trying individual words\n");
}
for (i = 0; i < wordcount; i++)
{
if (WordTry (entry_ptr, words[i]))
{
return (1);
}
}
if (!advanced)
{
return (0);
}
/* try pairings of words */
if (verbose_bool)
{
Log ("Trying paired words\n");
}
for (j = 1; j < wordcount; j++)
{
for (i = 0; i < j; i++)
{
/* Skip initials for next pass */
if (!words[i][1] || !words[j][1])
{
continue;
}
strcpy (junk, words[i]);
strcat (junk, words[j]);
if (WordTry (entry_ptr, junk))
{
return (1);
}
strcpy (junk, words[j]);
strcat (junk, words[i]);
if (WordTry (entry_ptr, junk))
{
return (1);
}
}
}
/* try initials + words */
if (verbose_bool)
{
Log ("Trying initial'ed words\n");
}
for (j = 1; j < wordcount; j++)
{
for (i = 0; i < j; i++)
{
junk[0] = words[i][0];
junk[0] = CRACK_TOUPPER (junk[0]);
strcpy (junk + 1, words[j]);
if (WordTry (entry_ptr, junk))
{
return (1);
}
}
}
return (0);
}
/* run over password entries looking for passwords */
void
Pass1 ()
{
struct USER *head;
char junk[DOTFILESIZE];
register struct USER *this;
#ifdef CRACK_DOTFILES
#ifdef CRACK_DOTSANE
#include <sys/types.h>
#include <sys/stat.h>
struct stat sb;
#endif /* CRACK_DOTSANE */
int i;
int j;
FILE *fp;
char filename[STRINGSIZE];
static char *dotfiles[] =
{
".plan",
".project",
".signature",
(char *) 0
};
#endif /* CRACK_DOTFILES */
Log ("Starting pass 1 - password information\n");
for (head = userroot; head; head = head -> next)
{
for (this = head; this; this = this -> across)
{
strcpy (junk, this -> passwd.pw_gecos);
if (WordTry (this, this -> passwd.pw_name) ||
ParseBuffer (this, junk, 1))
{
continue;
}
#ifdef CRACK_DOTFILES
for (i = 0; dotfiles[i]; i++)
{
sprintf (filename, "%s/%s", this -> passwd.pw_dir, dotfiles[i]);
#ifdef CRACK_DOTSANE
if (stat (filename, &sb) < 0)
{
continue;
}
if ((!(sb.st_mode & S_IFREG))
#ifdef S_IFSOCK
|| ((sb.st_mode & S_IFSOCK) == S_IFSOCK)
#endif /* S_IFSOCK */
)
{
continue;
}
#endif /* CRACK_DOTSANE */
if (!(fp = fopen (filename, "r")))
{
continue;
}
j = fread (junk, 1, DOTFILESIZE, fp);
fclose (fp);
if (j <= 2)
{
continue;
}
junk[j - 1] = '\0'; /* definite terminator */
if (verbose_bool)
{
Log ("DOTFILES: Checking %d bytes of %s\n", j, filename);
}
if (ParseBuffer (this, junk, 0))
{
continue;
}
}
#endif /* CRACK_DOTFILES */
}
}
return;
}
void
Pass2 (dictfile)
char *dictfile;
{
int pointuser;
struct USER *headptr;
struct RULE *ruleptr;
struct DICT *dictptr;
Log ("Starting pass 2 - dictionary words\n");
headptr = (struct USER *) 0;
ruleptr = (struct RULE *) 0;
/* check if we are recovering from a crash */
if (recover_bool)
{
recover_bool = 0; /* switch off */
for (ruleptr = ruleroot;
ruleptr && strcmp (ruleptr -> rule, old_rule);
ruleptr = ruleptr -> next);
if (!ruleptr)
{
Log ("Fatal: Ran off end of list looking for rule '%s'\n",
old_rule);
exit (1);
}
for (headptr = userroot;/* skip right number of users */
headptr && old_usernum--;
headptr = headptr -> next);
if (!headptr)
{
Log ("Fatal: Ran off end of list looking for user '%s'\n",
old_username);
exit (1);
}
}
/* start iterating here */
for (ruleptr = (ruleptr ? ruleptr : ruleroot);
ruleptr;
ruleptr = ruleptr -> next)
{
long int rval;
int continue_dict;
continue_dict = 0;
load_dict:
rval = LoadDict (dictfile, ruleptr -> rule, continue_dict);
if (rval == 0)
{
Log ("Oops! I got an empty dictionary! Skipping rule '%s'!\n",
ruleptr -> rule);
continue;
}
pointuser = 0;
/* iterate all the users */
for (headptr = (headptr ? headptr : userroot);
headptr;
headptr = headptr -> next)
{
SetPoint (dictfile,
ruleptr -> rule,
pointuser++,
headptr -> passwd.pw_name);
/* iterate all the words */
for (dictptr = dictroot;
dictptr;
dictptr = dictptr -> next)
{
/* skip repeated words... */
if (!TryManyUsers (headptr, dictptr -> word))
{
break;
}
}
}
/* free up memory */
DropDict ();
/* write feedback file */
if (!FeedBack (0))
{
Log ("FeedBack: All Users Are Cracked! Bloody Hell!\n");
return;
}
/* on next pass, start from top of user list */
headptr = (struct USER *) 0;
/* did we REALLY finish this dictionary ? */
if (rval < 0)
{
continue_dict = 1;
goto load_dict;
}
}
}
int
main (argc, argv)
int argc;
char *argv[];
{
int i;
long t;
int uerr;
int die_bool = 0;
FILE *fp;
char *crack_out;
extern int optind;
extern char *optarg;
static char getopt_string[] = "i:fX:n:r:vml:";
uerr = 0;
if (argc == 1)
{
uerr++;
}
while ((i = getopt (argc, argv, getopt_string)) != EOF)
{
switch (i)
{
case 'i':
strcpy (input_file, optarg);
if (!freopen (input_file, "r", stdin))
{
perror (input_file);
exit (1);
}
if (!strncmp (input_file, "/tmp/pw.", 7))
{
unlink (input_file);
}
break;
case 'm':
mail_bool = 1;
break;
case 'f':
foreground_bool = 1;
break;
case 'X':
remote_bool = 1;
strcpy (supplied_name, optarg);
break;
case 'l':
pwlength = atoi (optarg);
break;
case 'n':
nice_value = atoi (optarg);
nice (nice_value);
break;
case 'r':
recover_bool = 1;
strcpy (recover_file, optarg);
break;
case 'v':
verbose_bool = 1;
break;
default:
case '?':
uerr++;
break;
}
}
if (optind >= argc)
{
uerr++;
}
if (uerr)
{
fprintf (stderr,
"Usage:\t%s -%s dictfile [dictfile...]\n",
argv[0],
getopt_string);
exit (1);
}
pid = getpid ();
if (gethostname (this_hostname, STRINGSIZE))
{
perror ("gethostname");
}
if (!(crack_out = (char *) getenv ("CRACK_OUT")))
{
crack_out = ".";
}
sprintf (opfile, "%s/out.%s%d", crack_out, this_hostname, pid);
if (remote_bool)
{
sprintf (diefile, "%s", supplied_name);
} else
{
sprintf (diefile, "%s/D%s%d", runtime, this_hostname, pid);
}
sprintf (pointfile, "%s/P%s%d", runtime, this_hostname, pid);
sprintf (feedbackfile, "%s/F%s%d", runtime, this_hostname, pid);
if (!foreground_bool)
{
if (!freopen (opfile, "w", stdout))
{
perror ("freopen(stdout)");
exit (1);
}
if (!freopen (opfile, "a", stderr))
{
perror ("freopen(stderr)");
exit (1);
}
}
/*
* don't generate a die file unless we are not 'attached' to a
* terminal... except when we are remote as well...
*/
time (&t);
if (!foreground_bool || (foreground_bool && remote_bool))
{
if (!(fp = fopen (diefile, "w")))
{
perror (diefile);
exit (1);
}
die_bool = 1;
fprintf (fp, "#!/bin/sh\n");
fprintf (fp, "# ID=%s.%d start=%s", this_hostname, pid, ctime (&t));
fprintf (fp, "kill -TERM %d && rm $0", pid);
fclose (fp);
chmod (diefile, 0700);
}
Log ("Crack v%s: The Password Cracker, (c) Alec D.E. Muffett, 1992\n",
version);
#ifdef FCRYPT
init_des ();
#endif
/* Quick, verify that we are sane ! */
if (strcmp (crypt ("fredfred", "fredfred"), "frxWbx4IRuBBA"))
{
Log ("Version of crypt() being used internally is not compatible with standard.\n");
Log ("This could be due to byte ordering problems - see the comments in Sources/conf.h\n");
Log ("If there is another reason for this, edit the source to remove this assertion.\n");
Log ("Terminating...\n");
exit (0);
}
#ifndef AMIGA
signal (SIGTERM, CatchTERM);
#endif
Log ("Loading Data, host=%s pid=%d\n", this_hostname, pid);
if (LoadData () <= 0)
{
Log ("Nothing to Crack. Exiting...\n");
exit (0);
}
if (LoadRules (rulefile, &ruleroot) < 0 ||
LoadRules (gecosfile, &gecosroot) < 0)
{
exit (1);
}
if (!recover_bool)
{
/* We are starting afresh ! Ah, the birds in May ! */
Pass1 ();
if (!FeedBack (0))
{
Log ("FeedBack: information: all users are cracked after gecos pass\n");
goto finish_crack;
}
} else
{
int rval;
if (rval = GetPoint (recover_file))
{
Log ("Recovery from file %s not permitted on this host [code %d]\n",
recover_file,
rval);
exit (0);
}
/* Some spodulous creep pulled our plug... */
while ((optind < argc) && strcmp (argv[optind], old_dictname))
{
optind++;
}
}
for (i = optind; i < argc; i++)
{
Pass2 (argv[i]);
}
Log ("Tidying up files...\n");
FeedBack (1);
finish_crack:
if (die_bool)
{
unlink (diefile);
}
unlink (pointfile);
Log ("Done.\n");
return (0);
}