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]] | ||