1 Architecture
nsportsman edited this page 2026-02-12 14:17:42 -06:00

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() and Test().
  • 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/brutus is 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:embed for 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:

  1. Create a new package under internal/plugins/<protocol>/.
  2. Implement the brutus.Plugin interface (Name() and Test()).
  3. Register the plugin in an init() function with brutus.Register().
  4. 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 integration for tests requiring live services
  • testing.Short() to skip long-running tests

Navigation: Home | Plugins | LLM Analyzers | Bad Keys | Wordlists and Defaults | Configuration | Pipeline Integration