2 RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique.
4 Copyright (C) Zhu Shuanglei <shuanglei@hotmail.com>
6 Modified by Martin Westergaard Jørgensen <martinwj2005@gmail.com> to support indexed and hybrid tables
8 Modified by neinbrucke to support multi threading and a bunch of other stuff :)
10 2009-01-04 - <james.dickson@comhem.se> - Slightly modified (or "fulhack" as we say in sweden)
11 to support cain .lst files.
15 #pragma warning(disable : 4786 4267 4018)
18 #include "CrackEngine.h"
27 #include <sys/types.h>
33 #include <openssl/md4.h>
35 #pragma comment(lib, "libeay32.lib")
38 //////////////////////////////////////////////////////////////////////
41 void GetTableList(string sWildCharPathName, vector<string>& vPathName)
46 int n = sWildCharPathName.find_last_of('\\');
48 if (n == (sWildCharPathName.size() - 1))
50 sWildCharPathName = sWildCharPathName.substr(0, n);
51 n = sWildCharPathName.find_last_of('\\');
55 sPath = sWildCharPathName.substr(0, n + 1);
59 long handle = _findfirst(sWildCharPathName.c_str(), &fd);
64 string sName = fd.name;
66 if (sName.substr(sName.size()-3, 3) == ".rt" && !(fd.attrib & _A_SUBDIR))
68 string sPathName = sPath + sName;
69 vPathName.push_back(sPathName);
73 if (sName.substr(sName.size()-4, 4) == ".rti" && !(fd.attrib & _A_SUBDIR))
75 string sPathName = sPath + sName;
76 vPathName.push_back(sPathName);
80 if (sName.substr(sName.size()-5, 5) == ".rti2" && !(fd.attrib & _A_SUBDIR))
82 string sPathName = sPath + sName;
83 vPathName.push_back(sPathName);
87 if (sName != "." && sName != ".." && (fd.attrib & _A_SUBDIR))
89 string sPath_sub = sPath + sName + '\\';
90 string sWildCharPathName_sub = sPath_sub + '*';
91 GetTableList(sWildCharPathName_sub, vPathName);
94 } while (_findnext(handle, &fd) == 0);
98 //printf("Found %d rainbowtables (files) in %d sub directories...\n", vPathName.size(), subDir_count);
101 //void GetTableList(int argc, char* argv[], vector<string>& vPathName)
102 void GetTableList(string sWildCharPathName, vector<string>& vPathName)
107 if (lstat(sWildCharPathName.c_str(), &buf) == 0)
109 if (S_ISDIR(buf.st_mode))
111 DIR *dir = opendir(sWildCharPathName.c_str());
114 struct dirent *pDir=NULL;
115 while((pDir = readdir(dir)) != NULL)
117 string filename = "";
118 filename += (*pDir).d_name;
119 if (filename != "." && filename != "..")
121 string new_filename = sWildCharPathName + '/' + filename;
122 GetTableList(new_filename, vPathName);
128 else if (S_ISREG(buf.st_mode))
130 if (sWildCharPathName.size()>3)
132 if (sWildCharPathName.substr(sWildCharPathName.size()-3, 3) == ".rt")
134 vPathName.push_back(sWildCharPathName);
137 if (sWildCharPathName.size()>4)
139 if (sWildCharPathName.substr(sWildCharPathName.size()-4, 4) == ".rti")
141 //string sPathName_sub = sPath_sub + sName_sub;
142 vPathName.push_back(sWildCharPathName);
143 //printf("sPathName_sub: %s\n", sPathName_sub.c_str());
151 bool NormalizeHash(string& sHash)
153 string sNormalizedHash = sHash;
155 if ( sNormalizedHash.size() % 2 != 0
156 || sNormalizedHash.size() < MIN_HASH_LEN * 2
157 || sNormalizedHash.size() > MAX_HASH_LEN * 2)
162 for (i = 0; i < sNormalizedHash.size(); i++)
164 if (sNormalizedHash[i] >= 'A' && sNormalizedHash[i] <= 'F')
165 sNormalizedHash[i] = sNormalizedHash[i] - 'A' + 'a';
169 for (i = 0; i < sNormalizedHash.size(); i++)
171 if ( (sNormalizedHash[i] < 'a' || sNormalizedHash[i] > 'f')
172 && (sNormalizedHash[i] < '0' || sNormalizedHash[i] > '9'))
176 sHash = sNormalizedHash;
180 void LoadLMHashFromPwdumpFile(string sPathName, vector<string>& vUserName, vector<string>& vLMHash, vector<string>& vNTLMHash)
182 vector<string> vLine;
183 if (ReadLinesFromFile(sPathName, vLine))
186 for (i = 0; i < vLine.size(); i++)
188 vector<string> vPart;
189 if (SeperateString(vLine[i], "::::", vPart))
191 string sUserName = vPart[0];
192 string sLMHash = vPart[2];
193 string sNTLMHash = vPart[3];
195 if (sLMHash.size() == 32 && sNTLMHash.size() == 32)
197 if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash))
199 vUserName.push_back(sUserName);
200 vLMHash.push_back(sLMHash);
201 vNTLMHash.push_back(sNTLMHash);
204 printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str());
210 printf("can't open %s\n", sPathName.c_str());
213 // 2009-01-04 - james - Added this so we can load hashes from cain .LST files.
214 void LoadLMHashFromCainLSTFile(string sPathName, vector<string>& vUserName, vector<string>& vLMHash, vector<string>& vNTLMHash)
216 vector<string> vLine;
217 if (ReadLinesFromFile(sPathName, vLine))
220 for (i = 0; i < vLine.size(); i++)
222 vector<string> vPart;
223 if (SeperateString(vLine[i], "\t\t\t\t\t\t", vPart))
225 string sUserName = vPart[0];
226 string sLMHash = vPart[4];
227 string sNTLMHash = vPart[5];
229 if (sLMHash.size() == 32 && sNTLMHash.size() == 32)
231 if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash))
233 vUserName.push_back(sUserName);
234 vLMHash.push_back(sLMHash);
235 vNTLMHash.push_back(sNTLMHash);
238 printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str());
244 printf("can't open %s\n", sPathName.c_str());
247 bool NTLMPasswordSeek(unsigned char* pLMPassword, int nLMPasswordLen, int nLMPasswordNext,
248 unsigned char* pNTLMHash, string& sNTLMPassword)
250 if (nLMPasswordNext == nLMPasswordLen)
252 unsigned char md[16];
253 MD4(pLMPassword, nLMPasswordLen * 2, md);
254 if (memcmp(md, pNTLMHash, 16) == 0)
258 for (i = 0; i < nLMPasswordLen; i++)
259 sNTLMPassword += char(pLMPassword[i * 2]);
266 if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword))
269 if ( pLMPassword[nLMPasswordNext * 2] >= 'A'
270 && pLMPassword[nLMPasswordNext * 2] <= 'Z')
272 pLMPassword[nLMPasswordNext * 2] = pLMPassword[nLMPasswordNext * 2] - 'A' + 'a';
273 if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword))
275 pLMPassword[nLMPasswordNext * 2] = pLMPassword[nLMPasswordNext * 2] - 'a' + 'A';
281 bool LMPasswordCorrectCase(string sLMPassword, unsigned char* pNTLMHash, string& sNTLMPassword)
283 if (sLMPassword.size() == 0)
289 unsigned char* pLMPassword = new unsigned char[sLMPassword.size() * 2];
291 for (i = 0; i < sLMPassword.size(); i++)
293 pLMPassword[i * 2 ] = sLMPassword[i];
294 pLMPassword[i * 2 + 1] = 0x00;
296 bool fRet = NTLMPasswordSeek(pLMPassword, sLMPassword.size(), 0, pNTLMHash, sNTLMPassword);
307 printf("usage: rcracki_mt -h hash rainbow_table_pathname\n");
308 printf(" rcracki_mt -l hash_list_file rainbow_table_pathname\n");
309 printf(" rcracki_mt -f pwdump_file rainbow_table_pathname\n");
310 printf(" rcracki_mt -c lst_file rainbow_table_pathname\n");
312 printf("-h hash: use raw hash as input\n");
313 printf("-l hash_list_file: use hash list file as input, each hash in a line\n");
314 printf("-f pwdump_file: use pwdump file as input, handles lanmanager hash only\n");
315 printf("-c lst_file: use .lst (cain format) file as input\n");
316 printf("-r [-s session_name]: resume from previous session, optional session name\n");
317 printf("rainbow_table_pathname: pathname(s) of the rainbow table(s)\n");
319 printf("Extra options: -t [nr] use this amount of threads/cores, default is 1\n");
320 printf(" -o [output_file] write (temporary) results to this file\n");
321 printf(" -s [session_name] write session data with this name\n");
322 printf(" -k keep precalculation on disk\n");
323 printf(" -v show debug information\n");
326 printf("example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 2 [path]\\MD5\n");
327 printf(" rcracki_mt -l hash.txt [path_to_specific_table]\\*\n");
329 printf("example: rcracki_mt -h 5d41402abc4b2a76b9719d911017c592 -t 2 [path]/MD5\n");
330 printf(" rcracki_mt -l hash.txt [path_to_specific_table]/*\n");
332 printf(" rcracki_mt -f hash.txt -t 4 -o results.txt *.rti\n");
336 int main(int argc, char* argv[])
344 vector<string> vPathName;
345 vector<string> vDefaultRainbowTablePath;
346 string sWildCharPathName = "";
347 string sInputType = "";
349 string outputFile = "";
350 string sApplicationPath = "";
351 string sIniPathName = "rcracki_mt.ini";
352 bool writeOutput = false;
353 string sSessionPathName = "rcracki.session";
354 string sProgressPathName = "rcracki.progress";
355 string sPrecalcPathName = "rcracki.precalc";
356 bool resumeSession = false;
357 bool useDefaultRainbowTablePath = false;
359 bool keepPrecalcFiles = false;
360 string sAlgorithm = "";
364 // Read defaults from ini file;
365 bool readFromIni = false;
366 vector<string> vLine;
367 if (ReadLinesFromFile(sIniPathName, vLine)) {
370 else if (ReadLinesFromFile(GetApplicationPath() + sIniPathName, vLine)) {
376 for (i = 0; i < vLine.size(); i++)
378 if (vLine[i].substr(0,1) != "#")
380 vector<string> vPart;
381 if (SeperateString(vLine[i], "=", vPart))
383 string sOption = vPart[0];
384 string sValue = vPart[1];
386 if (sOption == "Threads") {
387 maxThreads = atoi(sValue.c_str());
389 else if (sOption == "DefaultResultsFile") {
392 else if (sOption == "AlwaysStoreResultsToFile") {
396 else if (sOption.substr(0,24) == "DefaultRainbowTablePath.") {
397 //printf("Default RT path: %s\n", sValue.c_str());
398 vDefaultRainbowTablePath.push_back(vLine[i]);
400 else if (sOption == "DefaultAlgorithm") {
401 useDefaultRainbowTablePath = true;
404 else if (sOption == "AlwaysDebug") {
408 else if (sOption == "AlwaysKeepPrecalcFiles") {
410 keepPrecalcFiles = true;
413 printf("illegal option %s in ini file %s\n", sOption.c_str(), sIniPathName.c_str());
419 if (writeOutput && outputFile == "")
421 printf("You need to specify a 'DefaultResultsFile' with 'AlwaysStoreResultsToFile=1'\n");
426 // Parse command line arguments
428 for (i = 1; i < argc; i++)
430 string cla = argv[i];
437 else if (cla == "-l") {
443 else if (cla == "-f") {
449 else if (cla == "-c") {
455 else if (cla == "-t") {
458 maxThreads = atoi(argv[i]);
460 else if (cla == "-o") {
464 outputFile = argv[i];
466 else if (cla == "-r") {
467 resumeSession = true;
469 else if (cla == "-s") {
473 sSessionPathName = argv[i];
474 sSessionPathName += ".session";
475 sProgressPathName = argv[i];
476 sProgressPathName += ".progress";
477 sPrecalcPathName = argv[i];
478 sPrecalcPathName += ".precalc";
481 else if (cla == "-v") {
484 else if (cla == "-k") {
485 keepPrecalcFiles = true;
487 else if (cla == "-a") {
488 useDefaultRainbowTablePath = true;
491 sAlgorithm = argv[i];
494 GetTableList(cla, vPathName);
498 if (debug && !readFromIni)
499 printf("Debug: Couldn't read rcracki_mt.ini, continuing anyway.\n");
501 // Load session data if we are resuming
505 vector<string> sSessionData;
506 if (ReadLinesFromFile(sSessionPathName.c_str(), sSessionData))
509 for (i = 0; i < sSessionData.size(); i++)
511 vector<string> vPart;
512 if (SeperateString(sSessionData[i], "=", vPart))
514 string sOption = vPart[0];
515 string sValue = vPart[1];
517 if (sOption == "sPathName") {
518 vPathName.push_back(sValue);
520 else if (sOption == "sInputType") {
523 else if (sOption == "sInput") {
526 else if (sOption == "outputFile") {
530 else if (sOption == "keepPrecalcFiles") {
532 keepPrecalcFiles = true;
538 printf("Couldn't open session file %s\n", sSessionPathName.c_str());
546 // don't load these if we are resuming a session that already has a list of tables
547 if (useDefaultRainbowTablePath && !resumeSession)
550 for (i = 0; i < vDefaultRainbowTablePath.size(); i++)
552 vector<string> vPart;
553 if (SeperateString(vDefaultRainbowTablePath[i], ".=", vPart))
555 string lineAlgorithm = vPart[1];
556 string linePath = vPart[2];
558 if (lineAlgorithm == sAlgorithm)
559 GetTableList(linePath, vPathName);
564 printf("Using %d threads for pre-calculation and false alarm checking...\n", maxThreads);
566 setvbuf(stdout, NULL, _IONBF,0);
567 if (vPathName.size() == 0)
569 printf("no rainbow table found\n");
572 printf("Found %d rainbowtable files...\n\n", vPathName.size());
574 bool fCrackerType; // true: hash cracker, false: lm cracker
575 vector<string> vHash; // hash cracker
576 vector<string> vUserName; // lm cracker
577 vector<string> vLMHash; // lm cracker
578 vector<string> vNTLMHash; // lm cracker
579 if (sInputType == "-h")
583 string sHash = sInput;
584 if (NormalizeHash(sHash))
585 vHash.push_back(sHash);
587 printf("invalid hash: %s\n", sHash.c_str());
589 else if (sInputType == "-l")
593 string sPathName = sInput;
594 vector<string> vLine;
595 if (ReadLinesFromFile(sPathName, vLine))
598 for (i = 0; i < vLine.size(); i++)
600 string sHash = vLine[i];
601 if (NormalizeHash(sHash))
602 vHash.push_back(sHash);
604 printf("invalid hash: %s\n", sHash.c_str());
608 printf("can't open %s\n", sPathName.c_str());
610 else if (sInputType == "-f")
612 fCrackerType = false;
614 string sPathName = sInput;
615 LoadLMHashFromPwdumpFile(sPathName, vUserName, vLMHash, vNTLMHash);
617 else if (sInputType == "-c")
619 // 2009-01-04 - james - Added this for cain-files.
620 fCrackerType = false;
621 string sPathName = sInput;
622 LoadLMHashFromCainLSTFile(sPathName, vUserName, vLMHash, vNTLMHash);
630 if (fCrackerType && vHash.size() == 0)
632 printf("no hashes found");
635 if (!fCrackerType && vLMHash.size() == 0)
638 printf("no hashes found");
644 for (i = 0; i < vHash.size(); i++)
645 hs.AddHash(vHash[i]);
650 for (i = 0; i < vLMHash.size(); i++)
652 hs.AddHash(vLMHash[i].substr(0, 16));
653 hs.AddHash(vLMHash[i].substr(16, 16));
657 // Load found hashes from session file
660 vector<string> sSessionData;
661 if (ReadLinesFromFile(sSessionPathName.c_str(), sSessionData))
664 for (i = 0; i < sSessionData.size(); i++)
666 vector<string> vPart;
667 if (SeperateString(sSessionData[i], "=", vPart))
669 string sOption = vPart[0];
670 string sValue = vPart[1];
672 if (sOption == "sHash") {
673 vector<string> vPartHash;
674 if (SeperateString(sValue, "::", vPartHash))
676 string sHash = vPartHash[0];
677 string sBinary = vPartHash[1];
678 string sPlain = vPartHash[2];
680 hs.SetPlain(sHash, sPlain, sBinary);
688 // (Over)write session data if we are not resuming
691 FILE* file = fopen(sSessionPathName.c_str(), "w");
696 buffer += "sInputType=" + sInputType + "\n";
697 buffer += "sInput=" + sInput + "\n";
700 for (i = 0; i < vPathName.size(); i++)
702 buffer += "sPathName=" + vPathName[i] + "\n";
706 buffer += "outputFile=" + outputFile + "\n";
708 if (keepPrecalcFiles)
709 buffer += "keepPrecalcFiles=1\n";
711 fputs (buffer.c_str(), file);
714 file = fopen(sProgressPathName.c_str(), "w");
721 ce.setOutputFile(outputFile);
722 ce.setSession(sSessionPathName, sProgressPathName, sPrecalcPathName, keepPrecalcFiles);
723 ce.Run(vPathName, hs, maxThreads, resumeSession, debug);
725 // Remove session files
726 if (debug) printf("Debug: Removing session files.\n");
727 if (remove(sSessionPathName.c_str()) == 0)
728 remove(sProgressPathName.c_str());
730 if (debug) printf("Debug: Failed removing session files.\n");
733 printf("statistics\n");
734 printf("-------------------------------------------------------\n");
735 printf("plaintext found: %d of %d (%.2f%%)\n", hs.GetStatHashFound(),
736 hs.GetStatHashTotal(),
737 100.0f * hs.GetStatHashFound() / hs.GetStatHashTotal());
738 printf("total disk access time: %.2f s\n", ce.GetStatTotalDiskAccessTime());
739 printf("total cryptanalysis time: %.2f s\n", ce.GetStatTotalCryptanalysisTime());
740 printf("total chain walk step: %d\n", ce.GetStatTotalChainWalkStep());
741 printf("total false alarm: %d\n", ce.GetStatTotalFalseAlarm());
742 printf("total chain walk step due to false alarm: %d\n", ce.GetStatTotalChainWalkStepDueToFalseAlarm());
743 // printf("total chain walk step skipped due to checkpoints: %d\n", ce.GetStatTotalFalseAlarmSkipped()); // Checkpoints not used - yet
748 printf("-------------------------------------------------------\n");
752 for (i = 0; i < vHash.size(); i++)
754 string sPlain, sBinary;
755 if (!hs.GetPlain(vHash[i], sPlain, sBinary))
757 sPlain = "<notfound>";
758 sBinary = "<notfound>";
761 printf("%s\t%s\thex:%s\n", vHash[i].c_str(), sPlain.c_str(), sBinary.c_str());
767 for (i = 0; i < vLMHash.size(); i++)
769 string sPlain1, sBinary1;
770 bool fPart1Found = hs.GetPlain(vLMHash[i].substr(0, 16), sPlain1, sBinary1);
773 sPlain1 = "<notfound>";
774 sBinary1 = "<notfound>";
777 string sPlain2, sBinary2;
778 bool fPart2Found = hs.GetPlain(vLMHash[i].substr(16, 16), sPlain2, sBinary2);
781 sPlain2 = "<notfound>";
782 sBinary2 = "<notfound>";
785 string sPlain = sPlain1 + sPlain2;
786 string sBinary = sBinary1 + sBinary2;
789 if (fPart1Found && fPart2Found)
791 unsigned char NTLMHash[16];
793 ParseHash(vNTLMHash[i], NTLMHash, nHashLen);
795 printf("debug: nHashLen mismatch\n");
796 string sNTLMPassword;
797 if (LMPasswordCorrectCase(sPlain, NTLMHash, sNTLMPassword))
799 sPlain = sNTLMPassword;
800 sBinary = HexToStr((const unsigned char*)sNTLMPassword.c_str(), sNTLMPassword.size());
804 printf("%-14s\t%s\thex:%s\n", vUserName[i].c_str(), sPlain.c_str(), sBinary.c_str());
805 LM2NTLMcorrector corrector;
806 if (corrector.LMPasswordCorrectUnicode(sBinary, NTLMHash, sNTLMPassword))
808 sPlain = sNTLMPassword;
809 sBinary = corrector.getBinary();
812 if (!writeResultLineToFile(outputFile, vNTLMHash[i].c_str(), sPlain.c_str(), sBinary.c_str()))
813 printf("Couldn't write final result to file!\n");
817 printf("case correction for password %s failed!\n", sPlain.c_str());
823 printf("%-14s\t%s\thex:%s\n", vUserName[i].c_str(),