Getting Started Guide - zmap/zmap GitHub Wiki

Learn best by doing? Want some guided examples to walk through basic scanning with ZMap to get your feet wet? This is the place to start! The ZMap team created this guide to help newcomers get accustomed to scanning, but if something is still unclear feel free to ask in our Github Discussions.

We'll cover:

  • Installing ZMap
  • Running your First Scan
  • Directing Output
  • Performance
  • Other Scan Types
  • Default Blocked Subnets
  • Further Resources

Installation

ZMap is available through many common package managers, or you can compile from source. Full instructions are available here. To check that you've installed everything correctly, run with zmap --version. We recommend using version 4.1.0-RC2 currently.

⚠️ Warning on Scanning Rate

By default, ZMap will scan as fast as your network card allows. Since ZMap directly composes Ethernet frames, it doesn't have any concept of congestion control (as you'd get by default with TCP). This has two potential pitfalls:

  • Target Network DoS: If you scan a small subnet (e.g., a single organization) at a very high rate, you can easily accidentally DOS the destination network. We recommend not running scans at 1Gbps or faster unless you're scanning the full Internet. You'll have more success running at a rate closer under 10Mbps if you're scanning a single network.

  • Source Network overload: Regardless of how many hosts you are scanning, you also risk the possibility of overloading your source network (i.e., the network you're using to scan the Internet). Many pieces of network equipment that advertise only throughput numbers (e.g., 1Gbps) cannot handle that full speed using minimum sized packets (since this requires handling the theoretical maximum packet per second rate of the equipment.)

To reduce the impact on destination networks, ZMap scans in-scope IP addresses in a random order. This means the load in a given time period placed on a target subnet depends both on ZMap's rate of scanning and the size of the target range.

target_subnet_load

In the image above, the different subnets being scanned are represented by colored lines. So a "good" scanning rate can be faster if the target scanned is larger, since the load is more spread out.

Not only is this about being a altruistic internet citizen, overloading a target network can lead to:

  1. Network administrators blocking your IP

  2. Routers dropping your scan packets

  3. Depriving other LAN users of internet bandwidth

#1 and #2 will both lead to inaccurate scan results since there's no way to differentiate if no response to a scan is due to no host being present, a firewall blocking your IP, or a dropped packet en-route. More details available in the Increasing Scan Accuracy section.

We've listed what we feel are sane bandwidth/probe throttling parameters in the given examples based on the size of the target scanned, but a good rule of thumb is to scan at the slowest rate your specific application requires.

⚠️ Warning on Threads

The ZMap CLI offers the ability to set the number of sending threads with -T number_of_threads. A thread for receiving packets and for monitoring scan progress are also created, so the number of threads ZMap requires is 2 + T where T is the number of threads you set (4 by default unless the host has < 4 cores).

As a general rule, we recommend sticking to <= 8 sending threads, since we've found performance plateaus after this point. Additionally, using very large thread counts (> 32) will not increase scan performance and could lead to you encountering packet sending issues or encountering packet drop leading to inaccurate scan results.

To be clear, there is zero benefit in creating more threads than you have cores on your machine. The sending threads are all sending as fast as possible and increasing threads over core count will lead to core contention.

[!TIP] As with scanning rate, we recommend starting with less and increasing as needed to ensure your scans are accurate and you minimize host and network strain.

Running Your First Scan

Let's start your first scan. We're going to send a TCP SYN packet to all IP's in the subnet 171.67.70.0/23 on port 80 with a packet send rate of 128 packets per second. If there's anything running on that IP/Port pair, we should get a SYN ACK packet in reply.

Run the following command:

sudo zmap -p 80 -r 128 171.67.70.0/23

You should get some output similar to:

171.67.71.204
171.67.71.128
171.67.70.245
171.67.71.191
171.67.71.186
171.67.71.202
171.67.70.240
171.67.70.242
171.67.71.192
 0:05 42% (7s left); send: 512 done (127 p/s avg); recv: 91 17 p/s (17 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.77%
 0:06 50% (6s left); send: 512 done (127 p/s avg); recv: 91 0 p/s (15 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.77%
 0:07 59% (5s left); send: 512 done (127 p/s avg); recv: 91 0 p/s (12 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.77%
 0:08 67% (4s left); send: 512 done (127 p/s avg); recv: 91 0 p/s (11 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.77%
 0:09 75% (3s left); send: 512 done (127 p/s avg); recv: 91 0 p/s (10 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.77%
 0:10 84% (2s left); send: 512 done (127 p/s avg); recv: 91 0 p/s (9 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.77%
 0:11 92% (1s left); send: 512 done (127 p/s avg); recv: 91 0 p/s (8 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.77%
Dec 05 22:29:12.352 [INFO] zmap: completed

Troubleshooting

ZMap is pretty good about telling you about any issues it encounters. Some common ones include:

  1. Your user may not have permission to capture traffic on the default interface unless you run with sudo.
  2. You may need to specify your MAC address if there are multiple network adapters to choose from. If you see something to that effect, use the flag --source-mac=XX:XX:XX:XX:XX:XX with any ZMap command you run.
  3. The first time you run ZMap, ZMap may mention zmap: could not detect GW MAC address.... Follow its direction to run the command curl zmap.io and that should initialize the GW MAC address so ZMap can route traffic correctly.

If you followed the instructions, are on a supported OS, and are still having issues, please reach out to us on Github Discussions!

We can see 2 types of output here:

  1. IP's that responded to our SYN packet
  2. Detailed scan status printed every second (% complete, seconds left, send/receive rate,...)

We did say "output similar to" and that brings up a key point, your results may vary slightly. Internet-reachable hosts can go on/offline in-between scans and your hardware might be faster/slower than what we're running on.

It's nice to have all output in one screen, but you might want to separate IP/Port hits and status info.

Output

The -o or --output-file flag allows us to specify where we'd like IP/Port pair hits to go. Try using the -o flag like this:

sudo zmap -p 80 -o "output.csv" -r 128 171.67.70.0/23

You should get output similar to:

Dec 05 21:52:43.347 [INFO] zmap: By default, ZMap will output the unique IP addresses of hosts that respond successfully (e.g., SYN-ACK packet). This is equivalent to running ZMap with the following flags: --output-module=csv --output-fields=saddr --output-filter='success=1 && repeat=0' --no-header-row. If you want all responses, explicitly set an output module or set --output-filter="".
Dec 05 21:52:43.348 [INFO] dedup: Response deduplication method is full
Dec 05 21:52:43.406 [INFO] recv: duplicate responses will be excluded from output
Dec 05 21:52:43.406 [INFO] recv: unsuccessful responses will be excluded from output
 0:00 0%; send: 1 1 p/s (22 p/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
 0:00 0%; send: 1 0 p/s (22 p/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
 0:01 9%; send: 134 133 p/s (128 p/s avg); recv: 22 22 p/s (21 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 16.42%
 0:02 17%; send: 263 129 p/s (128 p/s avg); recv: 22 0 p/s (10 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 8.37%
 0:03 25%; send: 391 128 p/s (128 p/s avg); recv: 51 29 p/s (16 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 13.04%
 0:04 34%; send: 512 done (128 p/s avg); recv: 74 23 p/s (18 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 14.45%
 0:05 42% (7s left); send: 512 done (128 p/s avg); recv: 90 16 p/s (17 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.58%
 0:06 50% (6s left); send: 512 done (128 p/s avg); recv: 90 0 p/s (14 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.58%
 0:07 59% (5s left); send: 512 done (128 p/s avg); recv: 90 0 p/s (12 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.58%
 0:08 67% (4s left); send: 512 done (128 p/s avg); recv: 90 0 p/s (11 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.58%
 0:09 75% (3s left); send: 512 done (128 p/s avg); recv: 90 0 p/s (9 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.58%
 0:10 84% (2s left); send: 512 done (128 p/s avg); recv: 90 0 p/s (8 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.58%
 0:11 92% (1s left); send: 512 done (128 p/s avg); recv: 90 0 p/s (8 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 17.58%
Dec 05 21:52:55.412 [INFO] zmap: completed

A new output.csv file (located in the folder from which you ran the scan) should now include all IP's that responded on port 80 and our stdout will only show us the status of the scan, nice!

Performance

A big selling point of ZMap is the speed with which it can survey large subnets. On a Gigabit Ethernet connection it's possible to survey the entire IPv4 space on a single port in 40 minutes! Let's discuss the various factors at play when it comes to performance. To do that, we'll need to learn a bit more about how ZMap operates.

Decoupling Sending/Receiving

To get such high performance, ZMap is stateless by design. This means one/multiple sending threads send packets and one/multiple receiving threads record any replies, but there's no inter-thread communication. This lack of communication/locking is what allows ZMap to be so performant. It also helps explain the output we saw above. The subnet (/23) has 512 IPs and coupled with our scan rate of 128 packets per second (p/s), sending of probe packets (TCP SYN packets by default) concluded in 4 seconds.

 0:04 34%; send: 512 done (128 p/s avg); recv: 74 23 p/s (18 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 14.45%

By default, the receiving threads wait 8 seconds after the sending threads finish sending to wait on any SYN ACK packets. We found that 8 seconds strikes a good balance between waiting on slow SYN ACK's potentially from distant hosts but not waiting so long that there's diminishing returns. Depending on your needs, you may wish to modify this value with --cooldown-time=10. This would force the receiving threads to wait for 10 seconds for any straggler packets.

Note that in the output we captured above, the hit-rate didn't increase (at least at the hundredth of a percent granularity) after 0:05, so keep the diminishing returns in mind.

Increasing Send Performance

We covered in the section above the constant time the receiving threads will wait on SYN ACK's with --cooldown-time, but that doesn't address the rate of sending SYN's. In larger scans, this is the component which will dominate the runtime of the scan.

With a large enough target range, there's much less worry about overloading a target subnet. To increase performance further, we need to add more threads. Use the --sender-threads=N flag to do this:

1 Thread Send Rate

sudo zmap -p 80 -o "output.csv" -B 1G --sender-threads=1 171.67.70.0/2
 0:00 0%; send: 211 1 p/s (3.69 Kp/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
 0:00 0%; send: 400 274 Kp/s (6.92 Kp/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
 0:01 0%; send: 505473 505 Kp/s (478 Kp/s avg); recv: 5176 5.17 Kp/s (4.89 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.02%
 0:02 0%; send: 1113056 607 Kp/s (541 Kp/s avg); recv: 12962 7.78 Kp/s (6.30 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.16%
 0:03 0%; send: 1726996 614 Kp/s (565 Kp/s avg); recv: 20562 7.60 Kp/s (6.72 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.19%
 0:04 0%; send: 2335833 609 Kp/s (575 Kp/s avg); recv: 28571 8.01 Kp/s (7.04 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.22%
 0:05 0% (30m left); send: 2943829 608 Kp/s (582 Kp/s avg); recv: 36420 7.85 Kp/s (7.20 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.24%
 0:06 0% (30m left); send: 3550851 607 Kp/s (586 Kp/s avg); recv: 44122 7.70 Kp/s (7.28 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.24%
 0:07 0% (30m left); send: 4156961 606 Kp/s (589 Kp/s avg); recv: 52047 7.92 Kp/s (7.37 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.25%
 0:08 0% (30m left); send: 4756743 600 Kp/s (590 Kp/s avg); recv: 59834 7.79 Kp/s (7.42 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.26%
 ...

4 Thread Send Rate

sudo zmap -p 80 -o "output.csv" -B 1G --sender-threads=4 171.67.70.0/2
 0:00 0%; send: 360 1 p/s (5.31 Kp/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
 0:00 0%; send: 586 388 Kp/s (8.58 Kp/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
 0:01 0%; send: 1125395 1.12 Mp/s (1.05 Mp/s avg); recv: 11804 11.8 Kp/s (11.0 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.05%
 0:02 0%; send: 2401617 1.28 Mp/s (1.16 Mp/s avg); recv: 28248 16.4 Kp/s (13.7 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.18%
 0:03 0%; send: 3667077 1.27 Mp/s (1.19 Mp/s avg); recv: 44305 16.1 Kp/s (14.4 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.21%
 0:04 0%; send: 4932812 1.27 Mp/s (1.21 Mp/s avg); recv: 60886 16.6 Kp/s (15.0 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.23%
 0:05 1% (14m left); send: 6193698 1.26 Mp/s (1.22 Mp/s avg); recv: 77131 16.2 Kp/s (15.2 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.25%
 0:06 1% (14m left); send: 7452466 1.26 Mp/s (1.23 Mp/s avg); recv: 93092 16.0 Kp/s (15.3 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.25%
 0:07 1% (14m left); send: 8713445 1.26 Mp/s (1.23 Mp/s avg); recv: 109494 16.4 Kp/s (15.5 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.26%
 0:08 1% (14m left); send: 9970806 1.26 Mp/s (1.24 Mp/s avg); recv: 125807 16.3 Kp/s (15.6 Kp/s avg); drops: 0 p/s (0 p/s avg); hitrate: 1.26%
 ...

We can see that increasing the number of sending threads has increased our packet send rate and therefore decreased our runtime (30 mins expected runtime vs. 14 mins). For most purposes, a single thread is probably sufficient, but if you need to push the performance, have a high bandwidth uplink, and are doing scans across a large section of the IPv4 space, this is available to you.

Increasing Scan Accuracy

ZMap will send packets at the rate of min(max rate your Network Interface Card (NIC) can sustain, the rate you specify with CLI flags). This rate could be greater than your LAN or target subnets are capable of handling, leading to dropped packets and imprecise scan results.

A dropped SYN or SYN ACK will appear the same to ZMap as if no host was present at all. Since ZMap is composing Ethernet frames directly, there's no guarantee of delivery. With that in mind, you may wish to lower your --rate=pps or --bandwidth=bps to determine if your hit rate stays consistent (indicative that you aren't seeing a large amount of dropped packets/false negatives). Another option is to increase --probes=N (default = 1). "Probes" are the ZMap term for the packets ZMap sends out to potential hosts, and you can use multiple probes per IP/Port pair to try to account for packet loss.

Probes and Deduplication

As mentioned in the previous section, you may wish to increase the --probes=N count to decrease the chance of a false negative in the scan. However, since ZMap is stateless, how can it prevent multiple logs for a given IP if --probes=N and N > 1?

For example:

sudo zmap -p 80 --probes=5 -B 100M --dedup-method=none 171.67.70.0/10
 0:00 0%; send: 42 1 p/s (821 p/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
171.67.224.74
171.67.224.74
171.67.224.74
171.67.224.74
171.67.224.74
171.67.35.131
171.67.35.131
171.67.35.131
171.67.35.131
171.67.35.131
171.67.7.61
171.67.7.61
171.67.7.61
171.67.7.61
171.67.7.61
...

Every time a probe is responded to, it's logged. By default, the --dedup-method=window with a window size of 1M. This means ZMap will track the last 1M probe responses in memory to avoid logging a hit multiple times. You can use --dedup-method=full but understand that the memory requirements will scale up with the size of the scan space.

Note: We've said that ZMap is stateless, but here we seem to be contradicting that statement with making mention of storing the last 1M probe responses  😄. ZMap is stateless between sending threads and receiving threads but ZMap's receiving threads can hold state so as not to log duplicates.

Expected Max Send Rate

In our lab environment, off-the-shelf Ubuntu VM's with 1 Gbit wired Ethernet could get up to 1.3-1.45 Million packets per second. Using a wireless connection, lower-end hardware, or LAN bandwidth contention could all potentially negatively affect your performance.

Other Scans

Get N Reachable Hosts

In our earlier example (sudo zmap -p 80 -o "output.csv" -B 1G --sender-threads=4 171.67.70.0/2) we used a given subnet. However, perhaps you just want to get 10 reachable hosts across the IPv4 space. You can use --max-results=10 or -N 10

sudo zmap -p 80 -B 100M -N 10
 0:00 0%; send: 0 0 p/s (0 p/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
 0:00 0%; send: 0 0 p/s (0 p/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
104.252.154.129
207.241.227.232
34.102.143.17
185.227.252.87
88.218.93.178
206.54.174.210
50.3.14.225
34.43.9.190
184.84.170.239
154.37.15.2
Dec 05 22:19:22.112 [INFO] zmap: completed

Reproducibility and Random Search

Run the above command again...

sudo zmap -p 80 -B 100M -N 10
 0:00 0%; send: 0 0 p/s (0 p/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
 0:00 0%; send: 17 62.7 Kp/s (305 p/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
206.2.159.123
47.251.50.46
144.49.230.210
34.220.245.127
34.160.175.57
216.180.232.245
20.94.252.204
172.65.73.3
38.173.125.213
172.67.201.97
Dec 05 22:33:37.695 [INFO] zmap: completed

That's interesting, we got an entirely different set of IPs! When you define a given search space, ZMap scans it randomly, not linearly. This wasn't a big deal when we wanted to search the entire search space like with previous scans since we'd still end up with the same (unordered) result set. However, in this case ZMap returns when it finds the first 10 hits in a given search space. ZMap's random search pattern leads to this variance in output.

Why search randomly? Resource contention can happen on both the sending and receiving side. We want to spread out the load placed on a receiving subnet(s) as much as possible, so by randomly searching a search space, we spread out the load we're placing on a given network at a given time.

You may want to have reproducibility in your scan's order, so you can use the --seed=n to ensure subsequent runs use the same ordering.

Multiple Ports

Added in ZMap v.4.0.0

You can scan across multiple ports. In this case, ZMap will send a TCP SYN packet to each IP/Port pair.

sudo zmap -p 22,80 -B 100M -N 10 171.67.70.0/10
 0:00 0%; send: 824 1 p/s (10.7 Kp/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
 0:00 0%; send: 1163 623 Kp/s (15.0 Kp/s avg); recv: 0 0 p/s (0 p/s avg); drops: 0 p/s (0 p/s avg); hitrate: 0.00%
125.6.184.89,80
125.6.189.199,80
125.62.85.202,22
125.62.86.43,22
125.103.118.1,22
125.135.32.198,80
125.136.125.185,80
125.140.210.154,80
125.143.130.34,80
125.137.239.225,80
Nov 28 23:15:06.878 [INFO] zmap: completed

Multiple Subnets

You can scan multiple subnets with the same scan as well.

sudo zmap -p 80 10.0.0.0/8 192.168.0.0/16 (scan both subnets on TCP/80)

ZMap's Config Files

By default, ZMap doesn't scan the entire IPv4 space. There are two files that include subnets that are not scanned by default. You can change these files to explicitly scan those ranges, however in each case there's a reason they're not scanned by default.

blocklist.conf

blocklist.conf includes the subnets which have been defined by RFC's (e.g. multicast, RFC 1918 to be reserved/unallocated, these are not scanned by default. You can add to this file if you wish to not scan certain IPs/subnets.

zmap.conf

If you find yourself specifying certain settings, such as your maximum bandwidth or blacklist file every time you run ZMap, you can specify these in /etc/zmap/zmap.conf or use a custom configuration file.

Finding the Config Files on your Installation

You can find these files in your installation usually at /etc/zmap/...conf.

If you installed ZMap with a package manager, it may have installed the config files in it's own location.

For example, homebrew installs config files in /opt/homebrew/etc/zmap/. If you're unable to locate the files, try running sudo find . -name "blocklist.conf" -maxdepth 5 from the root directory after installing ZMap.

Further Resources

  • Github Discussions - Where you can share your questions, fun projects, or interact with the community/ZMap team!
  • ZMap Paper - The research paper giving further details into the motivation, design, and architecture of ZMap.
  • man zmap - We outlined the most common flags/CLI parameters in this Getting Started guide, but further details/flags are available in the man pages.
  • Scanning Best Practices