Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 2 additions & 14 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

- uses: actions/setup-go@v7
with:
go-version: 1.24.x
go-version: 1.25.x

- name: Test
run: go test ./...
Expand All @@ -23,19 +23,7 @@ jobs:
run: go build .
working-directory: cmd/shuffledns/

# Installing massdns
- uses: actions/checkout@v7
with:
repository: blechschmidt/massdns
path: ./massdns

- name: Install massdns
working-directory: ./massdns
run: |
make all
sudo make install

# Tests
# Integration tests (native resolver; massdns binary not required)
- name: Bruteforce Mode Test
run: go run . -v -d scanme.sh -r ../../tests/resolvers.txt -w ../../tests/wordlist.txt -mode bruteforce
working-directory: cmd/shuffledns/
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v7
with:
go-version: 1.24.x
go-version: 1.25.x

- name: Checkout code
uses: actions/checkout@v7
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-binary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: 1.24.x
go-version: 1.25.x
- uses: goreleaser/goreleaser-action@v7
with:
args: "release --clean"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v7
with:
go-version: 1.24.x
go-version: 1.25.x

- name: release test
uses: goreleaser/goreleaser-action@v7
Expand Down
192 changes: 115 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
<br>
</h1>

<h4 align="center">massDNS wrapper to bruteforce and resolve the subdomains with wildcard handling support</h4>

<h4 align="center">Native high-throughput DNS bruteforce and resolve with wildcard handling</h4>

<p align="center">
<a href="https://goreportcard.com/report/github.com/projectdiscovery/shuffledns"><img src="https://goreportcard.com/badge/github.com/projectdiscovery/shuffledns"></a>
Expand All @@ -13,50 +12,59 @@
<a href="https://twitter.com/pdiscoveryio"><img src="https://img.shields.io/twitter/follow/pdiscoveryio.svg?logo=twitter"></a>
<a href="https://discord.gg/projectdiscovery"><img src="https://img.shields.io/discord/695645237418131507.svg?logo=discord"></a>
</p>

<p align="center">
<a href="#features">Feature</a> •
<a href="#installation-instructions">Install</a> •
<a href="#features">Features</a> •
<a href="#installation">Install</a> •
<a href="#usage">Usage</a> •
<a href="#running-shuffledns">Run</a> •
<a href="#handling-wildcards">Wildcard</a> •
<a href="#license">License</a> •
<a href="https://discord.gg/projectdiscovery">Discord</a>
<a href="#handling-wildcards">Wildcards</a> •
<a href="#massdns-parity">massdns parity</a> •
<a href="#license">License</a>
</p>

---

`shuffledns` bruteforces and resolves subdomains with multi-level wildcard filtering. Resolution is done by a **native Go stub resolver** (massdns-compatible design and output). An optional **iterative** mode walks from the DNS roots and needs no public resolver list.

`shuffleDNS` is a wrapper around `massdns`, written in go, that allows you to enumerate valid subdomains using active bruteforce, as well as resolve subdomains with wildcard handling and easy input-output support.

Based on the work on `massdns` project by [@blechschmidt](https://github.com/blechschmidt).
Inspired by [massdns](https://github.com/blechschmidt/massdns) by [@blechschmidt](https://github.com/blechschmidt). The massdns binary is **not required**.

# Features
# Features

<h1 align="left">
<img src="static/shuffledns-run.png" alt="shuffledns" width="700px">
<br>
</h1>

- Simple and modular code base making it easy to contribute.
- Fast And Simple active subdomain scanning.
- Handles wildcard subdomains in a smart manner.
- Optimized for **ease of use**
- **Stdin** and **stdout** support for integrating in workflows
- Native high-throughput resolver (no massdns binary)
- Bruteforce, resolve, and filter modes with stdin/stdout
- Multi-level wildcard handling
- Optional iterative resolution from the DNS roots (`-it`)
- Resolver health scoring, adaptive concurrency, Linux sendmmsg/recvmmsg batching
- Shard / resume for distributed or long runs

# Installation

`go1.24+` required:

```bash
go install -v github.com/projectdiscovery/shuffledns/cmd/shuffledns@latest
```

You still need a resolver list for the default stub mode (e.g. from [dnsvalidator](https://github.com/vortexau/dnsvalidator)) unless you use `-it` / `--iterative`.

# Usage

```bash
shuffledns -h
```
This will display help for the tool. Here are all the switches it supports.

```yaml
shuffleDNS is a wrapper around massdns written in go that allows you to enumerate valid subdomains using active bruteforce as well as resolve subdomains with wildcard handling and easy input-output support.
shuffleDNS is a high-throughput DNS bruteforcer and resolver with wildcard handling. It uses a native Go stub resolver (massdns-compatible) and optional iterative resolution from the DNS roots.

Usage:
./shuffledns [flags]

Flags:
Flags:
INPUT:
-d, -domain string[] Domain to find or resolve subdomains for
Expand All @@ -65,30 +73,47 @@ INPUT:
-w, -wordlist string File containing words to bruteforce for domain
-r, -resolver string File containing list of resolvers for enumeration
-tr, -trusted-resolver string File containing list of trusted resolvers
-ri, -raw-input string Validate raw full massdns output
-ri, -raw-input string Filter wildcards from an existing massdns-format output file
-mode string Execution mode (bruteforce, resolve, filter)

RATE-LIMIT:
-t int Number of concurrent massdns resolves (default 10000)

UPDATE:
-up, -update update shuffledns to latest version
-duc, -disable-update-check disable automatic shuffledns update check
-t int Max concurrent in-flight DNS queries (default 10000)
-qps int Max outbound DNS queries per second (0 = unlimited)

OUTPUT:
-o, -output string File to write output to (optional)
-j, -json Make output format as ndjson
-wo, -wildcard-output string Dump wildcard ips to output file

CONFIGURATIONS:
-m, -massdns string Path to the massdns binary
-mcmd, -massdns-cmd string Optional massdns commands to run (example '-i 10')
-m, -massdns string Deprecated (ignored): massdns binary is not used
-mcmd, -massdns-cmd string Deprecated (ignored)
-directory string Temporary directory for enumeration

OPTIMIZATIONS:
-retries int Number of retries for dns enumeration (default 5)
-sw, -strict-wildcard Perform wildcard check on all found subdomains
-wt int Number of concurrent wildcard checks (default 250)
-retries int Number of retries for dns enumeration (default 5)
-sw, -strict-wildcard Perform wildcard check on all found subdomains
-wt int Number of concurrent wildcard checks (default 250)
-filter-internal-ips Filter out internal/private IP addresses

RESOLVER:
-rt, -type string DNS record type (A, AAAA, CNAME, NS, PTR, MX, TXT, SOA)
-bm, -batch-mode string sendmmsg/recvmmsg: off | on | adaptive (Linux, default off)
-sc, -socket-count int UDP sockets per run (0 = scale to cores)
-udp-size int EDNS0 UDP payload size (0 = 1232)
-norecurse Send non-recursive queries (RD=0)
-sticky Do not rotate resolver on retry
-rhz, -resolver-health De-weight failing resolvers
-acy, -adaptive-concurrency Adapt in-flight concurrency to packet loss
-cc, -cross-check Re-verify positive answers on a second resolver
-ei, -extended-input Parse 'name [resolver ...]' input lines
-no-verify-ip Disable reply source-IP verification
-no-tcp-fallback Disable TCP fallback on truncated answers
-it, -iterative Recurse from root servers (no -r needed)

DISTRIBUTED:
-shard string Process only shard m of n (e.g. 2/8)
-rs, -resume string Checkpoint file for crash-safe stop/resume

DEBUG:
-silent Show only subdomains in output
Expand All @@ -97,83 +122,96 @@ DEBUG:
-nc, -no-color Don't Use colors in output
```

<table>
<tr>
<td>

## Prerequisite
# Running shuffledns

`shuffledns` requires `massdns` to be installed in order to perform its operations. You can see the installation instructions at [massdns project](https://github.com/blechschmidt/massdns#compilation). If you place the binary in `/usr/bin/massdns` or `/usr/local/bin/massdns`, the tool will auto-detect the presence of the binary and use it. On Windows, you need to supply the path to the binary for the tool to work.
### Resolve

The tool also needs a list of valid resolvers. The [dnsvalidator](https://github.com/vortexau/dnsvalidator) project can be used to generate these lists. You also need to provide wordlist, you can use a custom wordlist or use the [commonspeak2-wordlist](https://wordlists-cdn.assetnote.io/data/manual/best-dns-wordlist.txt).

</td>
</tr>
</table>
```bash
shuffledns -d example.com -list example-subdomains.txt -r resolvers.txt -mode resolve
```

## Installation Instructions
```bash
subfinder -d example.com | shuffledns -d example.com -r resolvers.txt -mode resolve
```

`shuffledns` requires `go1.24+` to install successfully. Run the following command to install the latest version:
### Bruteforce

```bash
go install -v github.com/projectdiscovery/shuffledns/cmd/shuffledns@latest
shuffledns -d hackerone.com -w wordlist.txt -r resolvers.txt -mode bruteforce
```

## Running shuffledns
```bash
echo hackerone.com | shuffledns -w wordlist.txt -r resolvers.txt -mode bruteforce
```

`shuffledns` supports two types of operations:
### Iterative (no public resolvers)

<ins>**Subdomain resolving**</ins>
```bash
shuffledns -d example.com -w wordlist.txt -mode bruteforce -it
```

To resolve a list of subdomains, you can pass the list of subdomains via the `-list` option.
### Filter existing massdns-format output

```bash
shuffledns -d example.com -list example-subdomains.txt -r resolvers.txt -mode resolve
shuffledns -d example.com -ri massdns-output.txt -mode filter
```

This will run the tool against subdomains in `example-subdomains.txt` and returns the results. The tool uses the resolvers specified with `-r` flag to do the resolving.
### Tuning

You can also pass the list of subdomains at standard input (STDIN). This allows for easy integration in automation pipelines.
- `-t` caps **in-flight** queries (massdns `-s`). More concurrency does not help once resolvers or RTT are the limit.
- `-qps` caps **send rate** when you need to stay under resolver/abuse limits.
- `-bm adaptive` helps on high-RTT / bursty paths; leave `off` on LAN/low latency (default).
- `-rhz` / `-acy` help when public resolvers drop or rate-limit.

```bash
subfinder -d example.com | shuffledns -d example.com -r resolvers.txt -mode resolve
```
Live QPS is roughly `min(-t / RTT, -qps, resolver capacity)`. For loopback and public-resolver head-to-heads see [`bench/`](bench/).

This uses the subdomains found passively by `subfinder` and resolves them with `shuffledns` returning only the unique and valid subdomains.
# Handling Wildcards

<ins>**Subdomain Bruteforcing**</ins>
`shuffledns` tracks how many names map to each IP. Past a small threshold it walks hostname labels for that IP and filters wildcard answers with few extra DNS requests. Wildcard filtering requires `-d` / domain input.

`shuffledns` also supports bruteforce of a target with a given wordlist. You can use the `w` flag to pass a wordlist which will be used to generate permutations that will be resolved using massdns.
# massdns parity

```bash
shuffledns -d hackerone.com -w wordlist.txt -r resolvers.txt -mode bruteforce
```
`go run ./cmd/resolve` aims to be a **massdns CLI drop-in** for common stub workloads:

| Area | Status |
|---|---|
| Stub resolve (`-s/-c/-i/-t/-r`, sticky, norecurse, verify-ip, extended-input, socket-count) | Yes |
| Output `-o` S / F / L / J / **B** (+ modifiers) | Yes (`pkg/output`) |
| `--bindto`, `--rcvbuf`, `--sndbuf`, `--predictable`, `--flush`, `--filter/--ignore/--retry` | Yes |
| `--status-format`, `-q`, `-l` | Yes |
| PTR / validate / AXFR / NSEC(3) / iterative / shard / resume | Yes (native extras) |
| `--drop-user` / `--drop-group` / `--root` | Yes (Unix; after sockets open) |
| `--rand-src-ipv6` / `--rand-src-ipv6-file` | Yes (Linux + `CAP_NET_RAW`; IPv6 resolvers; not with `--bindto`) |
| `--processes`, `--busy-poll` | Accepted, **ignored** |

This will run the tool against `hackerone.com` with the wordlist `wordlist.txt`. The domain bruteforce can also be done with standard input as in previous example for resolving the subdomains.
**shuffledns** itself is not a massdns replacement (hostname list + wildcards). Use `cmd/resolve` when you need massdns-compatible output and flags.

Example:

```bash
echo hackerone.com | shuffledns -w wordlist.txt -r resolvers.txt -mode bruteforce
go run ./cmd/resolve -r resolvers.txt -t AAAA -o Snl -w out.txt names.txt
# same shape as: massdns -r resolvers.txt -t AAAA -o Snl -w out.txt names.txt
```

---

<table>
<tr>
<td>
# Throughput

## Handling Wildcards
Local loopback bench (`RESOLVE_BENCH=1 go test ./pkg/resolve -run TestResolverBenchmark`, 50k names, 8 simulated resolvers, `-t 10000`):

A special feature of `shuffleDNS` is its ability to handle multi-level DNS based wildcards, and do it so with a very reduced number of DNS requests. Sometimes all the subdomains would resolve, leading to lots of garbage in the results. The way `shuffleDNS` handles this is by keeping track of how many subdomains point to an IP, and if the number of subdomains increase beyond a certain small threshold, it checks for wildcard on all the levels of the hosts for that IP iteratively.
| scenario | ~qps |
|---|---|
| lan-fast (~0.5ms) | ~90k |
| wan-typical (15±10ms, 0.5% loss) | ~40k |
| wan-lossy (25±20ms, 5% loss) | ~27k |
| rate-limited (3k qps/resolver) | ~40k |

</td>
</tr>
</table>
Compare against the massdns binary with [`bench/`](bench/) (Docker). Numbers are workload- and resolver-bound; public resolver lists will land closer to the wan/rate-limited rows than lan-fast.

### Notes
# Notes

- Wildcard filter feature works with domain (`-d`) input only.
- Resolving or Brute-forcing only one operation can be done at a time.
- Resolving and bruteforcing are separate modes (`-mode`).
- `-m` / `-mcmd` / `-retain-stderr` / `-batch-size` are accepted for compatibility and ignored.
- README usage dump may lag slightly behind `-h` as flags evolve.

### License

`shuffledns` is distributed under [GPL v3 License](https://github.com/projectdiscovery/shuffledns/blob/main/LICENSE.md)
`shuffledns` is distributed under [GPL v3 License](https://github.com/projectdiscovery/shuffledns/blob/main/LICENSE.md).
35 changes: 35 additions & 0 deletions bench/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Benchmark image: builds massdns from source and the dnsbench harness, then
# benchmarks the native resolver against massdns using a battery of loopback
# resolvers. No DNS traffic leaves the container (resolvers are 127.0.0.1).
#
# Build (context must be the repository root):
# docker build -f bench/Dockerfile -t shuffledns-dnsbench .
#
# Run:
# docker run --rm shuffledns-dnsbench -names 200000 -resolvers 16 -hit 5
#
# The container needs no network at runtime; everything is loopback.

FROM golang:1.24-bookworm AS build

# --- build massdns from source -------------------------------------------------
RUN apt-get update \
&& apt-get install -y --no-install-recommends git ca-certificates gcc make libc6-dev \
&& rm -rf /var/lib/apt/lists/*
RUN git clone --depth 1 https://github.com/blechschmidt/massdns.git /opt/massdns \
&& make -C /opt/massdns

# --- build the dnsbench harness ------------------------------------------------
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /usr/local/bin/dnsbench ./cmd/dnsbench

# --- runtime image -------------------------------------------------------------
FROM debian:bookworm-slim
COPY --from=build /opt/massdns/bin/massdns /usr/local/bin/massdns
COPY --from=build /usr/local/bin/dnsbench /usr/local/bin/dnsbench

ENTRYPOINT ["/usr/local/bin/dnsbench", "-massdns", "/usr/local/bin/massdns"]
CMD ["-names", "100000", "-resolvers", "8", "-hit", "5"]
Loading
Loading