diff --git a/cmd/kerbrute.go b/cmd/kerbrute.go index cc7683b..f477fd0 100644 --- a/cmd/kerbrute.go +++ b/cmd/kerbrute.go @@ -28,6 +28,7 @@ func init() { rootCmd.PersistentFlags().StringVarP(&logFileName, "output", "o", "", "File to write logs to. Optional.") rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Log failures and errors") rootCmd.PersistentFlags().BoolVar(&safe, "safe", false, "Safe mode. Will abort if any user comes back as locked out. Default: FALSE") + rootCmd.PersistentFlags().IntVarP(&semiSafe, "semisafe", "", 0, "Semi-Safe mode. Will abort if more than N accounts are locked out. 0 (default) to disable") rootCmd.PersistentFlags().IntVarP(&threads, "threads", "t", 10, "Threads to use") rootCmd.PersistentFlags().IntVarP(&delay, "delay", "", 0, "Delay in millisecond between each attempt. Will always use single thread if set") rootCmd.PersistentFlags().BoolVar(&downgrade, "downgrade", false, "Force downgraded encryption type (arcfour-hmac-md5)") diff --git a/cmd/passwordspray.go b/cmd/passwordspray.go index b016abb..7f0e025 100644 --- a/cmd/passwordspray.go +++ b/cmd/passwordspray.go @@ -68,7 +68,6 @@ func passwordSpray(cmd *cobra.Command, args []string) { } else { scanner = bufio.NewScanner(os.Stdin) } - for i := 0; i < threads; i++ { go makeSprayWorker(ctx, usersChan, &wg, password, userAsPass) diff --git a/cmd/setup.go b/cmd/setup.go index 85c8f51..6d6bf9d 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -15,16 +15,17 @@ var ( logFileName string verbose bool safe bool + semiSafe int delay int threads int stopOnSuccess bool userAsPass = false - downgrade bool + downgrade bool hashFileName string - logger util.Logger - kSession session.KerbruteSession + logger util.Logger + kSession session.KerbruteSession // Used for multithreading ctx, cancel = context.WithCancel(context.Background()) @@ -39,8 +40,9 @@ func setupSession(cmd *cobra.Command, args []string) { DomainController: domainController, Verbose: verbose, SafeMode: safe, + SemiSafeMode: semiSafe, HashFilename: hashFileName, - Downgrade: downgrade, + Downgrade: downgrade, } k, err := session.NewKerbruteSession(kOptions) if err != nil { diff --git a/session/errors.go b/session/errors.go index dda9567..d4e8aa1 100644 --- a/session/errors.go +++ b/session/errors.go @@ -3,8 +3,11 @@ package session import ( "fmt" "strings" + "sync/atomic" ) +var LOCKOUTS int64 = 0 + func (k KerbruteSession) HandleKerbError(err error) (bool, string) { eString := err.Error() @@ -32,6 +35,11 @@ func (k KerbruteSession) HandleKerbError(err error) (bool, string) { if strings.Contains(eString, "KDC_ERR_CLIENT_REVOKED") { if k.SafeMode { return false, "USER LOCKED OUT and safe mode on! Aborting..." + } else if k.SemiSafeMode > 0 { + var _lockout int64 = atomic.AddInt64(&LOCKOUTS, 1) + if _lockout >= int64(k.SemiSafeMode) { + return false, "TOO MANY USERS LOCKED OUT and Semi-Safe mode on! Aborting..." + } } return true, "USER LOCKED OUT" } diff --git a/session/session.go b/session/session.go index af7d530..42a05ec 100644 --- a/session/session.go +++ b/session/session.go @@ -36,18 +36,20 @@ type KerbruteSession struct { Config *kconfig.Config Verbose bool SafeMode bool - HashFile *os.File - Logger *util.Logger + SemiSafeMode int + HashFile *os.File + Logger *util.Logger } type KerbruteSessionOptions struct { - Domain string + Domain string DomainController string - Verbose bool - SafeMode bool - Downgrade bool - HashFilename string - logger *util.Logger + Verbose bool + SafeMode bool + SemiSafeMode int + Downgrade bool + HashFilename string + logger *util.Logger } func NewKerbruteSession(options KerbruteSessionOptions) (k KerbruteSession, err error) { @@ -92,7 +94,8 @@ func NewKerbruteSession(options KerbruteSessionOptions) (k KerbruteSession, err Config: Config, Verbose: options.Verbose, SafeMode: options.SafeMode, - HashFile: hashFile, + SemiSafeMode: options.SemiSafeMode, + HashFile: hashFile, Logger: options.logger, } return k, err