Architecture
Navigation: Home | Plugins | LLM Analyzers | Bad Keys | Wordlists and Defaults | Configuration | Pipeline Integration
Brutus follows Go's standard project layout with a library-first, plugin-based architecture.
Directory Structure
cmd/brutus/ CLI entrypoint (flag-based)
pkg/
brutus/ Core API: Config, Result, Brute(), Plugin registry, worker pool
badkeys/ Embedded SSH bad keys with metadata and CVE tracking
keys/rapid7/ rapid7/ssh-badkeys collection (embedded via go:embed)
keys/vagrant/ HashiCorp Vagrant insecure key (embedded via go:embed)
internal/
plugins/ 22 protocol plugin implementations
ssh/ SSH password + key authentication
ftp/ FTP authentication
telnet/ Telnet authentication
http/ HTTP/HTTPS Basic Auth with banner capture
mysql/ MySQL authentication
postgresql/ PostgreSQL authentication
mssql/ Microsoft SQL Server authentication
mongodb/ MongoDB authentication
redis/ Redis AUTH command
neo4j/ Neo4j Bolt protocol authentication
cassandra/ Apache Cassandra authentication
couchdb/ CouchDB HTTP Basic Auth
elasticsearch/ Elasticsearch HTTP Basic Auth
influxdb/ InfluxDB HTTP Basic Auth
smb/ SMB/CIFS NTLM authentication
ldap/ LDAP bind authentication
rdp/ RDP NLA authentication (CGO/Rust FFI, optional)
vnc/ VNC password authentication
smtp/ SMTP authentication (LOGIN/PLAIN)
imap/ IMAP authentication
pop3/ POP3 authentication
snmp/ SNMP community string testing (tiered wordlists)
analyzers/ LLM banner analyzer implementations
claude/ Anthropic Claude API integration
deepseek/ DeepSeek API integration (OpenAI-compatible)
wordlists/ Protocol-specific default credential files (21 files)
examples/ Usage examples
basic/ Basic library usage
llm/ LLM-enabled credential testing
docs/ Documentation (ARCHITECTURE.md, COMPARISON.md, etc.)
Core Component Interfaces
Brutus defines canonical types in pkg/brutus/ that all components implement:
- Plugin -- tests credentials against a target service. Must implement
Name()andTest(). - BannerAnalyzer -- analyzes service banners via LLM to suggest default credentials.
- Config -- drives all credential testing with target, protocol, credentials, and execution parameters.
- Result -- contains the outcome of testing a single credential (success/failure/error with metadata).
All components are discovered at startup through Go's init() function registration pattern.
Key Design Decisions
- Library-first design --
pkg/brutusis importable as a Go module; the CLI is a thin wrapper - Plugin-style registration using Go
init()functions for protocols and LLM analyzers - Concurrent scanning with bounded goroutine pools via
errgroup - Three-state error semantics -- success, auth failure (Error=nil), and connection error (Error!=nil)
- Zero external dependencies -- pure Go implementations,
go:embedfor static assets, no CGO (except optional RDP) - LLM integration via pluggable analyzer registry with banner sanitization and output validation
Data Flow
1. CLI parses flags and loads configuration
2. Plugin registry resolves protocol plugin by name
3. Worker pool initializes with bounded concurrency (errgroup)
4. For each credential (username x password cartesian product):
a. Worker acquires slot from errgroup semaphore
b. Plugin.Test() attempts authentication against target
c. Plugin returns Result with success/failure/error classification
d. Result collected under mutex protection
e. If StopOnSuccess and credential valid: cancel context, stop all workers
5. Results returned to caller
6. Output formatted (human-readable table or JSON)
LLM-Enhanced Flow (HTTP protocols only)
1. Phase 1: Capture service banner with dummy credential
2. Phase 2: Check if banner matches known standard patterns
3. Phase 3: If non-standard -> send sanitized banner to LLM analyzer
4. Phase 4: LLM returns JSON array of suggested passwords (max 4)
5. Phase 5: Validate suggestions (length, character whitelist)
6. Phase 6: Test LLM suggestions first (priority), then default wordlist
Concurrency Model
Brutus uses Go's errgroup for concurrent credential testing:
// Simplified worker pool implementation
func runWorkersDefault(ctx context.Context, cfg *Config, plug Plugin) ([]Result, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(cfg.Threads) // Default: 10
var (
results []Result
mu sync.Mutex
found atomic.Bool
)
for _, cred := range credentials {
if cfg.StopOnSuccess && found.Load() {
break
}
cred := cred
g.Go(func() error {
result := plug.Test(ctx, cfg.Target, cred.username, cred.password, cfg.Timeout)
mu.Lock()
results = append(results, *result)
mu.Unlock()
if result.Success && cfg.StopOnSuccess {
found.Store(true)
cancel()
}
return nil
})
}
return results, g.Wait()
}
The default concurrency limit is 10, configurable via the -t flag.
Plugin Registration Pattern
All plugins register themselves during package initialization using Go init() functions:
// Example: SSH plugin registration (internal/plugins/ssh/ssh.go)
package ssh
import "github.com/praetorian-inc/brutus/pkg/brutus"
func init() {
brutus.Register("ssh", func() brutus.Plugin {
return &Plugin{}
})
}
type Plugin struct{}
func (p *Plugin) Name() string { return "ssh" }
func (p *Plugin) Test(ctx context.Context, target, username, password string,
timeout time.Duration) *brutus.Result {
// Implementation...
}
The aggregator file internal/plugins/init.go imports all plugin packages so their init() functions execute:
package plugins
import (
_ "github.com/praetorian-inc/brutus/internal/plugins/ssh"
_ "github.com/praetorian-inc/brutus/internal/plugins/ftp"
_ "github.com/praetorian-inc/brutus/internal/plugins/mysql"
// ... all 22 plugins
)
To add a new protocol plugin:
- Create a new package under
internal/plugins/<protocol>/. - Implement the
brutus.Plugininterface (Name()andTest()). - Register the plugin in an
init()function withbrutus.Register(). - Add the import to
internal/plugins/init.go.
Error Classification
Brutus distinguishes three outcomes for every credential test:
| Outcome | Success |
Error |
Meaning |
|---|---|---|---|
| Valid credentials | true |
nil |
Authentication succeeded |
| Invalid credentials | false |
nil |
Wrong username/password (auth failure) |
| Connection error | false |
error |
Network/timeout/protocol error |
Each plugin classifies errors based on protocol-specific patterns:
SSH: "unable to authenticate" or "permission denied" -> auth failure (nil error)
MySQL: "Access denied" -> auth failure (nil error)
FTP: "530" response code -> auth failure (nil error)
HTTP: 401/403 status -> auth failure (nil error)
All other errors -> connection error (wrapped error)
Output Formats
Human-Readable (Terminal):
[+] VALID: root:toor @ 192.168.1.100:22 (1.23s)
[-] FAILED: root:password
[-] ERROR: ubuntu:ubuntu - connection reset
Results: 1 valid, 3 failed, 1 error
JSON:
[
{"protocol":"ssh","target":"192.168.1.100:22","username":"root","password":"toor","success":true,"duration":"1.23s"},
{"protocol":"ssh","target":"192.168.1.100:22","username":"root","password":"password","success":false,"duration":"0.45s"}
]
Testing Strategy
Unit Tests:
- Per-plugin test files with mock servers
- Tests for valid credentials, invalid credentials, connection errors, and timeouts
- Core API tests for Config validation, credential generation, worker pool
Integration Tests:
- Docker Compose environment with real services (SSH, MySQL, Redis, etc.)
- End-to-end CLI tests with fingerprintx pipeline input
- LLM analyzer tests with mock HTTP servers
Build Tags:
//go:build integrationfor tests requiring live servicestesting.Short()to skip long-running tests
Navigation: Home | Plugins | LLM Analyzers | Bad Keys | Wordlists and Defaults | Configuration | Pipeline Integration