diff -urpN jumbo-unstable1/doc/OPTIONS jumbo-unstable/doc/OPTIONS --- jumbo-unstable1/doc/OPTIONS 2012-04-19 21:34:23.531250000 +0000 +++ jumbo-unstable/doc/OPTIONS 2012-04-30 17:04:39.359375000 +0000 @@ -259,6 +259,36 @@ example is for studying which rules give you can't know for sure which rule produced a successful guess when analyzing the log file. +--regen-lost-salts=N Try to find password AND salt in a set of raw hashes. + +This option will allow certain types (with short salts), to be found, when +they are present in a set of raw hashes, but where we have lost the salt value. +Normally, without the salt, JtR would not be able to do anything with the hash, +and could not find the password. In this mode, JtR will load the hashes, giving +each a 'fake' salt (but all get the same salt). JtR then builds all of the +possible salt values for this format, and associates every hash with each one +of these salts. So, JtR will now run, and find passwords using all possible +salts for each of the hashes, thus recreating the salt (and finding the passwords). +There are only a few types supported. To properly run, you MUST use one of the +proper types in the -format=type command, AND use the -regen-lost-salts=N with +N being set properly. The valid N's are: + --regen-lost-salts=1 --format=PHPS (form md5($p.$s) s is 3 bytes) + --regen-lost-salts=2 --format=OSC (form md5($s.$p) s is 2 bytes) + --regen-lost-salts=3 --format=mediawiki (user id's from 0 to 999) + --regen-lost-salts=4 --format=mediawiki (user id's from 1000 to 9999) + --regen-lost-salts=5 --format=mediawiki (user id's from 10000 to 99999) +For types 3, 4, 5, we only look for numeric user id's, of the type: +md5($u.'-'.md5($p)) Mediawiki made some changes to use longer 8 byte hex strings +as salts. This salt is too long to try to find, so JtR only focuses on the older +type $B$ with the shorter numeric user id's. The found lines in john.pot will +be output as type $dynamic_2$ for the found PHPS (with the salts filled in). It +will be $dynamic_4$ for the OSC (with salt), and $dynamic_9$ (with user id as +salt), for the mediawiki items. +NOTE, normally the salt is preserved with the hash, and thus JtR can properly +find them using 'normal' methods. This new functionality was added to handle +leaked hashes where the original salt has been lost. It is VERY slow to run +in this manner, due to JtR having to check every possible salt, but it does +allow these hashes to be cracked 'properly'. Additional utilities (compiled/installed along with John). diff -urpN jumbo-unstable1/src/cracker.c jumbo-unstable/src/cracker.c --- jumbo-unstable1/src/cracker.c 2012-04-19 21:34:24.437500000 +0000 +++ jumbo-unstable/src/cracker.c 2012-04-28 17:54:46.734375000 +0000 @@ -189,6 +189,32 @@ static int crk_process_guess(struct db_s key = utf8key; } + // Ok, FIX the salt ONLY if -regen-lost-salts=X was used. + if (options.regen_lost_salts) { + if (options.regen_lost_salts == 1) + { + // 3 byte PHPS salt, the hash is in $dynamic_6$ format. + char *cp = pw->source; + char *cp2 = *(char**)(salt->salt); + memcpy(cp+11+32+1, cp2+6, 3); + } + else if (options.regen_lost_salts == 2) + { + // 2 byte osCommerce salt, the hash is in $dynamic_4$ format. + char *cp = pw->source; + char *cp2 = *(char**)(salt->salt); + memcpy(cp+11+32+1, cp2+6, 2); + } + else if (options.regen_lost_salts >= 3 && options.regen_lost_salts <= 5) + { + // Media wiki. Salt len is not known, but 5 bytes or less, and WILL fit into pw-source even after being fixed.. $dynamic_9$ format. + char Buf[256]; + char *cp2 = *(char**)(salt->salt); + extern void mediawiki_fix_salt(char *Buf, char *source_to_fix, char *salt_rec, int max_salt_len); + mediawiki_fix_salt(Buf, pw->source, cp2, options.regen_lost_salts+1); + strcpy(pw->source, Buf); + } + } log_guess(crk_db->options->flags & DB_LOGIN ? replogin : "?", dupe ? NULL : pw->source, repkey, key, crk_db->options->field_sep_char); diff -urpN jumbo-unstable1/src/dynamic.h jumbo-unstable/src/dynamic.h --- jumbo-unstable1/src/dynamic.h 2012-04-19 21:34:24.765625000 +0000 +++ jumbo-unstable/src/dynamic.h 2012-04-19 16:04:36.609375000 +0000 @@ -118,6 +118,10 @@ int dynamic_RESERVED_PRELOAD_SETUP(int c char *dynamic_PRELOAD_SIGNATURE(int cnt); int dynamic_IS_PARSER_VALID(int which); +// This one is called in the .pot writing. We 'fixup' salts which contain ':' chars, or other +// chars which cause problems (like the $ char). +char *dynamic_FIX_SALT_TO_HEX(char *ciphertext); + // Here are the 'parser' functions (i.e. user built stuff in john.conf) int dynamic_LOAD_PARSER_FUNCTIONS(int which, struct fmt_main *pFmt); char *dynamic_LOAD_PARSER_SIGNATURE(int which); diff -urpN jumbo-unstable1/src/dynamic_fmt.c jumbo-unstable/src/dynamic_fmt.c --- jumbo-unstable1/src/dynamic_fmt.c 2012-04-19 21:34:24.765625000 +0000 +++ jumbo-unstable/src/dynamic_fmt.c 2012-04-30 20:38:30.281250000 +0000 @@ -879,6 +879,46 @@ static char *prepare(char *split_fields[ if (strncmp(cpBuilding, "$dynamic_", 9)) return split_fields[1]; + /* at this point, we want to convert ANY and all $HEX$hex into values */ + /* the reason we want to do this, is so that things read from john.pot file will be in proper 'native' format */ + /* the ONE exception to this, is if there is a NULL byte in the $HEX$ string, then we MUST leave that $HEX$ string */ + /* alone, and let the later calls in dynamic.c handle them. */ + if (strstr(cpBuilding, "$HEX$")) { + char *ct = mem_alloc_tiny(strlen(cpBuilding)+1, MEM_ALIGN_NONE); + char *cp, *cpo; + int bGood=1; + + strcpy(ct, cpBuilding); + cp = strstr(ct, "$HEX$"); + cpo = cp; + *cpo++ = *cp; + cp += 5; + while (*cp && bGood) { + if (*cp == '0' && cp[1] == '0') { + bGood = 0; + break; + } + if (atoi16[ARCH_INDEX(*cp)] != 0x7f && atoi16[ARCH_INDEX(cp[1])] != 0x7f) { + *cpo++ = atoi16[ARCH_INDEX(*cp)]*16 + atoi16[ARCH_INDEX(cp[1])]; + *cpo = 0; + cp += 2; + } else if (*cp == '$') { + while (*cp && strncmp(cp, "$HEX$", 5)) { + *cpo++ = *cp++; + } + *cpo = 0; + if (!strncmp(cp, "$HEX$", 5)) { + *cpo++ = *cp; + cp += 5; + } + } else { + return split_fields[1]; + } + } + if (bGood) + cpBuilding = ct; + } + if (curdat.nUserName && !strstr(cpBuilding, "$$U")) { char *userName=split_fields[0], *cp; // assume field[0] is in format: username OR DOMAIN\\username If we find a \\, then use the username 'following' it. @@ -1623,11 +1663,11 @@ typedef struct { typedef struct { dyna_salt_list_main List; } SaltHashTab_t; -static SaltHashTab_t *SaltHashTab; -static dyna_salt_list_entry *pSaltHashData, *pSaltHashDataNext; -static int dyna_salt_list_count; -static unsigned char *pSaltDataBuf, *pNextSaltDataBuf; -static int nSaltDataBuf; +static SaltHashTab_t *SaltHashTab=NULL; +static dyna_salt_list_entry *pSaltHashData=NULL, *pSaltHashDataNext=NULL; +static int dyna_salt_list_count=0; +static unsigned char *pSaltDataBuf=NULL, *pNextSaltDataBuf=NULL; +static int nSaltDataBuf=0; static unsigned char *AddSaltHash(unsigned char *salt, unsigned len, unsigned int idx) { unsigned char *pRet; diff -urpN jumbo-unstable1/src/dynamic_utils.c jumbo-unstable/src/dynamic_utils.c --- jumbo-unstable1/src/dynamic_utils.c 2012-04-19 21:34:24.781250000 +0000 +++ jumbo-unstable/src/dynamic_utils.c 2012-04-19 19:59:21.218750000 +0000 @@ -134,3 +134,36 @@ char *dynamic_Demangle(char *Line, int * if (Len) *Len = cp-tmp; return tmp; } + +// This one is called in the .pot writing. We 'fixup' salts which contain ':' chars, or other +// chars which cause problems (like the $ char). +char *dynamic_FIX_SALT_TO_HEX(char *ciphertext) { + char *cp; + if (strncmp(ciphertext, "$dynamic_", 9)) + return ciphertext; // not a dynamic format, so we can not 'fix' it. + // ok, see if this is salted: + cp = strchr(&ciphertext[10+16], '$'); + if (!cp) + return ciphertext; // not a salted format. + + // we will HAVE to get this much more functional. But for now, we simply convert + // anything where we find a ':' or '$' or one of the line feed chars, in the salt, + // into a HEX string. Otherwise the .pot file output can easily be 'broken'. + // it would be nice to also handle 'null' bytes (since some salts CAN have them), however + // since we are a C program, we already have problems with them. With recent changes + // in john, john CAN find them, but I do not think it can properly store them to pot + // file, unless the $HEX$ part is maintained (it may be maintained, I have to test that). + + ++cp; + if ( strchr(cp, ':') || strchr(cp, '$') || strchr(cp, '\n') || strchr(cp, '\r') ) { + // ok, we are going to convert to a 'HEX' The length is length of ciphertext, the null, the HEX$ and 2 bytes per char of salt string. + char *cpx, *cpNew = mem_alloc_tiny(strlen(ciphertext) + 1 + 4 + strlen(cp), MEM_ALIGN_NONE); + cpx = cpNew; + // put the hash, including first '$' into the ouput string, AND the starting HEX$ + cpx += sprintf(cpNew, "%*.*sHEX$", cp-ciphertext, cp-ciphertext, ciphertext); + while (*cp) + cpx += sprintf(cpx, "%x", *cp++); + return cpNew; + } + return ciphertext; +} diff -urpN jumbo-unstable1/src/fake_salts.c jumbo-unstable/src/fake_salts.c --- jumbo-unstable1/src/fake_salts.c 1970-01-01 00:00:00.000000000 +0000 +++ jumbo-unstable/src/fake_salts.c 2012-04-30 17:43:30.984375000 +0000 @@ -0,0 +1,161 @@ +/* + * This software was written by Jim Fougeron jfoug AT cox dot net + * in 2012. No copyright is claimed, and the software is hereby + * placed in the public domain. In case this attempt to disclaim + * copyright and place the software in the public domain is deemed + * null and void, then the software is Copyright © 2012 Jim Fougeron + * and it is hereby released to the general public under the following + * terms: + * + * This software may be modified, redistributed, and used for any + * purpose, in source and binary forms, with or without modification. + * + * Salt finder. This will allow JtR to process a few salted type + * hashes, if the original salt has been lost. The only 2 types + * done at this time, are PHPS (VB), and osCommerce. PHPS is dynamic_6 + * which is md5(md5($p).$s) with a 3 byte salt. osCommerce is dynamic_4 + * of md5($s.$p) with a 2 type salt. + * + */ + + +#include +#include "memory.h" +#include "options.h" + +void build_fake_salts_for_regen_lost(struct db_salt *salts) { + if (options.regen_lost_salts == 1) // this is for PHPS, with a raw 32 byte hash input file (i.e. missing the salts) + { + static struct db_salt *sp, fake_salts[('~'-' '+1)*('~'-' '+1)*('~'-' '+1)+1]; + int i, j, k, idx=0; + char *buf, *cp; + unsigned long *ptr; + // Find the 'real' salt. We loaded ALL of the file into 1 salt. + // we then take THAT salt record, and build a list pointing to these fake salts, + // AND build 'proper' dynamic salts for all of our data. + sp = salts; + while (sp->next) { + sp = sp->next; + } + + // a dynamic salt is 030000[3-byte-salt] for ALL of the salts. + buf = mem_alloc_tiny(('~'-' '+1)*('~'-' '+1)*('~'-' '+1)*9+1, MEM_ALIGN_NONE); + cp = buf; + for (i = ' '; i <= '~'; ++i) { + for (j = ' '; j <= '~'; ++j) { + for (k = ' '; k <= '~'; ++k) { + sprintf(cp, "030000%c%c%c", i, j, k); + sp->next = &fake_salts[idx]; + fake_salts[idx].next = NULL; + fake_salts[idx].count = sp->count; + fake_salts[idx].hash = sp->hash; + fake_salts[idx].hash_size = sp->hash_size; + fake_salts[idx].index = sp->index; + fake_salts[idx].keys = sp->keys; + fake_salts[idx].list = sp->list; + ptr=mem_alloc_tiny(sizeof(char*), MEM_ALIGN_WORD); + *ptr = (unsigned long) (buf + (cp-buf)); + fake_salts[idx].salt = ptr; + ++idx; + cp += 9; + sp = sp->next; + } + } + } + } + else if (options.regen_lost_salts == 2) // this is for osCommerce, with a raw 32 byte hash input file (i.e. missing the salts) + { + static struct db_salt *sp, fake_salts[('~'-' '+1)*('~'-' '+1)+1]; + int i, j, idx=0; + char *buf, *cp; + unsigned long *ptr; + // Find the 'real' salt. We loaded ALL of the file into 1 salt. + // we then take THAT salt record, and build a list pointing to these fake salts, + // AND build 'proper' dynamic salts for all of our data. + sp = salts; + while (sp->next) { + sp = sp->next; + } + + // a dynamic salt is 020000[2-byte-salt] for ALL of the salts. + buf = mem_alloc_tiny(('~'-' '+1)*('~'-' '+1)*8+1, MEM_ALIGN_NONE); + cp = buf; + for (i = ' '; i <= '~'; ++i) { + for (j = ' '; j <= '~'; ++j) { + sprintf(cp, "020000%c%c", i, j); + sp->next = &fake_salts[idx]; + fake_salts[idx].next = NULL; + fake_salts[idx].count = sp->count; + fake_salts[idx].hash = sp->hash; + fake_salts[idx].hash_size = sp->hash_size; + fake_salts[idx].index = sp->index; + fake_salts[idx].keys = sp->keys; + fake_salts[idx].list = sp->list; + ptr=mem_alloc_tiny(sizeof(char*), MEM_ALIGN_WORD); + *ptr = (unsigned long) (buf + (cp-buf)); + fake_salts[idx].salt = ptr; + ++idx; + cp += 8; + sp = sp->next; + } + } + } + else if (options.regen_lost_salts >= 3 && options.regen_lost_salts <= 5) // this is for media-wiki, with a raw 32 byte hash input file (i.e. missing the salts) + { + // 3 gets salts from 0- to 999- 4 salts from 1000- to 9999- 5 salts from 10000- to 99999- + static struct db_salt *sp, *fake_salts; + int i, idx=0; + char *buf, *cp; + unsigned long *ptr; + int max, min; + // Find the 'real' salt. We loaded ALL of the file into 1 salt. + // we then take THAT salt record, and build a list pointing to these fake salts, + // AND build 'proper' dynamic salts for all of our data. + sp = salts; + while (sp->next) { + sp = sp->next; + } + + max = 1000; + min = 0; + switch(options.regen_lost_salts) { + case 4: + max = 10000; + min = 1000; + break; + case 5: + max = 100000; + min = 10000; + break; + } + + fake_salts = mem_calloc_tiny( ((max-min)+1) * sizeof(struct db_salt), MEM_ALIGN_WORD); + + // for type 3, we do not use 100% of this buffer, but we do use 'most' of it. + buf = mem_alloc_tiny(((max-min)+1)*(7+options.regen_lost_salts), MEM_ALIGN_NONE); + cp = buf; + for (i = min; i < max; ++i) { + int l; + char *cp2 = cp; + if (i > 9999) l = 6; + else if (i > 999) l = 5; + else if (i > 99) l = 4; + else if (i > 9) l = 3; + else l = 2; + cp += sprintf(cp, "0%d0000%d-", l, i); + sp->next = &fake_salts[idx]; + fake_salts[idx].next = NULL; + fake_salts[idx].count = sp->count; + fake_salts[idx].hash = sp->hash; + fake_salts[idx].hash_size = sp->hash_size; + fake_salts[idx].index = sp->index; + fake_salts[idx].keys = sp->keys; + fake_salts[idx].list = sp->list; + ptr=mem_alloc_tiny(sizeof(char*), MEM_ALIGN_WORD); + *ptr = (unsigned long) (buf + (cp2-buf)); + fake_salts[idx].salt = ptr; + ++idx; + sp = sp->next; + } + } +} diff -urpN jumbo-unstable1/src/john.c jumbo-unstable/src/john.c --- jumbo-unstable1/src/john.c 2012-04-19 21:34:25.015625000 +0000 +++ jumbo-unstable/src/john.c 2012-04-20 19:20:12.343750000 +0000 @@ -233,7 +233,9 @@ static void john_register_all(void) john_register_one(&fmt_ssh); john_register_one(&fmt_pdf); +#ifndef _MSC_VER john_register_one(&rar_fmt); +#endif john_register_one(&zip_fmt); john_register_one(&fmt_dummy); @@ -465,6 +467,11 @@ static void john_load(void) printf("Remaining %s\n", john_loaded_counts()); } + if (options.regen_lost_salts) { + extern void build_fake_salts_for_regen_lost(struct db_salt *); + build_fake_salts_for_regen_lost(database.salts); + } + if ((options.flags & FLG_PWD_REQ) && !database.salts) exit(0); } } diff -urpN jumbo-unstable1/src/loader.c jumbo-unstable/src/loader.c --- jumbo-unstable1/src/loader.c 2012-04-19 21:34:25.078125000 +0000 +++ jumbo-unstable/src/loader.c 2012-04-28 17:48:33.718750000 +0000 @@ -616,6 +616,22 @@ static void ldr_load_pot_line(struct db_ if ((current = db->password_hash[hash])) do { + if (db->options->regen_lost_salts) { + if (db->options->regen_lost_salts == 1 && !strncmp(current->source, "$dynamic_6$", 11)) { + char *cp = current->source; + memcpy(&(cp[44]), &(ciphertext[44]), 3); + } else if (db->options->regen_lost_salts == 2 && !strncmp(current->source, "$dynamic_4$", 11)) { + char *cp = current->source; + memcpy(&(cp[44]), &(ciphertext[44]), 2); + } + else if (db->options->regen_lost_salts >= 3 && db->options->regen_lost_salts <= 5 && !strncmp(current->source, "$dynamic_9$", 11)) { + char Buf[256]; + extern void mediawiki_fix_salt(char *Buf, char *source_to_fix, char *salt_rec, int max_salt_len); + mediawiki_fix_salt(Buf, current->source, &(ciphertext[44-6]), db->options->regen_lost_salts+1); + strcpy(current->source, Buf); + } + //else if (db->options->regen_lost_salts == 6 && !strncmp(current->source, "???????????", 11)) + } if (current->binary && !memcmp(current->binary, binary, format->params.binary_size) && !strcmp(current->source, ciphertext)) diff -urpN jumbo-unstable1/src/loader.h jumbo-unstable/src/loader.h --- jumbo-unstable1/src/loader.h 2012-04-19 21:34:25.093750000 +0000 +++ jumbo-unstable/src/loader.h 2012-04-19 15:32:47.468750000 +0000 @@ -183,6 +183,13 @@ struct db_options { /* by default will be ':', but -field-separator-char=c can over ride the default */ char field_sep_char; + +/* This is a 'special' flag. It causes john to add 'extra' code to search for some salted types, when we have */ +/* only the hashes. The only type supported is PHPS (at this time.). So PHPS will set this to a 1. OTherwise */ +/* it will always be zero. LIKELY we will add the same type logic for the OSC (mscommerse) type, which has only */ +/* a 2 byte salt. That will set this field to be a 2. If we add other types, then we will have other values */ +/* which can be assigned to this variable. This var is set by the undocummented --regen_lost_salts=# */ + int regen_lost_salts; }; /* diff -urpN jumbo-unstable1/src/logger.c jumbo-unstable/src/logger.c --- jumbo-unstable1/src/logger.c 2012-04-19 21:34:25.093750000 +0000 +++ jumbo-unstable/src/logger.c 2012-04-19 16:04:06.250000000 +0000 @@ -38,6 +38,7 @@ #include "config.h" #include "options.h" #include "unicode.h" +#include "dynamic.h" #ifdef HAVE_MPI #include "john-mpi.h" #endif @@ -201,11 +202,14 @@ void log_guess(char *login, char *cipher in_logger = 1; - if (pot.fd >= 0 && ciphertext && - strlen(ciphertext) + strlen(store_plain) <= LINE_BUFFER_SIZE - 3) { - count1 = (int)sprintf(pot.ptr, - "%s%c%s\n", ciphertext, field_sep, store_plain); - if (count1 > 0) pot.ptr += count1; + if (pot.fd >= 0 && ciphertext ) { + if (!strncmp(ciphertext, "$dynamic_", 9)) + ciphertext = dynamic_FIX_SALT_TO_HEX(ciphertext); + if (strlen(ciphertext) + strlen(store_plain) <= LINE_BUFFER_SIZE - 3) { + count1 = (int)sprintf(pot.ptr, + "%s%c%s\n", ciphertext, field_sep, store_plain); + if (count1 > 0) pot.ptr += count1; + } } if (log.fd >= 0 && diff -urpN jumbo-unstable1/src/Makefile jumbo-unstable/src/Makefile --- jumbo-unstable1/src/Makefile 2012-04-19 21:34:24.093750000 +0000 +++ jumbo-unstable/src/Makefile 2012-04-20 19:19:53.343750000 +0000 @@ -117,6 +117,7 @@ JOHN_OBJS = \ loader.o logger.o math.o memory.o misc.o options.o params.o path.o \ recovery.o rpp.o rules.o signals.o single.o status.o tty.o wordlist.o \ mkv.o mkvlib.o \ + fake_salts.o \ unicode.o \ unshadow.o \ unafs.o \ diff -urpN jumbo-unstable1/src/mediawiki_fmt_plug.c jumbo-unstable/src/mediawiki_fmt_plug.c --- jumbo-unstable1/src/mediawiki_fmt_plug.c 2012-04-19 21:34:25.203125000 +0000 +++ jumbo-unstable/src/mediawiki_fmt_plug.c 2012-04-30 20:12:19.296875000 +0000 @@ -45,6 +45,7 @@ userName2:$B$107$dd494cb03ac1c5b8f8d2ddd #include "common.h" #include "formats.h" #include "dynamic.h" +#include "options.h" #define FORMAT_LABEL "mediawiki" #define FORMAT_NAME "MediaWiki -- md5($s.'-'.md5($p))" @@ -79,6 +80,27 @@ static char Conv_Buf[80]; static struct fmt_main *pFmt_Dynamic_9; static void mediawiki_init(struct fmt_main *pFmt); +/* this utility function is used by cracker.c AND loader.c. Since media-wiki has a variable width salt, of which + in regen_lost_salts mode, we only handle 0 to 99999 as salts, we built a function that will assign the salt from + one buffer into another */ +void mediawiki_fix_salt(char *Buf, char *source_to_fix, char *salt_rec, int max_salt_len) { + char *cp = source_to_fix; + char *cp2 = salt_rec; + int i = 0; + + strncpy(Buf, cp, 11+32+1); + Buf += (11+32+1); + cp += (11+32+1); + cp2 += 6; + while (++i < max_salt_len && *cp2 != '-') { + *Buf++ = *cp2++; + ++cp; + } + ++cp; + *Buf++ = *cp2++; + *Buf = 0; +} + /* this function converts a 'native' mediawiki signature string into a Dynamic_9 syntax string */ static char *Convert(char *Buf, char *ciphertext) { @@ -106,6 +128,24 @@ static char *our_split(char *ciphertext, return Convert(Conv_Buf, ciphertext); } +static char *our_prepare(char *split_fields[10], struct fmt_main *pFmt) +{ + int i = strlen(split_fields[1]); + if (!pFmt_Dynamic_9) + mediawiki_init(pFmt); + /* this 'special' code added to do a 'DEEP' test of hashes which have lost their salts */ + /* in this type run, we load the passwords, then run EVERY salt against them, as though*/ + /* all of the hashes were available for ALL salts. We also only want 1 salt */ + if ( (options.regen_lost_salts >= 3 && options.regen_lost_salts <= 5) && i == 32) { + char *Ex = mem_alloc_tiny((3+options.regen_lost_salts+1+MD5_HEX_SIZE)+1, MEM_ALIGN_NONE); + // add a 'garbage' placeholder salt that is the proper 'max' size for salt. NOTE + // the real saltlen is not known at this time. We are simply making sure there is ENOUGH room. + sprintf(Ex, "$B$000%s%s$%s", options.regen_lost_salts>3?"0":"", options.regen_lost_salts>4?"0":"", split_fields[1]); + return Ex; + } + return pFmt_Dynamic_9->methods.prepare(split_fields, pFmt); +} + static int mediawiki_valid(char *ciphertext, struct fmt_main *pFmt) { int i; @@ -115,6 +155,19 @@ static int mediawiki_valid(char *ciphert return 0; if (!pFmt_Dynamic_9) mediawiki_init(pFmt); + + i = strlen(ciphertext); + /* this 'special' code added to do a 'DEEP' test of hashes which have lost their salts */ + /* in this type run, we load the passwords, then run EVERY salt against them, as though*/ + /* all of the hashes were available for ALL salts. We also only want 1 salt */ + + if ( (options.regen_lost_salts >= 3 && options.regen_lost_salts <= 5) && i == 32) { + static char Ex[(1+1+1+5+1+MD5_HEX_SIZE)+1]; + sprintf(Ex, "$B$000%s%s$%s", options.regen_lost_salts>3?"0":"", options.regen_lost_salts>4?"0":"", ciphertext); + ciphertext = Ex; + i = strlen(ciphertext); + } + if (strncmp(ciphertext, "$B$", 3) != 0) { return pFmt_Dynamic_9->methods.valid(ciphertext, pFmt_Dynamic_9); } @@ -153,7 +206,7 @@ struct fmt_main fmt_mediawiki = /* All we setup here, is the pointer to valid, and the pointer to init */ /* within the call to init, we will properly set this full object */ mediawiki_init, - fmt_default_prepare, + our_prepare, mediawiki_valid } }; @@ -166,6 +219,7 @@ static void mediawiki_init(struct fmt_ma fmt_mediawiki.methods.salt = our_salt; fmt_mediawiki.methods.binary = our_binary; fmt_mediawiki.methods.split = our_split; + fmt_mediawiki.methods.prepare = our_prepare; fmt_mediawiki.params.algorithm_name = pFmt_Dynamic_9->params.algorithm_name; pFmt->private.initialized = 1; } diff -urpN jumbo-unstable1/src/options.c jumbo-unstable/src/options.c --- jumbo-unstable1/src/options.c 2012-04-19 21:34:25.687500000 +0000 +++ jumbo-unstable/src/options.c 2012-04-30 20:09:48.750000000 +0000 @@ -132,6 +132,8 @@ static struct opt_entry opt_list[] = { "%u", &options.mkpc}, {"max-run-time", FLG_NONE, FLG_NONE, 0, OPT_REQ_PARAM, "%u", &options.max_run_time}, + {"regen-lost-salts", FLG_NONE, FLG_NONE, 0, OPT_REQ_PARAM, + "%u", &options.regen_lost_salts}, #ifdef CL_VERSION_1_0 {"platform", FLG_NONE, FLG_NONE, 0, OPT_REQ_PARAM, OPT_FMT_STR_ALLOC, &options.ocl_platform}, @@ -196,7 +198,8 @@ static struct opt_entry opt_list[] = { "--fix-state-delay=N performance tweak, see documentation\n" \ "--nolog disables creation and writing to john.log file\n" \ "--crack-status emit a status line whenever a password is cracked\n" \ -"--max-run-time=N gracefully exit after this many seconds\n" +"--max-run-time=N gracefully exit after this many seconds\n" \ +"--regen-lost-salts=N regenerate lost salts for some hashes (see OPTIONS)\n" #define JOHN_USAGE_PLUGIN \ "--plugin=NAME[,..] load this (these) dynamic plugin(s)\n" @@ -284,6 +287,7 @@ void opt_init(char *name, int argc, char memset(&options, 0, sizeof(options)); options.loader.field_sep_char = options.field_sep_char = ':'; + options.loader.regen_lost_salts = options.regen_lost_salts = 0; options.loader.max_fix_state_delay = 0; options.loader.max_wordfile_memory = 5000000; options.mkpc = 0; @@ -505,6 +509,8 @@ void opt_init(char *name, int argc, char if (options.loader.activesinglerules == NULL) options.loader.activesinglerules = str_alloc_copy(SUBSECTION_SINGLE); + options.loader.regen_lost_salts = options.regen_lost_salts; + if (field_sep_char_string != NULL) { if (!strcasecmp(field_sep_char_string, "tab")) // Literal tab or TAB will mean 0x09 tab character diff -urpN jumbo-unstable1/src/options.h jumbo-unstable/src/options.h --- jumbo-unstable1/src/options.h 2012-04-19 21:34:25.718750000 +0000 +++ jumbo-unstable/src/options.h 2012-04-19 15:24:37.578125000 +0000 @@ -155,6 +155,13 @@ struct options_main { char *salt_param; char field_sep_char; +/* This is a 'special' flag. It causes john to add 'extra' code to search for some salted types, when we have */ +/* only the hashes. The only type supported is PHPS (at this time.). So PHPS will set this to a 1. OTherwise */ +/* it will always be zero. LIKELY we will add the same type logic for the OSC (mscommerse) type, which has only */ +/* a 2 byte salt. That will set this field to be a 2. If we add other types, then we will have other values */ +/* which can be assigned to this variable. This var is set by the undocummented --regen_lost_salts=# */ + int regen_lost_salts; + /* wordfile character encoding 'stuff' */ /* The canonical name of chosen encoding. User might have said 'koi8r' but this string will be 'KOI8-R'. An empty string means default/old-style */ diff -urpN jumbo-unstable1/src/osc_fmt_plug.c jumbo-unstable/src/osc_fmt_plug.c --- jumbo-unstable1/src/osc_fmt_plug.c 1970-01-01 00:00:00.000000000 +0000 +++ jumbo-unstable/src/osc_fmt_plug.c 2012-04-30 20:11:37.859375000 +0000 @@ -0,0 +1,204 @@ +/* + * osc_fmt_plug.c + * + * Salted md5, as seen in osCommerce. This is $dynamic_4$ format, with a 2 + * byte salt. The format is: md5($s.$p). + * + * By JimF, 2012. The reason for this format, is so that I can add + * a method to find these when we ONLY have the hash, and not the salt. + * In that mode (-regen-lost-salts=2) JtR will load the flat hash data + * (without the salts), and then will create a salt record for ALL valid + * salts from ' ' to '~~', and point ALL of the hashes to these salts. + * Then john will test all hashes, using ALL salts, thus will find all + * md5($s.$p) where the $s is a 2 byte salt. NOTE there are 95^2 salts + * (9025 salts). + * + * NOTE This file was taken from the PHPS_fmt_plug.c (which is also a + * 'thin' dynamic format). That format already had hooks to do the salt + * loading. We have 3 byte salts there, but it works the same (just + * 95x slower due to the extra salt byte). + * + */ + +#include + +#include "common.h" +#include "formats.h" +#include "dynamic.h" +#include "options.h" + +#define FORMAT_LABEL "osc" +#define FORMAT_NAME "OSC -- md5($salt.$pass)" + +#define ALGORITHM_NAME "?" /* filled in by md5-gen */ +#define BENCHMARK_COMMENT "" +#define BENCHMARK_LENGTH 0 + +#define MD5_BINARY_SIZE 16 +#define MD5_HEX_SIZE (MD5_BINARY_SIZE * 2) + +#define BINARY_SIZE MD5_BINARY_SIZE + +#define SALT_SIZE 2 +#define PROCESSED_SALT_SIZE SALT_SIZE + +#define PLAINTEXT_LENGTH 32 +#define CIPHERTEXT_LENGTH (1 + 3 + 1 + SALT_SIZE * 2 + 1 + MD5_HEX_SIZE) + +#define MIN_KEYS_PER_CRYPT 1 +#define MAX_KEYS_PER_CRYPT 1 + +static struct fmt_tests osc_tests[] = { + {"$OSC$2020$05de5c963ee6234dc7d52f7589a1922b", "welcome"}, + {NULL} +}; + +extern struct options_main options; + +static char Conv_Buf[80]; +static struct fmt_main *pFmt_Dynamic_4; +static void osc_init(struct fmt_main *pFmt); + +/* this function converts a 'native' phps signature string into a $dynamic_6$ syntax string */ +static char *Convert(char *Buf, char *ciphertext) +{ + unsigned long val, i; + char *cp; + + if (text_in_dynamic_format_already(pFmt_Dynamic_4, ciphertext)) + return ciphertext; + + cp = strchr(&ciphertext[7], '$'); + if (!cp) + return "*"; + + sprintf(Buf, "$dynamic_4$%s$", &cp[1]); + for (i = 0; i < SALT_SIZE; ++i) + { + char bTmp[3]; + bTmp[0] = ciphertext[5+i*2]; + bTmp[1] = ciphertext[5+i*2+1]; + bTmp[2] = 0; + val = strtoul(bTmp, 0, 16); + sprintf(bTmp, "%c", (unsigned char)val); + strcat(Buf, bTmp); + } + return Buf; +} + +static char *our_split(char *ciphertext, int index) +{ + return Convert(Conv_Buf, ciphertext); +} + +static char *our_prepare(char *split_fields[10], struct fmt_main *pFmt) +{ + int i = strlen(split_fields[1]); + if (!pFmt_Dynamic_4) + osc_init(pFmt); + /* this 'special' code added to do a 'DEEP' test of hashes which have lost their salts */ + /* in this type run, we load the passwords, then run EVERY salt against them, as though*/ + /* all of the hashes were available for ALL salts. We also only want 1 salt */ + if (options.regen_lost_salts == 2 && i == 32) { + char *Ex = mem_alloc_tiny(CIPHERTEXT_LENGTH+1, MEM_ALIGN_NONE); + // add a 'garbage' placeholder salt to this candidate. However, we want ALL of them to + // be setup as the exact same salt (so that all candidate get dumped into one salt block. + // We use ' ' as the salt (3 spaces). + sprintf(Ex, "$OSC$2020$%s", split_fields[1]); + return Ex; + } + return pFmt_Dynamic_4->methods.prepare(split_fields, pFmt); +} + +static int osc_valid(char *ciphertext, struct fmt_main *pFmt) +{ + int i; + if (!ciphertext ) // || strlen(ciphertext) < CIPHERTEXT_LENGTH) + return 0; + + if (!pFmt_Dynamic_4) + osc_init(pFmt); + + i = strlen(ciphertext); + /* this 'special' code added to do a 'DEEP' test of hashes which have lost their salts */ + /* in this type run, we load the passwords, then run EVERY salt against them, as though*/ + /* all of the hashes were available for ALL salts. We also only want 1 salt */ + if (options.regen_lost_salts == 2 && i == 32) { + static char Ex[CIPHERTEXT_LENGTH+1]; + sprintf(Ex, "$OSC$2020$%s", ciphertext); + ciphertext = Ex; + i = CIPHERTEXT_LENGTH; + } + + if (i != CIPHERTEXT_LENGTH) { + return pFmt_Dynamic_4->methods.valid(ciphertext, pFmt_Dynamic_4); + } + + if (strncmp(ciphertext, "$OSC$", 5) != 0) + return 0; + + if (ciphertext[9] != '$') + return 0; + + for (i = 0;i < SALT_SIZE*2; ++i) + if (atoi16[ARCH_INDEX(ciphertext[i+5])] == 0x7F) + return 0; + + for (i = 0;i < MD5_HEX_SIZE; ++i) + if (atoi16[ARCH_INDEX(ciphertext[i+5+1+SALT_SIZE*2])] == 0x7F) + return 0; + + if (!pFmt_Dynamic_4) + osc_init(pFmt); + return pFmt_Dynamic_4->methods.valid(Convert(Conv_Buf, ciphertext), pFmt_Dynamic_4); +} + + +static void * our_salt(char *ciphertext) +{ + return pFmt_Dynamic_4->methods.salt(Convert(Conv_Buf, ciphertext)); +} +static void * our_binary(char *ciphertext) +{ + return pFmt_Dynamic_4->methods.binary(Convert(Conv_Buf, ciphertext)); +} + +struct fmt_main fmt_OSC = +{ + { + // setup the labeling and stuff. NOTE the max and min crypts are set to 1 + // here, but will be reset within our init() function. + FORMAT_LABEL, FORMAT_NAME, ALGORITHM_NAME, BENCHMARK_COMMENT, BENCHMARK_LENGTH, + PLAINTEXT_LENGTH, BINARY_SIZE, SALT_SIZE+1, 1, 1, FMT_CASE | FMT_8_BIT, osc_tests + }, + { + /* All we setup here, is the pointer to valid, and the pointer to init */ + /* within the call to init, we will properly set this full object */ + osc_init, + fmt_default_prepare, + osc_valid + } +}; + + +static void osc_init(struct fmt_main *pFmt) +{ + if (pFmt->private.initialized == 0) { + pFmt_Dynamic_4 = dynamic_THIN_FORMAT_LINK(&fmt_OSC, Convert(Conv_Buf, osc_tests[0].ciphertext), "osc"); + fmt_OSC.methods.salt = our_salt; + fmt_OSC.methods.binary = our_binary; + fmt_OSC.methods.split = our_split; + fmt_OSC.methods.prepare = our_prepare; + fmt_OSC.params.algorithm_name = pFmt_Dynamic_4->params.algorithm_name; + pFmt->private.initialized = 1; + } +} + +/** + * GNU Emacs settings: K&R with 1 tab indent. + * Local Variables: + * c-file-style: "k&r" + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ Files jumbo-unstable1/src/osc_fmt_plug.o and jumbo-unstable/src/osc_fmt_plug.o differ diff -urpN jumbo-unstable1/src/params.h jumbo-unstable/src/params.h --- jumbo-unstable1/src/params.h 2012-04-19 21:34:25.765625000 +0000 +++ jumbo-unstable/src/params.h 2012-04-25 18:30:36.765625000 +0000 @@ -242,9 +242,9 @@ extern int password_hash_thresholds[PASS /* * Hash and buffer sizes for unique. */ -#define UNIQUE_HASH_LOG 21 +#define UNIQUE_HASH_LOG 26 #define UNIQUE_HASH_SIZE (1 << UNIQUE_HASH_LOG) -#define UNIQUE_BUFFER_SIZE 0x8000000 +#define UNIQUE_BUFFER_SIZE 0x30000000 /* * Maximum number of GECOS words per password to load. diff -urpN jumbo-unstable1/src/PHPS_fmt_plug.c jumbo-unstable/src/PHPS_fmt_plug.c --- jumbo-unstable1/src/PHPS_fmt_plug.c 2012-04-19 21:34:24.156250000 +0000 +++ jumbo-unstable/src/PHPS_fmt_plug.c 2012-04-30 20:11:34.156250000 +0000 @@ -35,6 +35,7 @@ #include "common.h" #include "formats.h" #include "dynamic.h" +#include "options.h" #define FORMAT_LABEL "phps" #define FORMAT_NAME "PHPS -- md5(md5($pass).$salt)" @@ -62,6 +63,8 @@ static struct fmt_tests phps_tests[] = { {NULL} }; +extern struct options_main options; + static char Conv_Buf[80]; static struct fmt_main *pFmt_Dynamic_6; static void phps_init(struct fmt_main *pFmt); @@ -80,7 +83,7 @@ static char *Convert(char *Buf, char *ci return "*"; sprintf(Buf, "$dynamic_6$%s$", &cp[1]); - for (i = 0; i < 3; ++i) + for (i = 0; i < SALT_SIZE; ++i) { char bTmp[3]; bTmp[0] = ciphertext[6+i*2]; @@ -98,16 +101,46 @@ static char *our_split(char *ciphertext, return Convert(Conv_Buf, ciphertext); } +static char *our_prepare(char *split_fields[10], struct fmt_main *pFmt) +{ + int i = strlen(split_fields[1]); + if (!pFmt_Dynamic_6) + phps_init(pFmt); + /* this 'special' code added to do a 'DEEP' test of hashes which have lost their salts */ + /* in this type run, we load the passwords, then run EVERY salt against them, as though*/ + /* all of the hashes were available for ALL salts. We also only want 1 salt */ + if (options.regen_lost_salts == 1 && i == 32) { + char *Ex = mem_alloc_tiny(CIPHERTEXT_LENGTH+1, MEM_ALIGN_NONE); + // add a 'garbage' placeholder salt to this candidate. However, we want ALL of them to + // be setup as the exact same salt (so that all candidate get dumped into one salt block. + // We use ' ' as the salt (3 spaces). + sprintf(Ex, "$PHPS$202020$%s", split_fields[1]); + return Ex; + } + return pFmt_Dynamic_6->methods.prepare(split_fields, pFmt); +} + static int phps_valid(char *ciphertext, struct fmt_main *pFmt) { int i; - if (!ciphertext || strlen(ciphertext) < CIPHERTEXT_LENGTH) + if (!ciphertext ) // || strlen(ciphertext) < CIPHERTEXT_LENGTH) return 0; if (!pFmt_Dynamic_6) phps_init(pFmt); - if (strlen(ciphertext) != CIPHERTEXT_LENGTH) { + i = strlen(ciphertext); + /* this 'special' code added to do a 'DEEP' test of hashes which have lost their salts */ + /* in this type run, we load the passwords, then run EVERY salt against them, as though*/ + /* all of the hashes were available for ALL salts. We also only want 1 salt */ + if (options.regen_lost_salts == 1 && i == 32) { + static char Ex[CIPHERTEXT_LENGTH+1]; + sprintf(Ex, "$PHPS$202020$%s", ciphertext); + ciphertext = Ex; + i = CIPHERTEXT_LENGTH; + } + + if (i != CIPHERTEXT_LENGTH) { return pFmt_Dynamic_6->methods.valid(ciphertext, pFmt_Dynamic_6); } @@ -165,6 +198,7 @@ static void phps_init(struct fmt_main *p fmt_PHPS.methods.salt = our_salt; fmt_PHPS.methods.binary = our_binary; fmt_PHPS.methods.split = our_split; + fmt_PHPS.methods.prepare = our_prepare; fmt_PHPS.params.algorithm_name = pFmt_Dynamic_6->params.algorithm_name; pFmt->private.initialized = 1; }