# brutespray: turning Nmap, Nessus and Nexpose output into credential tests

> brutespray is a Go credential brute-forcer that reads scan results instead of port ranges. It is convenient for authorized engagements and dangerous everywhere else.

**x90skysn3k/brutespray** — Fast, multi-protocol credential brute-forcer. Parses Nmap, Nessus, and Nexpose output to automatically test default and custom credentials across 30+ protocols.

- Repository: https://github.com/x90skysn3k/brutespray
- Website: https://pkg.go.dev/github.com/x90skysn3k/brutespray/v2
- Stars: 2,542 · Forks: 438
- Language: Go
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/x90skysn3k-brutespray

## The problem brutespray solves: a scan file full of open ports and no credentials tested

An Nmap run tells you that port 3389 is open on forty hosts. It does not tell you whether any of them answers to Administrator with a blank password. Closing that gap by hand means reading the scan, grouping hosts by service, picking a wordlist per service, and re-typing the whole thing into a tool that wants a target list rather than a scan file. brutespray exists to delete that middle step. According to the README, it "takes scan output from Nmap (GNMAP/XML), Nessus, Nexpose, JSON, and lists, then brute-forces credentials across 40+ protocols in parallel." The unit of work is the scan file, not the host.

The intended reader is a penetration tester or red teamer working under a scope document, on an engagement where a scan has already been run and the open ports are known. That framing matters, because the tool has no discovery phase. It does not sweep a subnet for live hosts or probe ports to guess services. If you hand it a CIDR it still needs a service and a port attached, as the README's own example shows: `brutespray -H ssh://10.1.1.0/24:22 -u root -p passlist.txt`. The scheme and the port are part of the target string. Anyone hoping for an all-in-one scanner will be disappointed, and that is a deliberate boundary rather than a missing feature.

## How brutespray maps a scan file onto protocol modules

The repository layout is the clearest statement of the architecture. Alongside `main.go` there are `modules/`, `brute/`, `tui/` and `wordlist/` directories. The `brute/` package holds the parsing and orchestration logic, `modules/` holds the per-protocol implementations, `wordlist/` holds the wordlists that are compiled into the binary, and `tui/` holds the terminal interface. The dependency list in `go.mod` confirms that the protocol support is real client code rather than shelling out to external binaries: `github.com/jlaffaye/ftp` for FTP, `github.com/go-redis/redis/v8` for Redis, `github.com/go-ldap/ldap/v3` for LDAP, `github.com/gosnmp/gosnmp` for SNMP, `github.com/mitchellh/go-vnc` for VNC, `github.com/hirochachacha/go-smb2` for SMB, `github.com/denisenkom/go-mssqldb` for MSSQL, `github.com/sijms/go-ora/v2` for Oracle, and `github.com/x90skysn3k/grdp` for RDP. Each service is a Go library call, not a subprocess.

Data flows in one direction. A parser reads the scan file and produces a set of host, port and service tuples. Those tuples are matched against the service list (`ssh`, `ftp`, `rdp`, `smbnt`, `mysql`, `postgres`, `redis`, `ldap`, `winrm`, `vnc`, `snmp` and the rest), and each match is dispatched to the corresponding module with the credentials you supplied. The README describes per-module settings passed as `-m KEY:VALUE`, which is how you reach module-specific knobs such as an auth type, a target path or an NTLM domain without a separate flag for every protocol. Multi-auth handling is also per module: HTTP Basic, Digest and NTLM, SMTP PLAIN and LOGIN, IMAP and POP3 SASL, and SMB pass-the-hash are all listed as supported modes. That is the part of the design that earns its keep, because the alternative is a dozen near-identical command lines.

The concurrency story is deliberately vague in the README, which advertises "dynamic threading, circuit breaker, rate limiting" under performance tuning and points to `docs/advanced.md` for the details. Treat that as an area to read the docs on rather than assume. A circuit breaker implies the tool backs off when a service starts rejecting everything, which is the behaviour you want against a host that will lock accounts, but the README does not state the thresholds.

## Installing brutespray and running it against a scan file

The README gives one-line installation through the Go toolchain. The module path is versioned, so the trailing `/v2` is part of the command.

```bash
go install github.com/x90skysn3k/brutespray/v2@latest
```

After that, `brutespray` should be on your `PATH` under `$GOPATH/bin`. The README also links release binaries and a Docker image; the repository's `Dockerfile` builds a static binary in a `golang:latest` stage and copies it into an `alpine:latest` image that runs as an unprivileged `brutespray` user with `/app` as the working directory and `./brutespray` as the entrypoint. If you build from source instead, the `Makefile` exposes `make build`, `make test`, `make lint`, `make vet` and `make clean`, and stamps the version into the binary through `-X github.com/x90skysn3k/brutespray/v2/brutespray.version`.

The first real use is a scan file plus a credential. Point it at an Nmap GNMAP file, give it a username and a password, and it will test that pair against every discovered service it recognizes.

```bash
brutespray -f nmap.gnmap -u admin -p password
```

You should see the interactive terminal UI come up, with the README describing tabbed views for Findings, live settings and pausing or resuming hosts. If you would rather confirm what the tool found before attacking anything, the README documents a dry listing mode: `-P -q` prints the discovered services from a scan file. That is the command to run first on an unfamiliar file, because it shows you exactly which hosts and services brutespray intends to touch.

For a single host without a scan file, the target syntax carries the protocol and port explicitly.

```bash
brutespray -H ssh://192.168.1.1:22 -u admin -p passlist.txt
```

A password list goes in through `-p`, and a paired credential can be passed with `-C` instead, as in `brutespray -H ssh://10.0.0.1:22 -C root:root`. The README does not show the config file contents, only that YAML config files exist for per-engagement settings and that `docs/usage.md` covers them.

## Where brutespray is the wrong tool

The failure mode that matters is authorization, and it is not a bug in the code. A tool that reads a scan file and immediately fires credentials at every service in it will happily attack a host that was scanned by someone else, or a host that was in scope for scanning but not for exploitation. The README carries no warning about this. There is no dry-run flag documented beyond `-P -q` for listing services, and no confirmation prompt described before attempts begin. If your workflow separates "scan" from "exploit" as two signed-off phases, brutespray collapses them unless you are disciplined about which file you feed it.

The second limitation is coverage. The README's own comparison table lists brutespray at 41 services against hydra at 50+, and the table carries the caveat that the symbols "reflect documented behavior at PR time" and that competing tools change quickly. If your target runs a protocol that hydra supports and brutespray does not, the feature comparison is irrelevant.

Third, the tool assumes the scan file is accurate. If Nmap reported a service on a port because of a version probe guess, brutespray will dispatch the wrong module at it. There is no documented re-probing step to confirm a service before attacking it. And because the README does not document rollback or cleanup, an account lockout caused by a spray run is yours to explain. The spray mode is described as lockout-aware with configurable delays, which helps, but the README does not state what the defaults are.

## brutespray against hydra: scan-driven versus flag-driven

hydra is the obvious alternative and the difference is structural, not a matter of which tool has more protocols. hydra takes a target and a service on the command line, one invocation per service, and you build the loop yourself. brutespray inverts that: the scan file is the input and the loop is internal. If your engagement starts with an Nmap run and you want the results of that run tested without transcription, brutespray removes a step that hydra leaves to you. If you already know exactly which host and service you care about, hydra's explicit invocation is clearer and its larger service count may cover a protocol brutespray lacks.

The README's comparison table claims a single static binary for brutespray against a non-static build for hydra, an interactive TUI that hydra lacks, checkpoint and resume that only ncrack also lists, lockout-aware spray mode, per-attempt JSONL output, SOCKS5 with proxy rotation, embedded SSH bad keys tagged with CVEs, pipeline stdin from naabu, fingerprintx or masscan, and pre-auth RDP reconnaissance for NLA and sticky keys. Some of those are genuine workflow differences, particularly resume and the pipeline input. Others are presentation. The table is the project's own summary and is explicitly time-stamped, so verify any row you are relying on against the current hydra documentation rather than taking it as settled. medusa and ncrack occupy similar ground with different trade-offs; ncrack is the one that also lists checkpoint and resume.

## Maintenance, licensing and what an upgrade costs

brutespray is MIT licensed. That is permissive: you can use it commercially, modify it and redistribute it, provided the copyright notice and permission notice are retained. It says nothing about what you may point the tool at, and it offers no warranty. This is not legal advice; if you are shipping a modified brutespray inside a product, have someone read the LICENSE file in the repository rather than this paragraph.

On maintenance, the facts are concrete. The repository is not archived, and the last push was on 2026-09-28. Releases are frequent and versioned: v2.7.2 on 2026-08-25, v2.6.3 on 2026-06-28, v2.6.2 on 2026-05-29. The README badge and the go.mod module path both say v2. The Go directive in `go.mod` is `go 1.26.1`, which is a hard floor for building from source: an older toolchain will refuse the module. The Dockerfile uses `golang:latest` for the build stage, so a container build will pull whatever Go is current at build time rather than the pinned version.

Upgrade cost is low but not zero. Because the protocol modules are Go libraries rather than external binaries, upgrading brutespray upgrades the underlying client code too, including `golang.org/x/crypto` and `golang.org/x/net`. That is convenient and it is also the reason to read the release notes before jumping versions: a bump in a crypto or networking dependency can change how a protocol negotiates. The `Makefile` runs `go test ./...`, so the project ships a test target you can run yourself after any version change.

## Conclusion

Adopt brutespray if you already produce Nmap, Nessus or Nexpose output and want credential testing driven by that file rather than by a hand-written target list; the scan-to-attempt pipeline and the checkpointing are the reasons to pick it over hydra. Do not adopt it for anything you cannot put in writing as authorized, and do not expect it to discover services on its own. Before the first run, verify three things: that the scan file is GNMAP, XML, Nessus, Nexpose, JSON or a plain list, that the credentials you pass with -u and -p are ones you are allowed to use, and that Ctrl+C followed by a resume actually works on a throwaway host, because the README documents resume as a feature but does not document what happens when the process dies mid-attempt.

## FAQ

### What scan formats can brutespray read?

The README states that it accepts Nmap GNMAP and XML, Nessus, Nexpose, JSON, and plain lists. The service listing mode, `-P -q`, prints the discovered services from a scan file without attacking anything.

### Does brutespray find open ports on its own?

No. It has no discovery phase. Every target needs a service and a port attached, as in `brutespray -H ssh://192.168.1.1:22 -u admin -p passlist.txt`, or it needs a scan file that already lists them.

### Can brutespray resume an interrupted run?

The README lists resume and checkpoint as a feature and says you interrupt with Ctrl+C and resume later, with details in docs/advanced.md. What the README does not document is the state left behind if the process is killed rather than interrupted cleanly, so test that on a throwaway host before relying on it.

### How do I install brutespray?

The README gives `go install github.com/x90skysn3k/brutespray/v2@latest`. Release binaries and a Docker image are also documented in docs/installation.md, and the repository includes a Makefile with a `build` target.

### Does brutespray support per-protocol settings?

Yes. The README describes module parameters passed as `-m KEY:VALUE`, which cover settings such as auth type, target path and NTLM domain. Multi-auth modes are listed per protocol, including HTTP Basic, Digest and NTLM, SMTP PLAIN and LOGIN, IMAP and POP3 SASL, and SMB pass-the-hash.

## Sources

- [License: MIT](https://github.com/x90skysn3k/brutespray/blob/main/LICENSE)
- [Project website](https://pkg.go.dev/github.com/x90skysn3k/brutespray/v2)
- [README](https://github.com/x90skysn3k/brutespray/blob/main/README.md)
- [Releases](https://github.com/x90skysn3k/brutespray/releases)
- [x90skysn3k/brutespray on GitHub](https://github.com/x90skysn3k/brutespray)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/x90skysn3k-brutespray
