/* RainbowCrack - a general propose implementation of Philippe Oechslin's faster time-memory trade-off technique. Copyright (C) Zhu Shuanglei */ #ifdef _WIN32 #pragma warning(disable : 4786) #endif #include "CrackEngine.h" #ifdef _WIN32 #include #else #include #include #include #endif #include #ifdef _WIN32 #pragma comment(lib, "libeay32.lib") #endif ////////////////////////////////////////////////////////////////////// #ifdef _WIN32 void GetTableList(string sWildCharPathName, vector& vPathName) { vPathName.clear(); string sPath; int n = sWildCharPathName.find_last_of('\\'); if (n != -1) sPath = sWildCharPathName.substr(0, n + 1); _finddata_t fd; long handle = _findfirst(sWildCharPathName.c_str(), &fd); if (handle != -1) { do { string sName = fd.name; if (sName != "." && sName != ".." && !(fd.attrib & _A_SUBDIR)) { string sPathName = sPath + sName; vPathName.push_back(sPathName); } } while (_findnext(handle, &fd) == 0); _findclose(handle); } } #else void GetTableList(int argc, char* argv[], vector& vPathName) { vPathName.clear(); int i; for (i = 1; i <= argc - 3; i++) { string sPathName = argv[i]; struct stat buf; if (lstat(sPathName.c_str(), &buf) == 0) { if (S_ISREG(buf.st_mode)) vPathName.push_back(sPathName); } } } #endif bool NormalizeHash(string& sHash) { string sNormalizedHash = sHash; if ( sNormalizedHash.size() % 2 != 0 || sNormalizedHash.size() < MIN_HASH_LEN * 2 || sNormalizedHash.size() > MAX_HASH_LEN * 2) return false; // Make lower int i; for (i = 0; i < sNormalizedHash.size(); i++) { if (sNormalizedHash[i] >= 'A' && sNormalizedHash[i] <= 'F') sNormalizedHash[i] = sNormalizedHash[i] - 'A' + 'a'; } // Character check for (i = 0; i < sNormalizedHash.size(); i++) { if ( (sNormalizedHash[i] < 'a' || sNormalizedHash[i] > 'f') && (sNormalizedHash[i] < '0' || sNormalizedHash[i] > '9')) return false; } sHash = sNormalizedHash; return true; } void LoadLMHashFromPwdumpFile(string sPathName, vector& vUserName, vector& vLMHash, vector& vNTLMHash) { vector vLine; if (ReadLinesFromFile(sPathName, vLine)) { int i; for (i = 0; i < vLine.size(); i++) { vector vPart; if (SeperateString(vLine[i], "::::", vPart)) { string sUserName = vPart[0]; string sLMHash = vPart[2]; string sNTLMHash = vPart[3]; if (sLMHash.size() == 32 && sNTLMHash.size() == 32) { if (NormalizeHash(sLMHash) && NormalizeHash(sNTLMHash)) { vUserName.push_back(sUserName); vLMHash.push_back(sLMHash); vNTLMHash.push_back(sNTLMHash); } else printf("invalid lm/ntlm hash %s:%s\n", sLMHash.c_str(), sNTLMHash.c_str()); } } } } else printf("can't open %s\n", sPathName.c_str()); } bool NTLMPasswordSeek(unsigned char* pLMPassword, int nLMPasswordLen, int nLMPasswordNext, unsigned char* pNTLMHash, string& sNTLMPassword) { if (nLMPasswordNext == nLMPasswordLen) { unsigned char md[16]; MD4(pLMPassword, nLMPasswordLen * 2, md); if (memcmp(md, pNTLMHash, 16) == 0) { sNTLMPassword = ""; int i; for (i = 0; i < nLMPasswordLen; i++) sNTLMPassword += char(pLMPassword[i * 2]); return true; } else return false; } if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword)) return true; if ( pLMPassword[nLMPasswordNext * 2] >= 'A' && pLMPassword[nLMPasswordNext * 2] <= 'Z') { pLMPassword[nLMPasswordNext * 2] = pLMPassword[nLMPasswordNext * 2] - 'A' + 'a'; if (NTLMPasswordSeek(pLMPassword, nLMPasswordLen, nLMPasswordNext + 1, pNTLMHash, sNTLMPassword)) return true; pLMPassword[nLMPasswordNext * 2] = pLMPassword[nLMPasswordNext * 2] - 'a' + 'A'; } return false; } bool LMPasswordCorrectCase(string sLMPassword, unsigned char* pNTLMHash, string& sNTLMPassword) { if (sLMPassword.size() == 0) { sNTLMPassword = ""; return true; } unsigned char* pLMPassword = new unsigned char[sLMPassword.size() * 2]; int i; for (i = 0; i < sLMPassword.size(); i++) { pLMPassword[i * 2 ] = sLMPassword[i]; pLMPassword[i * 2 + 1] = 0x00; } bool fRet = NTLMPasswordSeek(pLMPassword, sLMPassword.size(), 0, pNTLMHash, sNTLMPassword); delete pLMPassword; return fRet; } void Usage() { Logo(); printf("usage: rcrack rainbow_table_pathname -h hash\n"); printf(" rcrack rainbow_table_pathname -l hash_list_file\n"); printf(" rcrack rainbow_table_pathname -f pwdump_file\n"); printf("rainbow_table_pathname: pathname of the rainbow table(s), wildchar(*, ?) supported\n"); printf("-h hash: use raw hash as input\n"); printf("-l hash_list_file: use hash list file as input, each hash in a line\n"); printf("-f pwdump_file: use pwdump file as input, this will handle lanmanager hash only\n"); printf("\n"); printf("example: rcrack *.rt -h 5d41402abc4b2a76b9719d911017c592\n"); printf(" rcrack *.rt -l hash.txt\n"); printf(" rcrack *.rt -f hash.txt\n"); } int main(int argc, char* argv[]) { #ifdef _WIN32 if (argc != 4) { Usage(); return 0; } string sWildCharPathName = argv[1]; string sInputType = argv[2]; string sInput = argv[3]; // vPathName vector vPathName; GetTableList(sWildCharPathName, vPathName); #else if (argc < 4) { Usage(); return 0; } string sInputType = argv[argc - 2]; string sInput = argv[argc - 1]; // vPathName vector vPathName; GetTableList(argc, argv, vPathName); #endif if (vPathName.size() == 0) { printf("no rainbow table found\n"); return 0; } // fCrackerType, vHash, vUserName, vLMHash bool fCrackerType; // true: hash cracker, false: lm cracker vector vHash; // hash cracker vector vUserName; // lm cracker vector vLMHash; // lm cracker vector vNTLMHash; // lm cracker if (sInputType == "-h") { fCrackerType = true; string sHash = sInput; if (NormalizeHash(sHash)) vHash.push_back(sHash); else printf("invalid hash: %s\n", sHash.c_str()); } else if (sInputType == "-l") { fCrackerType = true; string sPathName = sInput; vector vLine; if (ReadLinesFromFile(sPathName, vLine)) { int i; for (i = 0; i < vLine.size(); i++) { string sHash = vLine[i]; if (NormalizeHash(sHash)) vHash.push_back(sHash); else printf("invalid hash: %s\n", sHash.c_str()); } } else printf("can't open %s\n", sPathName.c_str()); } else if (sInputType == "-f") { fCrackerType = false; string sPathName = sInput; LoadLMHashFromPwdumpFile(sPathName, vUserName, vLMHash, vNTLMHash); } else { Usage(); return 0; } if (fCrackerType && vHash.size() == 0) return 0; if (!fCrackerType && vLMHash.size() == 0) return 0; // hs CHashSet hs; if (fCrackerType) { int i; for (i = 0; i < vHash.size(); i++) hs.AddHash(vHash[i]); } else { int i; for (i = 0; i < vLMHash.size(); i++) { hs.AddHash(vLMHash[i].substr(0, 16)); hs.AddHash(vLMHash[i].substr(16, 16)); } } // Run CCrackEngine ce; ce.Run(vPathName, hs); // Statistics printf("statistics\n"); printf("-------------------------------------------------------\n"); printf("plaintext found: %d of %d (%.2f%%)\n", hs.GetStatHashFound(), hs.GetStatHashTotal(), 100.0f * hs.GetStatHashFound() / hs.GetStatHashTotal()); printf("total disk access time: %.2f s\n", ce.GetStatTotalDiskAccessTime()); printf("total cryptanalysis time: %.2f s\n", ce.GetStatTotalCryptanalysisTime()); printf("total chain walk step: %d\n", ce.GetStatTotalChainWalkStep()); printf("total false alarm: %d\n", ce.GetStatTotalFalseAlarm()); printf("total chain walk step due to false alarm: %d\n", ce.GetStatTotalChainWalkStepDueToFalseAlarm()); printf("\n"); // Result printf("result\n"); printf("-------------------------------------------------------\n"); if (fCrackerType) { int i; for (i = 0; i < vHash.size(); i++) { string sPlain, sBinary; if (!hs.GetPlain(vHash[i], sPlain, sBinary)) { sPlain = ""; sBinary = ""; } printf("%s %s hex:%s\n", vHash[i].c_str(), sPlain.c_str(), sBinary.c_str()); } } else { int i; for (i = 0; i < vLMHash.size(); i++) { string sPlain1, sBinary1; bool fPart1Found = hs.GetPlain(vLMHash[i].substr(0, 16), sPlain1, sBinary1); if (!fPart1Found) { sPlain1 = ""; sBinary1 = ""; } string sPlain2, sBinary2; bool fPart2Found = hs.GetPlain(vLMHash[i].substr(16, 16), sPlain2, sBinary2); if (!fPart2Found) { sPlain2 = ""; sBinary2 = ""; } string sPlain = sPlain1 + sPlain2; string sBinary = sBinary1 + sBinary2; // Correct case if (fPart1Found && fPart2Found) { unsigned char NTLMHash[16]; int nHashLen; ParseHash(vNTLMHash[i], NTLMHash, nHashLen); if (nHashLen != 16) printf("debug: nHashLen mismatch\n"); string sNTLMPassword; if (LMPasswordCorrectCase(sPlain, NTLMHash, sNTLMPassword)) { sPlain = sNTLMPassword; sBinary = HexToStr((const unsigned char*)sNTLMPassword.c_str(), sNTLMPassword.size()); } else printf("case correction for password %s fail!\n", sPlain.c_str()); } // Display printf("%-14s %s hex:%s\n", vUserName[i].c_str(), sPlain.c_str(), sBinary.c_str()); } } return 0; }