MSCash2 code (simple)

/*
 * 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;
}

back to MSCash2 page

john/MSCash2_simple_code.txt · Last modified: 2011/06/24 19:51 by JimF
 
Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate to DokuWiki Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki Powered by OpenVZ Powered by Openwall GNU/*/Linux