# SPRAY fuzzes directories with masks, rule files and fingerprints

> SPRAY is a Go HTTP directory fuzzer that can build its wordlist from a mask, from a hashcat style rule file or from plain lists, fingerprint what it finds, filter the results and resume a run after an interruption. It is aimed at authorised assessment work, and the README is unusually candid about what it has not built yet.

**chainreactors/spray** — 最好用最智能最可控的目录Fuzz工具 | The most powerful, user-friendly, intelligent, and precise HTTP Fuzzer.

- Repository: https://github.com/chainreactors/spray
- Website: https://chainreactors.github.io/wiki/spray/
- Stars: 1,065 · Forks: 73
- Language: Go
- License: GPL-3.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/chainreactors-spray

## Three ways to produce the wordlist

SPRAY is a directory fuzzer, and the interesting part is that the list of paths to try is not always a list. Three generation modes are documented.

The plain mode reads directories from wordlists, and several lists can be given at once:

```bash
spray -u http://example.com -d wordlist1.txt -d wordlist2.txt
```

The mask mode generates the paths from a template, where the braces carry the pattern to expand:

```bash
spray -u http://example.com -w "/aaa/bbb{?l#4}/ccc"
```

The rule mode applies a rule file to a base wordlist, and the format follows hashcat's rule syntax for dictionary generation:

```bash
spray -u http://example.com -r rule.txt -d 1.txt
```

That third mode is the interesting one for a tester, because the hashcat rule format is a known standard and a tester who already maintains rules for password cracking can reuse the same thinking for paths. The mask mode fills the gap when the structure of the target is known in advance, and the plain mode is the fallback.

Multiple targets are handled by putting the URLs in a file, and the same rule and dictionary combination is applied to each of them:

```bash
spray -l url.txt -r rule.txt -d 1.txt
```

So the tool separates the target list from the payload generation, which is what makes a batch run over a list of hosts practical.

## Fingerprinting turns a 200 into a finding

A fuzzer that only reports status codes gives you a lot of noise, so SPRAY spends a lot of its weight on identifying what a response actually is. The project claims full fingerprint recognition from gogo, the full FingerprintHub database and wappalyzer fingerprints.

Fingerprinting is on by default in the check-only mode, which is described as similar to the single page information gathering approach of tools like ehole and httpx, with targeted performance optimisations, and it uses the templates finger library from the chainreactors templates repository:

```bash
spray -l url.txt --check-only
```

Extended fingerprinting is switched on separately with --finger, which actively probes common fingerprint directories and additionally enables the FingerprintHub and wappalyzer extension libraries:

```bash
spray -u http://example.com --finger
```

There is also a crawler, which changes the tool from a path prober into something that follows the site:

```bash
spray -u http://example.com --crawl
```

And a mode for the classic misconfiguration hunt, scanning for backup files and common files:

```bash
spray -u http://example.com --bak --common
```

Enabling all plugins at once is the -a flag. The repository root has a fingers/ directory and a templates entry alongside cmd/, core/ and pkg/, so the fingerprint data ships with the source rather than being downloaded at run time, and config.yaml holds the tool's configuration.

## Filtering is the feature the author wanted

The author's own account of why the tool exists appears in the list of similar projects. The most used directory fuzzer before spray was feroxbuster, and the reason for writing a new one was that batch scanning and filter configuration were inconvenient. Another project, httpx, contributed the idea of writing arbitrary filter conditions through a script language.

That explains the dependency list. The project requires github.com/expr-lang/expr, an expression language, and that is the mechanism behind the described dynamic intelligent filtering and custom filter strategies. The difference in approach against a plain fuzzer is that the decision about what to keep is a programmable expression rather than a fixed list of status codes and sizes, which matters when the interesting signal is a page title, a specific header, a body substring or a combination.

There is also custom information extraction with built-in rules for sensitive information, and custom output formats and content. The project describes its command line design as following Unix conventions so that it composes with other tools, and the dependency on github.com/imroc/req/v3 and github.com/valyala/fasthttp confirms that HTTP handling is done through fast libraries rather than the standard library, with mpb/v8 for progress bars and lipgloss for terminal styling.

The author also borrows from other tools openly, which is worth noting for anyone assessing provenance: dirsearch provided the default dictionary and some of the colour scheme, fuzzuli provided an idea for generating backup file lists, and the FingerprintHub and wappalyzer projects are credited as fingerprint sources.

## Resuming, batching and the problem of being blocked

A scan against a real host can be interrupted, and SPRAY handles that with a state file:

```bash
spray --resume stat.json
```

The feature list also claims multiple angles of automatic detection of being banned or judged by a WAF, which is a security tool's way of describing evasive behaviour built into the request logic. The repository dependency on refraction-networking/utls is consistent with that, since uTLS is a library for mimicking browser TLS fingerprints.

Proxy support exists, but the README flags it as not recommended and low priority, for a specific reason: the keep alive behaviour of a proxy causes severe performance degradation. That is an unusually concrete warning, and it is the kind of detail that tells you the performance claims were observed rather than assumed.

Those performance claims need to be read carefully, because they are the author's own. The README states that in local testing of extreme performance scenarios the tool can exceed ffuf and feroxbuster by more than fifty percent, that actual results depend on the network and are not as clear in practice, and that the difference is noticeable across multiple targets. It is a comparison the project makes about itself, on its own hardware, with network conditions as a stated confounder, not a benchmark result you can rely on for your own target.

## Two checklist items are still open

The TODO list in the README is a reasonable place to judge how finished this is. The completed items are the fuzzing comparison, resuming after interruption, a simple crawler, HTTP 2 support, custom recursive configuration, and automatic backup scanning of discovered directories, which is credited to feroxbuster's --collect-backups idea.

Two items are unchecked. The first is auto-tune, which would adjust the concurrency automatically, so the concurrency level is currently something you set and manage. The second is cloud function based distribution, described as a general distributed solution for the chainreactors tool chain. Both are meaningful omissions for scale. Auto-tune in particular means that a large run is either too slow or too aggressive depending on the setting you pick, and there is no built in assistance in choosing it.

The proxy item is checked but explicitly discouraged, which is a different thing from being supported well. Proxy support is there for routing, and the keep alive penalty makes it unattractive for high volume work.

For a reader deciding whether to adopt the tool, the honest summary is that single host work is well covered, batch work across many hosts is the case the author optimised for, and distributed work is not built yet.

## Building it and running the first scan

The project is built from source with the Go toolchain, and the clone needs the submodules, which is why the README uses the --recurse-submodules flag:

```bash
git clone --recurse-submodules https://github.com/chainreactors/spray

cd spray

go mod tidy

go generate

go build .
```

The go.mod requires Go 1.25.4, and the module list shows how much of the tool is shared with the rest of the author's project, with chainreactors/files, fingers, logs, neutron, proton, proxyclient, utils, parsers and words as dependencies alongside the general purpose libraries. The generate step matters because the fingerprint data and any embedded resources are produced there, and the build then produces the spray binary in the working directory. There is a .goreleaser.yml in the repository, which is how the tagged releases are packaged.

The first thing to try is the smallest possible run against a host you are authorised to test, with a short dictionary and the check-only mode, so that you can see the fingerprinting output and the filter behaviour before you point a mask or a rule file at anything. Then add the --crawl, --bak and --common options one at a time, because each of them changes how many requests the tool makes.

If you need the full reference, the README points to the wiki at chainreactors.github.io/wiki/spray/ for detailed usage.

## Conclusion

SPRAY suits a red team or security engineer doing authorised web reconnaissance who wants dictionary generation, fingerprinting and result filtering in one tool, and who is comfortable reading the wiki for the details the README leaves out. It does not suit a team that needs a distributed scanner today, because the cloud function distribution item in the TODO list is unchecked, and it is the wrong tool for any assessment performed without written authorisation for the target. Verify first that the build you use matches the go.mod requirement of Go 1.25.4, and expect to consult the wiki at chainreactors.github.io for the full filter and configuration reference.

## FAQ

### What is SPRAY and what does it do?

SPRAY is a Go HTTP fuzzer for directory discovery that can take its paths from wordlists, from a mask template, or from a hashcat style rule file applied to a base list, and then fingerprints, filters and reports the results.

### How do I build SPRAY from source?

Clone the repository with submodules, then run go mod tidy, go generate and go build . The module requires Go 1.25.4, and the generate step produces the embedded resources used by the build.

### Can SPRAY resume an interrupted scan?

Yes. The state is written to a file and a stopped run can be continued with spray --resume stat.json, and the resume feature is listed as complete in the project TODO.

### Does SPRAY support proxy connections?

Support for socks and http proxies is implemented but the README recommends against using it, noting that proxy keep alive causes severe performance degradation and that the feature is low priority.

### What is the difference between check-only mode and the full fuzzing mode?

Check-only mode gathers information about single pages in a style similar to ehole or httpx, using the templates finger library by default with targeted performance optimisations. Full mode fuzzes paths, and adding --finger additionally enables active probing of common fingerprint directories plus the FingerprintHub and wappalyzer extension libraries.

## Sources

- [chainreactors/spray on GitHub](https://github.com/chainreactors/spray)
- [License: GPL-3.0](https://github.com/chainreactors/spray/blob/master/LICENSE)
- [Project website](https://chainreactors.github.io/wiki/spray/)
- [README](https://github.com/chainreactors/spray/blob/master/README.md)
- [Releases](https://github.com/chainreactors/spray/releases)

---

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