ZGrab2: application-layer scanning with full handshake transcripts
Fast Application Layer Scanner
At a glance
- What is it?
- The ZMap project's modular Go scanner for follow-up L7 handshakes across 29 protocols, built to sit behind ZMap rather than replace it. Transcripts, not just banners, and a first 1.0 release in December 2025.
- Who is it for?
- ZGrab2 is the right tool when you already have a set of responsive hosts and need to know what protocol answers on them, with the exchange recorded well enough to analyse later. It is the wrong tool if you need host discovery, because ZMap does the L4 work, or if you need a penetration test, because the project's own rule is that application handshakes are aborted before authentication is attempted and contributions aimed at gaining access are not accepted.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Built to sit behind ZMap, not to compete with it
The README's first paragraph sets the division of labour: ZMap identifies L4 responsive hosts and ZGrab performs the in-depth follow-up L7 handshakes. That framing answers the first question anyone has about this tool, which is why you would reach for something like it when Nmap already exists.
The distinguishing feature is stated in the same paragraph. Unlike many other network scanners, ZGrab outputs detailed transcripts of network handshakes, for example all messages exchanged in a TLS handshake, for offline analysis. A banner grabber gives you one string per host. A transcript gives you the whole exchange, which is what makes it possible to fingerprint implementations and versions from the way they answered rather than only what they said.
The protocol list is broad: AMQP, BACnet, Banner, DNP3, Fox, FTP, HTTP, IMAP, IPP, JARM, ManageSieve, Memcached, Modbus, MongoDB, MQTT, MSSQL, MySQL, NTP, Oracle, POP3, PostgreSQL, PPTP, Redis, Siemens, SMB, SMTP, SOCKS5, SSH, Telnet and TLS. Industrial protocols sitting next to database protocols is a fair signal of who this is aimed at, since BACnet, DNP3, Modbus and Siemens are not what a general-purpose scanner audience usually reaches for.
The repository also carries an explicit note on ethical scanning: ZGrab only collects information available to any standard application client without authenticating, contributions that attempt to gain access through vulnerabilities or brute-forcing credentials are not accepted, and application handshakes are always aborted before authentication is attempted.
Build from source, because the README recommends it
The README recommends installing from source to get the latest version, and the sequence is a clone, a make, and a help call:
git clone https://github.com/zmap/zgrab2.git
cd zgrab2
make
./zgrab2 http --help # to see the http module's help messageThat leaves a `zgrab2` binary in the current directory, and `make install` puts it somewhere on your `PATH` instead. The troubleshooting note is the familiar one: Go may put the binary in `$GOPATH/bin`, which is often not on your `PATH`, so `command not found: zgrab2` means your `PATH` needs `export PATH=$PATH:$GOPATH/bin` and a shell reload.
The prerequisite line says to install Go 1.23 or later. The repository itself asks for more than that. `go.mod` declares `go 1.25.0` with an explicit `toolchain go1.25.8`, and the Dockerfile builds against an `ARG GO_VERSION=1.25` alpine golang image. So the README's stated floor is the minimum that could conceivably work and the module file is the real requirement. Building with 1.23 will fail on the toolchain directive, or prompt a download if your Go installation allows automatic toolchain switching.
The Docker route avoids the question entirely and gives you a smaller image, since the runtime stage copies just the compiled binary and a blocklist config:
echo 'example.com' | docker run --rm -i ghcr.io/zmap/zgrab2 httpFor multiple modules, the README mounts a configuration file into the container and invokes the `multiple` module against it.
Input is four CSV fields, and the port can be overridden per line
Targets arrive on stdin or in a file, one per line, in CSV with up to four fields:
IP, DOMAIN, TAG, PORTEach line needs an `IP`, a `DOMAIN`, or both. Give only a domain and the scanner resolves it. Give both and it connects to the `IP` but uses the `DOMAIN` where the protocol expects a name, which means the HTTP `HOST` header and the TLS SNI extension. That separation is the feature that makes this usable against a CDN or a load balancer, where connecting by name but by address is the whole point.
A CIDR block in the `IP` field expands to one target per address in the block. The `TAG` field pairs with the `--trigger` argument, and `PORT` is a per-line override for the `-p`/`--port` option. Unused fields can be blank and trailing ones omitted entirely, so `10.0.0.1, , , 5678` and `192.168.0.0/24, , tag` are both valid. For backwards compatibility a one-field line is read as a `DOMAIN`.
A worked example shows how the pieces combine:
echo "en.wikipedia.org" | ./zgrab2 http --max-redirects=1 --endpoint="/wiki/New_York_City"And the simplest possible invocation, which is also the fastest way to see whether a module works, is a hostname piped into a module name:
echo "pool.ntp.org" | zgrab2 ntpThe README shows the JSON that comes back, with a `status`, a `protocol`, a `port`, a nested `result` holding the NTP version and time, plus timestamps, followed by a summary line reporting targets scanned, throughput and success rate. That output shape is JSON Lines with a per-target record plus a status line, which is what makes it pipeable into jq or straight into a storage layer.
Multiple modules need an ini file, and section names have to match exactly
Running more than one module means using the `multiple` module with an ini configuration. The README's example runs two HTTP passes on different ports plus SSH:
[Application Options]
output-file="output.txt"
input-file="input.txt"
[http]
name="http80"
port=80
endpoint="/"
[http]
name="http8080"
port=8080
endpoint="/"
[ssh]
port=22./zgrab2 multiple -c multiple.iniThree rules govern the file, and the first release's changelog shows they were recently a source of bugs. `Application Options` must be the initial section. Other section names must correspond exactly to the module name. And if the same module appears more than once, `name` must be set and unique, which is why the two HTTP sections above carry `http80` and `http8080` rather than relying on the default.
That last point connects to a fix in v1.0.0: Application Options multiple module parsing was corrected, and the CLI flags were grouped so module `--help` text became descriptive. If you have an old configuration that parsed before, the rules are the same but the error reporting around them is better now.
Single module usage keeps the flag order rule simple: module-specific options come after the module name, application-wide options can go anywhere. `./zgrab2 [module] -h` shows both sets along with usage examples.
What 1.0 actually changed, and what the license question leaves open
The release list is short and recent: `v1.0.0` on 2025-12-04, preceded by `v1.0.0-RC3` and `v1.0.0-RC2`. The v1.0.0 notes state that the module and framework API had stabilised enough to justify a first major release, which is the useful thing to know if you are writing a module or automating this in CI.
The individual changes read like a pre-1.0 cleanup. A memcached module was added. DNS and per-IP target rate limits were implemented, which matters a great deal for large surveys where you must not hammer a resolver. Postgres scans now retry without TLS when the TLS attempt errors. A `SkipHost` request option was added to the HTTP module and a custom read timeout flag to the siemens module. One entry is a nice detail: a test that used cloudflare.com was replaced with a working domain, which tells you the test suite touches the live internet.
Prometheus client support is a dependency in `go.mod`, so metrics are part of the build rather than an afterthought. The tree also contains `integration_tests/`, several `_test.go` files beside the source, and a `bin/` directory.
On licensing, the situation is genuinely ambiguous and worth naming. GitHub reports no recognised license identifier for this repository, while a `LICENSE` file is present at the root of the tree. Both statements hold at once and neither tells you the terms. If you intend to use ZGrab2 inside a commercial product rather than running it as a measurement tool, read that file rather than the repository metadata.
There is also a small Python package in the repository that is easy to miss: `setup.py` publishes `zgrab2_schemas`, version 0.0.1, described as schema definitions for ZGrab2's JSON output, and `requirements.txt` pulls in zschema plus zcrypto schemas from a pinned Git commit. The `zgrab2_schemas/` directory at the root is what it packages, which is how you validate output against its expected shape.
Editorial conclusion
ZGrab2 is the right tool when you already have a set of responsive hosts and need to know what protocol answers on them, with the exchange recorded well enough to analyse later. It is the wrong tool if you need host discovery, because ZMap does the L4 work, or if you need a penetration test, because the project's own rule is that application handshakes are aborted before authentication is attempted and contributions aimed at gaining access are not accepted. Verify two things. First, that v1.0.0 is the tag you want, since it is the first release where the module and framework API were considered stable, published 2025-12-04 after two release candidates. Second, which Go version you build with: the README asks for 1.23 or later while `go.mod` declares 1.25.0 with a 1.25.8 toolchain and the Dockerfile defaults to a 1.25 base image.
Frequently asked questions
What is a ZGrab scanner?
ZGrab is a modular application-layer network scanner for large Internet-wide surveys. ZMap finds L4 responsive hosts and ZGrab then performs the follow-up L7 handshakes, recording full transcripts of the exchange rather than only a banner, so the data can be analysed offline.
How do I build ZGrab2 from source?
Clone the repository, run `make`, and the binary lands in the current directory; `make install` puts it on your PATH instead. The README asks for Go 1.23 or later, but go.mod declares Go 1.25.0 with a 1.25.8 toolchain, so build with 1.25 to match the module.
Which protocols does ZGrab2 support?
Twenty-nine modules, including AMQP, BACnet, DNP3, FTP, HTTP, IMAP, JARM, Memcached, Modbus, MongoDB, MQTT, MSSQL, MySQL, NTP, Oracle, POP3, PostgreSQL, Redis, Siemens, SMB, SMTP, SSH, Telnet and TLS among others. The memcached module was added in the v1.0.0 release.
Can ZGrab2 log in to the services it scans?
No, by design. The README states that ZGrab only collects information available to any standard application client without authenticating, and that application handshakes are always aborted before authentication is attempted. Contributions aimed at gaining access through exploits or credential brute-forcing are not accepted.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/zmap-zgrab2)