This shows you the differences between two versions of the page.
— |
john:MSCash2_simple_code [2011/06/24 19:51] (current) JimF created |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ===== MSCash2 code (simple) ===== | ||
+ | |||
+ | <code c> | ||
+ | /* | ||
+ | * dcc2_tst.c A 'very' simple OpenSSL primative only test app, showing the | ||
+ | * MSCASH2 format. | ||
+ | * Written June 24, 2011, Jim Fougeron. Placed in public domain | ||
+ | * This is a very slow, but easier to understand mscash2 hash computation program | ||
+ | * It has been written 100% with oSSL primative functions (SHA1 and MD4). It does | ||
+ | * not use any hmac primatives. This pbkdf2 'has' been reduced some, since we | ||
+ | * know a 'little' info. 1. the key will always be smaller than SHA_DIGEST_LENGTH | ||
+ | * thus we can remove an initial sha reduction. 2. we only use lower 128 bites, so | ||
+ | * only xor the first 4 words, not first 5 | ||
+ | * | ||
+ | * If the program is run with no params, then user=admin and pass=password is used | ||
+ | * arguments -p=PASSWORD and -u=USER can be used to change the pass / user | ||
+ | */ | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <string.h> | ||
+ | #ifndef _MSC_VER | ||
+ | #include <unistd.h> | ||
+ | #endif | ||
+ | #include "openssl/sha.h" | ||
+ | #include "openssl/md4.h" | ||
+ | |||
+ | /* | ||
+ | * This function is derived from IEEE Std 802.11-2004, Clause H.4. | ||
+ | * The main construction is from PKCS#5 v2.0. It is tweaked a little | ||
+ | * to remove some code not needed for our SHA1-128 output. | ||
+ | */ | ||
+ | void pbkdf2(unsigned char key[], size_t key_len, | ||
+ | unsigned char salt[],size_t salt_len, | ||
+ | unsigned int rounds, | ||
+ | unsigned char digest[]) | ||
+ | { | ||
+ | SHA_CTX ctx1, ctx2, tmp_ctx1, tmp_ctx2; | ||
+ | unsigned char ipad[SHA_CBLOCK+1], opad[SHA_CBLOCK+1], tmp_hash[SHA_DIGEST_LENGTH]; | ||
+ | unsigned i, j; | ||
+ | |||
+ | memset(ipad, 0x36, sizeof(ipad)); | ||
+ | memset(opad, 0x5C, sizeof(opad)); | ||
+ | |||
+ | for(i = 0;i < key_len;i++) { | ||
+ | ipad[i] ^= key[i]; | ||
+ | opad[i] ^= key[i]; | ||
+ | } | ||
+ | |||
+ | SHA1_Init(&ctx1); | ||
+ | SHA1_Init(&ctx2); | ||
+ | |||
+ | SHA1_Update(&ctx1,ipad,SHA_CBLOCK); | ||
+ | SHA1_Update(&ctx2,opad,SHA_CBLOCK); | ||
+ | |||
+ | memcpy(&tmp_ctx1,&ctx1,sizeof(SHA_CTX)); | ||
+ | memcpy(&tmp_ctx2,&ctx2,sizeof(SHA_CTX)); | ||
+ | |||
+ | SHA1_Update(&ctx1,salt,salt_len); | ||
+ | SHA1_Final(tmp_hash,&ctx1); | ||
+ | |||
+ | SHA1_Update(&ctx2,tmp_hash,SHA_DIGEST_LENGTH); | ||
+ | SHA1_Final(tmp_hash,&ctx2); | ||
+ | |||
+ | memcpy(digest,tmp_hash,SHA_DIGEST_LENGTH); | ||
+ | |||
+ | for(i = 1;i < rounds;i++) | ||
+ | { | ||
+ | memcpy(&ctx1,&tmp_ctx1,sizeof(SHA_CTX)); | ||
+ | memcpy(&ctx2,&tmp_ctx2,sizeof(SHA_CTX)); | ||
+ | |||
+ | SHA1_Update(&ctx1,tmp_hash,SHA_DIGEST_LENGTH); | ||
+ | SHA1_Final(tmp_hash,&ctx1); | ||
+ | |||
+ | SHA1_Update(&ctx2,tmp_hash,SHA_DIGEST_LENGTH); | ||
+ | SHA1_Final(tmp_hash,&ctx2); | ||
+ | |||
+ | for(j = 0;j < 4;j++) | ||
+ | ((unsigned int*)digest)[j] ^= ((unsigned int*)tmp_hash)[j]; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // simple 'to-unicode', adds null bytes. !WARNING! no overflow logic. | ||
+ | unsigned to_unicode(char *u16, char *a8) { | ||
+ | unsigned cnt = strlen(a8); | ||
+ | while (*a8) { | ||
+ | *u16++ = *a8++; *u16++ = 0; | ||
+ | } | ||
+ | return cnt<<1; | ||
+ | } | ||
+ | |||
+ | char hexdigit(int i) { // one hex digit | ||
+ | if (i < 10) return i+'0'; | ||
+ | return (i-10)+'a'; | ||
+ | } | ||
+ | char *to_hex(unsigned char *digest) { // convert 16 byte digest to 32 byte hex | ||
+ | static char buf[33]; | ||
+ | char *cp = buf; | ||
+ | int i; | ||
+ | for (i = 0; i < 16; ++i) { | ||
+ | *cp++ = hexdigit(*digest >> 4); *cp++ = hexdigit(*digest++ & 0xF); | ||
+ | } | ||
+ | *cp = 0; | ||
+ | return buf; | ||
+ | } | ||
+ | |||
+ | /* | ||
+ | * usage: dcc2_tst [-p=pass] [-u=username] | ||
+ | */ | ||
+ | int main(int argc, char **argv) | ||
+ | { | ||
+ | char *username = "admin", *password = "password"; | ||
+ | unsigned char username_lc[22], salt[44], pass_unicode[128+2], md4hash[16], digest[20]; | ||
+ | unsigned salt_len, pass_len; | ||
+ | MD4_CTX ctx; | ||
+ | int i; | ||
+ | |||
+ | // see if -p= or -u= was used. If so, then use them. | ||
+ | for (i = 1; i < argc; ++i) { | ||
+ | if (!strncmp(argv[i], "-p=", 3)) password = &argv[i][3]; | ||
+ | if (!strncmp(argv[i], "-u=", 3)) username = &argv[i][3]; | ||
+ | } | ||
+ | |||
+ | // low case user name (the salt), and convert to unicode. | ||
+ | strncpy(username_lc, username, 21); | ||
+ | username_lc[21] = 0; | ||
+ | if (strlen(username) != strlen(username_lc)) return !!printf("Error, the user name is longer than 21 bytes. Aborting\n"); | ||
+ | salt_len = to_unicode(salt, strlwr(username_lc)); | ||
+ | |||
+ | // pasword to unicode | ||
+ | pass_len = to_unicode(pass_unicode, password); | ||
+ | |||
+ | // now get NTLM of the password (MD4 of unicode) | ||
+ | |||
+ | MD4_Init(&ctx); | ||
+ | MD4_Update(&ctx,pass_unicode,pass_len); | ||
+ | MD4_Final(md4hash,&ctx); | ||
+ | // Now we have NTLM md4Hash==NTLM of the password | ||
+ | |||
+ | // Get DCC1. That is MD4( NTLM . unicode(lc username) ) | ||
+ | MD4_Init(&ctx); | ||
+ | MD4_Update(&ctx,md4hash,16); | ||
+ | MD4_Update(&ctx,salt,salt_len); | ||
+ | MD4_Final(md4hash,&ctx); | ||
+ | // now we have DCC1 (mscash) which is MD4 (MD4(unicode(pass)) . unicode(lc username)) | ||
+ | |||
+ | // we need to change the salt a little, before calling pbkdf2 (add a big endian 32 bit 1) | ||
+ | memset(&salt[salt_len], 0, 4); | ||
+ | salt[salt_len+3] = 1; | ||
+ | salt_len += 4; | ||
+ | |||
+ | // Now compute DCC2 | ||
+ | pbkdf2(md4hash, 16, salt, salt_len, 10240, digest); | ||
+ | |||
+ | // Ok, now output the hash: | ||
+ | printf ("%s:", username); | ||
+ | printf ("%$%s#%s:0:1:%s\n", username_lc, to_hex(digest), password); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | [[MSCash2|back to MSCash2 page]] | ||