|
Message-ID: <018501c9f425$4be5d2d0$e3b17870$@net> Date: Tue, 23 Jun 2009 12:08:57 -0500 From: "Jim" <jfoug@....net> To: <john-users@...ts.openwall.com> Subject: patch for new john format: phpass (also works for phpBBv3) Here is a patch designed for john.1.7.3.1 with jumbo 5 installed. Requires md5-mmx.S if building for MMX or SSE2. Hope this patch gets through without too much line wrapping issues. The patch for options.c will have wrapping issues if any do, since it is very long line. For admins running phpBB who do not know, the hashes are stored in the phpbb3_users table. The hash is slightly modified from the portable phpass format. The difference is only in the first 3 bytes (the signature). phpass hash uses $H$ while phpBB uses $P$. Other than that, they are identical, and encrypt/decrypt the same. This john format works with both native phpass or phpBB. phpBB prior to v3 uses raw md5 for passwords. If you have upgraded, from a board running v2, to v3, the passwords are not converted UNTIL a user logs in, so there will also likely be many passwords stored simply as non-salted MD5, and the raw-md5 hash can be used for them, and is MUCH faster. Jim. >>>> Patch <<<<< diff -urpN john-1.7.3.1-all-5-orig/src/Makefile john-1.7.3.1-all-5-phpass/src/Makefile --- john-1.7.3.1-all-5-orig/src/Makefile Mon Jun 22 17:22:59 2009 +++ john-1.7.3.1-all-5-phpass/src/Makefile Mon Jun 22 17:41:22 2009 @@ -63,6 +63,7 @@ JOHN_OBJS_MINIMAL = \ sapG_fmt.o sapB_fmt.o \ NS_fmt.o \ HDAA_fmt.o \ + phpassMD5_fmt.o \ batch.o bench.o charset.o common.o compiler.o config.o cracker.o \ crc32.o external.o formats.o getopt.o idle.o inc.o john.o list.o \ loader.o logger.o math.o memory.o misc.o options.o params.o path.o \ diff -urpN john-1.7.3.1-all-5-orig/src/john.c john-1.7.3.1-all-5-phpass/src/john.c --- john-1.7.3.1-all-5-orig/src/john.c Mon Sep 15 01:05:09 2008 +++ john-1.7.3.1-all-5-phpass/src/john.c Mon Jun 22 17:40:19 2009 @@ -44,6 +44,7 @@ extern struct fmt_main fmt_PO; extern struct fmt_main fmt_rawMD5go; extern struct fmt_main fmt_hmacMD5; extern struct fmt_main fmt_IPB2; +extern struct fmt_main fmt_phpassmd5; extern struct fmt_main fmt_MD5_apache; extern struct fmt_main fmt_BFEgg; extern struct fmt_main fmt_KRB5; @@ -107,6 +108,7 @@ static void john_register_all(void) john_register_one(&fmt_PO); john_register_one(&fmt_rawMD5go); john_register_one(&fmt_IPB2); + john_register_one(&fmt_phpassmd5); john_register_one(&fmt_rawSHA1); john_register_one(&fmt_KRB5); john_register_one(&fmt_NSLDAP); diff -urpN john-1.7.3.1-all-5-orig/src/options.c john-1.7.3.1-all-5-phpass/src/options.c --- john-1.7.3.1-all-5-orig/src/options.c Mon Sep 15 01:09:46 2008 +++ john-1.7.3.1-all-5-phpass/src/options.c Fri Jun 12 01:23:44 2009 @@ -98,7 +98,7 @@ static struct opt_entry opt_list[] = { "--salts=[-]COUNT load salts with[out] at least COUNT passwords " \ "only\n" \ "--format=NAME force hash type NAME: " \ - "DES/BSDI/MD5/BF/AFS/LM/NT/XSHA/PO/raw-MD5/IPB2/raw-sha1/md5a/hmac-md5/KRB5/ bfegg/nsldap/ssha/openssha/oracle/MYSQL/mysql-sha1/mscash/lotus5/DOMINOSEC/N ETLM/NETNTLM/NETLMv2/NETHALFLM/mssql/mssql05/epi/phps/mysql-fast/pix-md5/sap G/sapB/md5ns/HDAA\n" \ + "DES/BSDI/MD5/BF/AFS/LM/NT/XSHA/PO/raw-MD5/IPB2/raw-sha1/md5a/hmac-md5/phpas s-md5/KRB5/bfegg/nsldap/ssha/openssha/oracle/MYSQL/mysql-sha1/mscash/lotus5/ DOMINOSEC/NETLM/NETNTLM/NETLMv2/NETHALFLM/mssql/mssql05/epi/phps/mysql-fast/ pix-md5/sapG/sapB/md5ns/HDAA\n" \ "--save-memory=LEVEL enable memory saving, at LEVEL 1..3\n" void opt_init(char *name, int argc, char **argv) diff -urpN john-1.7.3.1-all-5-orig/src/phpassMD5_fmt.c john-1.7.3.1-all-5-phpass/src/phpassMD5_fmt.c --- john-1.7.3.1-all-5-orig/src/phpassMD5_fmt.c Thu Jan 1 00:00:00 1970 +++ john-1.7.3.1-all-5-phpass/src/phpassMD5_fmt.c Mon Jun 22 05:09:20 2009 @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2009 Jim Fougeron jfoug AT cox dot net. + * Cracks phpass 'portable' hashes, and phpBBv3 hashes, which + * are simply phpass portable, with a slightly different signature. + * These are 8 byte salted hashes, with a 1 byte 'salt' that + * defines the number of loops to compute. Internally we work + * with 8 byte salt (the 'real' salt), but let john track it as + * 9 byte salts (the loop count byte is appened to the 'real' + * 8 byte salt value. + * + * code should be pretty fast, and pretty well debugged. Works + * even if there are multple loop count values in the set of + * hashes. PHPv5 kicked up the default loop number, but it is + * programatically allowed to have different looping counts. + * This format should handle all valid loop values. + * + * using SSL version of MD5 and SSE2/MMX MD5 found in md5-mmx.S + * + */ + +#include <string.h> + +#include "arch.h" +#include "misc.h" +#include "common.h" +#include "formats.h" +#include "md5.h" + +#define FORMAT_LABEL "phpass-md5" +#define FORMAT_NAME "PHPass MD5" +#ifdef MMX_COEF +#if (MMX_COEF == 2) +#define ALGORITHM_NAME "phpass-MD5 MMX" +#else +#define ALGORITHM_NAME "phpass-MD5 SSE2" +#endif +#else +#define ALGORITHM_NAME "phpass-md5" +#endif + +#ifdef MMX_TYPE +#define BENCHMARK_COMMENT MMX_TYPE +#else +#define BENCHMARK_COMMENT "" +#endif +#define BENCHMARK_LENGTH -1 + +#define PLAINTEXT_LENGTH 64 +#define CIPHERTEXT_LENGTH 34 + +#define BINARY_SIZE 16 +#define SALT_SIZE 8 +// NOTE salts are only 8 bytes, but we tell john they are 9. +// We then take the 8 bytes of salt, and append the 1 byte of +// loop count data, making it 9. However, internal to this +// code, we only use the 8 bytes of salt. We do 'use' the loop +// count data to set our counters, whenever we set the salt, but +// it is NOT part of the rest of the salt 'usage'. +// So, $H$9PE8jEklg.... would have a salt of PE8jEklg9 but only +// the PE8jEklg is the 'actual' salt, and we use the '9' to figure +// out the looping. + +#ifdef MMX_COEF +#define MIN_KEYS_PER_CRYPT 1 +#define MAX_KEYS_PER_CRYPT MMX_COEF +#define GETPOS(i, index) ( (index)*4 + ((i)& (0xffffffff-3) )*MMX_COEF + ((i)&3) ) +#else +#define MIN_KEYS_PER_CRYPT 1 +#define MAX_KEYS_PER_CRYPT 1 +#endif + +static struct fmt_tests phpassmd5_tests[] = { + {"$H$9PE8jEklgZhgLmZl5.HYJAzfGCQtzi1", "123456"}, + {"$H$9pdx7dbOW3Nnt32sikrjAxYFjX8XoK1", "123456"}, + {"$P$912345678LIjjb6PhecupozNBmDndU0", "thisisalongertestPW"}, + {"$P$6ag7246lgFfuHnqxGz1sxOdHgbQTio.", "123456"}, // note smaller loop count + {"$H$9A5she.OeEiU583vYsRXZ5m2XIpI68/", "123456"}, + {"$P$917UOZtDi6ksoFt.y2wUYvgUI6ZXIK/", "test1"}, + {"$P$91234567AQwVI09JXzrV1hEC6MSQ8I0", "thisisalongertest"}, + {"$P$9234560A8hN6sXs5ir0NfozijdqT6f0", "test2"}, + {"$P$9234560A86ySwM77n2VA/Ey35fwkfP0", "test3"}, + {"$P$9234560A8RZBZDBzO5ygETHXeUZX5b1", "test4"}, + {"$P$91234567xogA.H64Lkk8Cx8vlWBVzH0", "thisisalongertst"}, + {NULL} +}; + +#ifdef MMX_COEF +/* Cygwin would not guarantee the alignment if these were declared static */ +#define phpassmd5_crypt_key crypt_key +#define phpassmd5_cursalt cursalt +char crypt_key[PLAINTEXT_LENGTH*MMX_COEF+1] __attribute__ ((aligned(16))); +unsigned char cursalt[PLAINTEXT_LENGTH*MMX_COEF+1] __attribute__ ((aligned(16))); +unsigned char dump[BINARY_SIZE*MMX_COEF] __attribute__((aligned(16))); +static unsigned keylen[MMX_COEF]; +static unsigned maxkeylen, bNewKeys; +static ARCH_WORD_32 total_len; +static unsigned tot_keys; +static unsigned char EncKey[MMX_COEF][PLAINTEXT_LENGTH + 1]; +#else +static MD5_CTX ctx; +static unsigned char cursalt[SALT_SIZE]; +static char crypt_key[PLAINTEXT_LENGTH+1+BINARY_SIZE]; +static unsigned char EncKey[PLAINTEXT_LENGTH + 1]; +static unsigned EncKeyLen; +#endif +static unsigned loopCnt; +static unsigned char out[PLAINTEXT_LENGTH+1]; + +static int valid(char *ciphertext) +{ + int i; + char *p; + unsigned count_log2; + + if (strlen(ciphertext) != 34) + return 0; + // Handle both the phpass signature, and the phpBB v3 signature (same formula) + // NOTE we are only dealing with the 'portable' encryption method + if (strncmp(ciphertext, "$P$", 3) != 0 && strncmp(ciphertext, "$H$", 3) != 0) + return 0; + for (i = 3; i < 34; ++i) + if (atoi64[(unsigned char)ciphertext[i]] == 0x7F) + return 0; + + p = strchr(itoa64, ciphertext[3]); + if (!p) + return 0; + count_log2 = p-itoa64; + if (count_log2 < 7 || count_log2 > 31) + return 0; + + return 1; +} + +static void phpassmd5_init(void) { +#ifdef MMX_COEF + memset(cursalt, 0, sizeof(cursalt)); + memset(crypt_key, 0, sizeof(crypt_key)); +#endif +} +static void phpassmd5_set_salt(void *salt) +{ + // compute the loop count for this salt + char *p; + unsigned count_log2; + p = strchr(itoa64, ((char*)salt)[8]); + count_log2 = p-itoa64; + loopCnt = (1 << count_log2); + + // Now, deal with the 8 byte salt 'value' +#ifdef MMX_COEF +#if (MMX_COEF == 4) + // since salt is 8 bytes long, we can use 2 32 bit assignments to + // handle the setting (replicated 4 times), vs 32 8 bit character + // assignments. Same end result, but faster. + ((ARCH_WORD_32 *)cursalt)[0] = ((ARCH_WORD_32 *)salt)[0]; + ((ARCH_WORD_32 *)cursalt)[1] = ((ARCH_WORD_32 *)salt)[0]; + ((ARCH_WORD_32 *)cursalt)[2] = ((ARCH_WORD_32 *)salt)[0]; + ((ARCH_WORD_32 *)cursalt)[3] = ((ARCH_WORD_32 *)salt)[0]; + ((ARCH_WORD_32 *)cursalt)[4] = ((ARCH_WORD_32 *)salt)[1]; + ((ARCH_WORD_32 *)cursalt)[5] = ((ARCH_WORD_32 *)salt)[1]; + ((ARCH_WORD_32 *)cursalt)[6] = ((ARCH_WORD_32 *)salt)[1]; + ((ARCH_WORD_32 *)cursalt)[7] = ((ARCH_WORD_32 *)salt)[1]; +#else + ((ARCH_WORD_32 *)cursalt)[0] = ((ARCH_WORD_32 *)salt)[0]; + ((ARCH_WORD_32 *)cursalt)[1] = ((ARCH_WORD_32 *)salt)[0]; + ((ARCH_WORD_32 *)cursalt)[2] = ((ARCH_WORD_32 *)salt)[1]; + ((ARCH_WORD_32 *)cursalt)[3] = ((ARCH_WORD_32 *)salt)[1]; +#endif // MMX_COEF != 4 +#else // !MMX_COEF + ((ARCH_WORD_32 *)cursalt)[0] = ((ARCH_WORD_32 *)salt)[0]; + ((ARCH_WORD_32 *)cursalt)[1] = ((ARCH_WORD_32 *)salt)[1]; +#endif +} + +static void phpassmd5_set_key(char *key, int index) { + int len; + len = strlen(key); +#ifdef MMX_COEF + int i; + + // the SSE code works up to 55 chars, but we have append PW to 16 byte prior + // md5 hashes, so 39 is max PW size we can do with this SSE phpass code. + if(len > (55-16) ) + len = (55-16); + if (index == 0) + { + tot_keys = total_len = 0; + memset(&cursalt[SALT_SIZE*MMX_COEF], 0, (maxkeylen+4)*MMX_COEF); + memset(&crypt_key[BINARY_SIZE*MMX_COEF], 0, (maxkeylen+4)*MMX_COEF); + //memset(&cursalt[SALT_SIZE*MMX_COEF], 0, sizeof(cursalt)-SALT_SIZE*MMX_COEF); + maxkeylen = 0; + } + bNewKeys = 1; + keylen[index] = len; + if (len > maxkeylen) + maxkeylen = len; + strncpy(EncKey[index], key, len); + EncKey[index][len] = 0; + i = SALT_SIZE; + int j, wordcnt = (len >> 2); + if (wordcnt) + { + i += (wordcnt << 2); + for (j = 0; j < wordcnt; ++j) + ((ARCH_WORD_32 *)cursalt)[((SALT_SIZE>>2)+j)*MMX_COEF+index] = ((ARCH_WORD_32 *)key)[j]; + } + for(; i < len+SALT_SIZE; ++i) + cursalt[GETPOS(i, index)] = ((unsigned char *)key)[i-SALT_SIZE]; + cursalt[GETPOS(i, index)] = 0x80; + total_len += ( len << ( ( (32/MMX_COEF) * index ) )); + ++tot_keys; +#else + if(len>PLAINTEXT_LENGTH) + len = PLAINTEXT_LENGTH; + EncKeyLen=len; + strcpy(EncKey, key); +#endif +} + +static char *phpassmd5_get_key(int index) { +#ifdef MMX_COEF + strcpy(out, EncKey[index]); +#else + strcpy(out, EncKey); +#endif + return (char *) out; +} + +static int phpassmd5_cmp_all(void *binary, int index) { + +#ifdef MMX_COEF +#if (MMX_COEF > 3) + unsigned int i=0; + while(i< (BINARY_SIZE/4) ) + { + if((((ARCH_WORD_32 *)binary)[i] != ((ARCH_WORD_32 *)crypt_key)[i*MMX_COEF]) && + (((ARCH_WORD_32 *)binary)[i] != ((ARCH_WORD_32 *)crypt_key)[i*MMX_COEF+1]) +#if (MMX_COEF > 3) + && + (((ARCH_WORD_32 *)binary)[i] != ((ARCH_WORD_32 *)crypt_key)[i*MMX_COEF+2]) && + (((ARCH_WORD_32 *)binary)[i] != ((ARCH_WORD_32 *)crypt_key)[i*MMX_COEF+3]) +#endif + ) + return 0; + i++; + } + return 1; +#endif +#else + int i=0; + while(i<BINARY_SIZE/4) + { + if(((ARCH_WORD_32 *)binary)[i]!=((ARCH_WORD_32 *)crypt_key)[i]) + return 0; + i++; + } +#endif + return 1; +} + +static int phpassmd5_cmp_exact(char *source, int count) +{ + return (1); +} + +#ifdef MMX_COEF +static int phpassmd5_cmp_one(void * binary, int index) +{ + return((((ARCH_WORD_32 *)binary)[0] == ((ARCH_WORD_32 *)crypt_key)[0*MMX_COEF+index]) && + (((ARCH_WORD_32 *)binary)[1] == ((ARCH_WORD_32 *)crypt_key)[1*MMX_COEF+index]) +#if (MMX_COEF > 3) + && + (((ARCH_WORD_32 *)binary)[2] == ((ARCH_WORD_32 *)crypt_key)[2*MMX_COEF+index]) && + (((ARCH_WORD_32 *)binary)[3] == ((ARCH_WORD_32 *)crypt_key)[3*MMX_COEF+index]) +#endif + ); +} +#else +#define phpassmd5_cmp_one phpassmd5_cmp_all +#endif + +#ifdef MMX_COEF +static void appendOneKey(int index) { + int i=0; + + int j, wordcnt = (keylen[index] >> 2); + if (wordcnt) + { + ARCH_WORD_32 *dwKey = ((ARCH_WORD_32*)EncKey[index]); + i += (wordcnt << 2); + for (j = 0; j < wordcnt; ++j) + ((ARCH_WORD_32 *)crypt_key)[((BINARY_SIZE>>2)+j)*MMX_COEF+index] = dwKey[j]; + } + for (; i < keylen[index]; ++i) + crypt_key[GETPOS(i+BINARY_SIZE, index)] = EncKey[index][i]; + crypt_key[GETPOS(keylen[index]+BINARY_SIZE, index)] = 0x80; +} +#endif + +static void phpassmd5_crypt_all(int count) { + unsigned Lcount; + +#ifdef MMX_COEF + int i, cur_working_lengths; + + // The first call, is to encrypt the seeds (8 bytes long) with the password + // appened. Thus, we need total_len + 0x08080808 (for sse2), since the + // 8 byte fixed length of the seeds (0x00080008 for MMX, for the 2 seeds) +#if (MMX_COEF > 2) + cur_working_lengths = 0x08080808 + total_len; +#else + cur_working_lengths = 0x80008 + total_len; +#endif + + // Now, encrypt the seed+pw data + mdfivemmx(crypt_key, cursalt, cur_working_lengths); + + // Now setup length for md5hash+password. The md5hash will be overwrittnen + // again and again, within our loop, but the password (and length info) will + // stay static. Huge improvement over doing 2 MD5 calls. Again, add 0x10 + // to the length of the passwords (0x10101010 for SSE2, 0x00100010 for MMX) +#if (MMX_COEF > 2) + cur_working_lengths = 0x10101010 + total_len; +#else + cur_working_lengths = 0x100010 + total_len; +#endif + if (bNewKeys) + { + bNewKeys = 0; + for (i = 0; i < tot_keys; ++i) + appendOneKey(i); + } + + Lcount = loopCnt; + // Now, encrypt the hash+pw data (again and again) NOTE crypt_key is both input + // and output. the md5 hashes will be at the 'base' of this, followed by the + // already stored passwords. + + mdfivemmx( crypt_key, crypt_key, cur_working_lengths); + --Lcount; + do + { + //mdfivemmx( crypt_key, crypt_key, cur_working_lengths); + mdfivemmx_nosizeupdate( crypt_key, crypt_key, cur_working_lengths); + } while (--Lcount); + +#else + MD5_Init( &ctx ); + MD5_Update( &ctx, cursalt, 8 ); + MD5_Update( &ctx, EncKey, EncKeyLen ); + MD5_Final( (unsigned char *) crypt_key, &ctx); + + strcpy(&crypt_key[BINARY_SIZE], EncKey); + Lcount = loopCnt; + do { + MD5_Init( &ctx ); + MD5_Update( &ctx, crypt_key, BINARY_SIZE+EncKeyLen); + MD5_Final( (unsigned char *) crypt_key, &ctx); + } while (--Lcount); +#endif +} + +static void * phpassmd5_binary(char *ciphertext) +{ + int i; + unsigned sixbits; + static unsigned char b[16]; + int bidx=0; + char *pos; + + // ugly code, but only called one time (at program load, + // once for each candidate pass hash). + + pos = &ciphertext[3+1+8]; + for (i = 0; i < 5; ++i) + { + sixbits = (unsigned)strchr(itoa64, *pos++); + sixbits -= (unsigned)itoa64; + b[bidx] = sixbits; + sixbits = (unsigned)strchr(itoa64, *pos++); + sixbits -= (unsigned)itoa64; + b[bidx++] |= (sixbits<<6); + sixbits >>= 2; + b[bidx] = sixbits; + sixbits = (unsigned)strchr(itoa64, *pos++); + sixbits -= (unsigned)itoa64; + b[bidx++] |= (sixbits<<4); + sixbits >>= 4; + b[bidx] = sixbits; + sixbits = (unsigned)strchr(itoa64, *pos++); + sixbits -= (unsigned)itoa64; + b[bidx++] |= (sixbits<<2); + } + sixbits = (unsigned)strchr(itoa64, *pos++); + sixbits -= (unsigned)itoa64; + b[bidx] = sixbits; + sixbits = (unsigned)strchr(itoa64, *pos); + sixbits -= (unsigned)itoa64; + b[bidx] |= (sixbits<<6); + return b; +} + +static void * phpassmd5_salt(char *ciphertext) +{ + static unsigned char salt[SALT_SIZE+2]; + // store off the 'real' 8 bytes of salt + memcpy(salt, &ciphertext[4], 8); + // append the 1 byte of loop count information. + salt[8] = ciphertext[3]; + salt[9]=0; + return salt; +} + +struct fmt_main fmt_phpassmd5 = { + { + FORMAT_LABEL, + FORMAT_NAME, + ALGORITHM_NAME, + BENCHMARK_COMMENT, + BENCHMARK_LENGTH, + PLAINTEXT_LENGTH, + BINARY_SIZE, + // only true salt of SALT_SIZE (8), but we store on 'extra' byte + // as a salt, since we need it AND it does act as a different salt + // byte. However, when we use the salt in our crypting, we only + // use the SALT_SIZE bytes. + SALT_SIZE+1, + MIN_KEYS_PER_CRYPT, + MAX_KEYS_PER_CRYPT, + FMT_CASE | FMT_8_BIT, + phpassmd5_tests + }, { + phpassmd5_init, + valid, + fmt_default_split, + phpassmd5_binary, + phpassmd5_salt, + { + fmt_default_binary_hash, + fmt_default_binary_hash, + fmt_default_binary_hash + }, + fmt_default_salt_hash, + phpassmd5_set_salt, + phpassmd5_set_key, + phpassmd5_get_key, + fmt_default_clear_keys, + phpassmd5_crypt_all, + { + fmt_default_get_hash, + fmt_default_get_hash, + fmt_default_get_hash + }, + phpassmd5_cmp_all, + phpassmd5_cmp_one, + phpassmd5_cmp_exact + } +}; -- To unsubscribe, e-mail john-users-unsubscribe@...ts.openwall.com and reply to the automated confirmation request that will be sent to you.
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.