TranslateProject/published/201507/20150401 ZMap Documentation.md
2015-08-01 23:35:42 +08:00

38 KiB
Raw Permalink Blame History

互联网扫描器 ZMap 完全手册

  1. 初识 ZMap
  2. 最佳扫描习惯
  3. 命令行参数
  4. 附加信息
    1. TCP SYN 探测模块
    2. ICMP Echo 探测模块
    3. UDP 探测模块
    4. 配置文件
    5. 详细
    6. 结果输出
    7. 黑名单
    8. 速度限制与抽样
    9. 发送多个探测
  5. ZMap 扩展
    1. 示例应用程序
    2. 编写探测和输出模块

初识 ZMap

ZMap被设计用来针对整个IPv4地址空间或其中的大部分实施综合扫描的工具。ZMap是研究者手中的利器但在运行ZMap时请注意您很有可能正在以每秒140万个包的速度扫描整个IPv4地址空间 。我们建议用户即使在实施小范围扫描之前,也联系一下本地网络的管理员并参考我们列举的最佳扫描体验

默认情况下ZMap会对于指定端口实施尽可能大速率的TCP SYN扫描。较为保守的情况下对10,000个随机的地址的80端口以10Mbps的速度扫描如下所示

$ zmap --bandwidth=10M --target-port=80 --max-targets=10000 --output-file=results.csv 

或者更加简洁地写成:

$ zmap -B 10M -p 80 -n 10000 -o results.csv

ZMap也可用于扫描特定子网或CIDR地址块。例如仅扫描10.0.0.0/8和192.168.0.0/16的80端口运行指令如下

zmap -p 80 -o results.csv 10.0.0.0/8 192.168.0.0/16

如果扫描进行的顺利ZMap会每秒输出类似以下内容的状态更新

0% (1h51m left); send: 28777 562 Kp/s (560 Kp/s avg); recv: 1192 248 p/s (231 p/s avg); hits: 0.04%
0% (1h51m left); send: 34320 554 Kp/s (559 Kp/s avg); recv: 1442 249 p/s (234 p/s avg); hits: 0.04%
0% (1h50m left); send: 39676 535 Kp/s (555 Kp/s avg); recv: 1663 220 p/s (232 p/s avg); hits: 0.04%
0% (1h50m left); send: 45372 570 Kp/s (557 Kp/s avg); recv: 1890 226 p/s (232 p/s avg); hits: 0.04%

这些更新信息提供了扫描的即时状态并表示成:

完成进度% (剩余时间); send: 发出包的数量 即时速率 (平均发送速率); recv: 接收包的数量 接收率 (平均接收率); hits: 命中率

如果您不知道您所在网络能支持的扫描速率,您可能要尝试不同的扫描速率和带宽限制直到扫描效果开始下降,借此找出当前网络能够支持的最快速度。

默认情况下ZMap会输出不同IP地址的列表例如根据SYN ACK数据包的情况像下面这样。其输出结果还有几种附加的格式JSON和Redis可以用作生成程序可解析的扫描统计。 同样,可以指定附加的输出字段并使用输出过滤来过滤输出的结果。

115.237.116.119
23.9.117.80
207.118.204.141
217.120.143.111
50.195.22.82

我们强烈建议您使用黑名单文件,以排除预留的/未分配的IP地址空间RFC1918 规定的私有地址、组播地址以及网络中需要排除在您扫描之外的地址。默认情况下ZMap将采用位于 /etc/zmap/blacklist.conf的这个简单的黑名单文件中所包含的预留和未分配地址。如果您需要某些特定设置比如每次运行ZMap时的最大带宽或黑名单文件,您可以在文件/etc/zmap/zmap.conf中指定或使用自定义配置文件

如果您正试图解决扫描的相关问题,有几个选项可以帮助您调试。首先,您可以通过添加--dryrun实施预扫,以此来分析包可能会发送到网络的何处。此外,还可以通过设置'--verbosity=n`来更改日志详细程度

最佳扫描体验

我们为针对互联网进行扫描的研究者提供了一些建议,以此来引导养成良好的互联网合作氛围。

  • 密切协同本地的网络管理员,以减少风险和调查
  • 确认扫描不会使本地网络或上游供应商瘫痪
  • 在发起扫描的源地址的网页和DNS条目中申明你的扫描是善意的
  • 明确解释你的扫描中所有连接的目的和范围
  • 提供一个简单的退出扫描的方法并及时响应请求
  • 实施扫描时,不使用比研究对象需求更大的扫描范围或更快的扫描频率
  • 如果可以,将扫描流量分布到不同的时间或源地址上

即使不声明,使用扫描的研究者也应该避免利用漏洞或访问受保护的资源,并遵守其辖区内任何特殊的法律规定。

命令行参数

通用选项

这些选项是实施简单扫描时最常用的选项。我们注意到某些选项取决于所使用的探测模块输出模块在实施ICMP Echo扫描时是不需要使用目的端口的

-p, --target-port=port

要扫描的目标TCP端口号例如443

-o, --output-file=name

将结果写入该文件,使用-代表输出到标准输出。

-b, --blacklist-file=path

文件中被排除的子网使用CIDR表示法如192.168.0.0/16一个一行。建议您使用此方法排除RFC 1918地址、组播地址、IANA预留空间等IANA专用地址。在conf/blacklist.example中提供了一个以此为目的示例黑名单文件。

扫描选项

-n, --max-targets=n

限制探测目标的数量。后面跟的可以是一个数字(例如'-n 1000),或可扫描地址空间的百分比(例如,-n 0.1`,不包括黑名单)

-N, --max-results=n

收到多少结果后退出

-t, --max-runtime=secs

限制发送报文的时间

-r, --rate=pps

设置发包速率,以包/秒为单位

-B, --bandwidth=bps

以比特/秒设置传输速率支持使用后缀GM或K-B 10M就是速度10 mbps的。设置会覆盖--rate

-c, --cooldown-time=secs

发送完成后等待多久继续接收回包(默认值= 8

-e, --seed=n

地址排序种子。如果要用多个ZMap以相同的顺序扫描地址那么就可以使用这个参数。

--shards=n

将扫描分片/区使其可多个ZMap中执行默认值= 1。启用分片时--seed参数是必需的。

--shard=n

选择扫描的分片(默认值= 0。n的范围在[0N)其中N为碎片的总数。启用分片时--seed参数是必需的。

-T, --sender-threads=n

用于发送数据包的线程数(默认值= 1

-P, --probes=n

发送到每个IP的探测数默认值= 1

-d, --dryrun

用标准输出打印出每个包,而不是将其发送(用于调试)

网络选项

-s, --source-port=port|range

发送数据包的源端口

-S, --source-ip=ip|range

发送数据包的源地址。可以仅仅是一个IP也可以是一个范围10.0.0.1-10.0.0.9

-G, --gateway-mac=addr

数据包发送到的网关MAC地址用以防止自动检测不工作的情况

-i, --interface=name

使用的网络接口

探测选项

ZMap允许用户指定并添加自己所需要的探测模块。 探测模块的职责就是生成要发送的探测包,并处理主机回复的响应包。

--list-probe-modules

列出可用探测模块如tcp_synscan

-M, --probe-module=name

选择探测模块(默认值= tcp_synscan

--probe-args=args

向模块传递参数

--list-output-fields

列出可用的输出模块

输出选项

ZMap允许用户指定和编写他们自己的输出模块。输出模块负责处理由探测模块返回的字段,并将它们输出给用户。用户可以指定输出的字段,并过滤相应字段。

--list-output-modules

列出可用输出模块如tcp_synscan

-O, --output-module=name

选择输出模块默认值为csv

--output-args=args

传递给输出模块的参数

-f, --output-fields=fields

输出的字段列表,以逗号分割

--output-filter

指定输出过滤器探测模块定义字段进行过滤

附加选项

-C, --config=filename

加载配置文件,可以指定其他路径。

-q, --quiet

不必每秒刷新输出

-g, --summary

在扫描结束后打印配置和结果汇总信息

-v, --verbosity=n

日志详细程度0-5默认值= 3

-h, --help

打印帮助并退出

-V, --version

打印版本并退出

附加信息

TCP SYN 扫描

在执行TCP SYN扫描时ZMap需要指定一个目标端口也支持指定发起扫描的源端口范围。

-p, --target-port=port

扫描的TCP端口例如 443

-s, --source-port=port|range

发送扫描数据包的源端口(例如 40000-50000

警示! ZMap基于Linux内核使用RST包来应答SYN/ACK包响应以关闭扫描器打开的连接。ZMap是在Ethernet层完成包的发送的这样做是为了减少跟踪打开的TCP连接和路由寻路带来的内核开销。因此如果您有跟踪连接建立的防火墙规则如类似于-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT的netfilter规则将阻止SYN/ACK包到达内核。这不会妨碍到ZMap记录应答但它会阻止RST包被送回最终被扫描主机的连接会一直打开直到超时后断开。我们强烈建议您在执行ZMap时选择一组主机上未使用且防火墙允许访问的端口加在-s后(如 -s '50000-60000' )。

ICMP Echo 请求扫描

虽然在默认情况下ZMap执行的是TCP SYN扫描但它也支持使用ICMP echo请求扫描。在这种扫描方式下ICMP echo请求包被发送到每个主机并以收到ICMP应答包作为答复。实施ICMP扫描可以通过选择icmp_echoscan扫描模块来执行如下

$ zmap --probe-module=icmp_echoscan

UDP 数据报扫描

ZMap还额外支持UDP探测它会发出任意UDP数据报给每个主机并接收UDP或ICMP不可达的应答。ZMap可以通过使用--probe-args命令行选项来设置四种不同的UDP载荷。这些是可在命令行设置可打印的ASCII 码的text载荷和十六进制载荷的hex外部文件中包含载荷的file和通过动态字段生成的载荷的template。为了得到UDP响应请使用-f参数确保您指定的“data”字段处于输出范围。

下面的例子将发送两个字节'ST'即PCAnwywhere的'status'请求到UDP端口5632。

$ zmap -M udp -p 5632 --probe-args=text:ST -N 100 -f saddr,data -o -

下面的例子将发送字节“0X02”即SQL Server的'client broadcast'请求到UDP端口1434。

$ zmap -M udp -p 1434 --probe-args=hex:02 -N 100 -f saddr,data -o -

下面的例子将发送一个NetBIOS状态请求到UDP端口137。使用一个ZMap自带的载荷文件。

$ zmap -M udp -p 1434 --probe-args=file:netbios_137.pkt -N 100 -f saddr,data -o -

下面的例子将发送SIP的'OPTIONS'请求到UDP端口5060。使用附ZMap自带的模板文件。

$ zmap -M udp -p 1434 --probe-args=file:sip_options.tpl -N 100 -f saddr,data -o -

UDP载荷模板仍处于实验阶段。当您在更多的使用一个以上的发送线程-T时可能会遇到崩溃和一个明显的相比静态载荷性能降低的表现。模板仅仅是一个由一个或多个使用${}将字段说明封装成序列构成的载荷文件。某些协议特别是SIP需要载荷来反射包中的源和目的包。其他协议如portmapper和DNS每个请求包含的字段应该是随机的或降低被Zamp扫描的多宿主系统的风险。

以下的载荷模板将发送SIP OPTIONS请求到每一个目的地

OPTIONS sip:${RAND_ALPHA=8}@${DADDR} SIP/2.0
Via: SIP/2.0/UDP ${SADDR}:${SPORT};branch=${RAND_ALPHA=6}.${RAND_DIGIT=10};rport;alias
From: sip:${RAND_ALPHA=8}@${SADDR}:${SPORT};tag=${RAND_DIGIT=8}
To: sip:${RAND_ALPHA=8}@${DADDR}
Call-ID: ${RAND_DIGIT=10}@${SADDR}
CSeq: 1 OPTIONS
Contact: sip:${RAND_ALPHA=8}@${SADDR}:${SPORT}
Content-Length: 0
Max-Forwards: 20
User-Agent: ${RAND_ALPHA=8}
Accept: text/plain

就像在上面的例子中展示的那样,注意每行行末以\r\n结尾请求以\r\n\r\n结尾大多数SIP实现都可以正确处理它。一个可以工作的例子放在ZMap的examples/udp-payloads目录下 (sip_options.tpl).

当前实现了下面的模板字段:

  • SADDR: 源IP地址的点分十进制格式
  • SADDR_N: 源IP地址的网络字节序格式
  • DADDR: 目的IP地址的点分十进制格式
  • DADDR_N: 目的IP地址的网络字节序格式
  • SPORT: 源端口的ascii格式
  • SPORT_N: 源端口的网络字节序格式
  • DPORT: 目的端口的ascii格式
  • DPORT_N: 目的端口的网络字节序格式
  • RAND_BYTE: 随机字节(0-255),长度由=(length) 参数决定
  • RAND_DIGIT: 随机数字0-9长度由=(length) 参数决定
  • RAND_ALPHA: 随机大写字母A-Z长度由=(length) 参数决定
  • RAND_ALPHANUM: 随机大写字母A-Z和随机数字0-9长度由=(length) 参数决定

配置文件

ZMap支持使用配置文件来代替在命令行上指定所有要求的选项。配置中可以通过每行指定一个长名称的选项和对应的值来创建

interface "eth1"
source-ip 1.1.1.4-1.1.1.8
gateway-mac b4:23:f9:28:fa:2d # upstream gateway
cooldown-time 300 # seconds
blacklist-file /etc/zmap/blacklist.conf
output-file ~/zmap-output
quiet
summary

然后ZMap就可以按照配置文件并指定一些必要的附加参数运行了

$ zmap --config=~/.zmap.conf --target-port=443

详细

ZMap可以在屏幕上生成多种类型的输出。默认情况下Zmap将每隔1秒打印出相似的基本进度信息。可以通过设置--quiet来禁用。

0:01 12%; send: 10000 done (15.1 Kp/s avg); recv: 144 143 p/s (141 p/s avg); hits: 1.44%

ZMap同样也可以根据扫描配置打印如下消息可以通过'--verbosity`参数加以控制。

Aug 11 16:16:12.813 [INFO] zmap: started
Aug 11 16:16:12.817 [DEBUG] zmap: no interface provided. will use eth0
Aug 11 16:17:03.971 [DEBUG] cyclic: primitive root: 3489180582
Aug 11 16:17:03.971 [DEBUG] cyclic: starting point: 46588
Aug 11 16:17:03.975 [DEBUG] blacklist: 3717595507 addresses allowed to be scanned
Aug 11 16:17:03.975 [DEBUG] send: will send from 1 address on 28233 source ports
Aug 11 16:17:03.975 [DEBUG] send: using bandwidth 10000000 bits/s, rate set to 14880 pkt/s
Aug 11 16:17:03.985 [DEBUG] recv: thread started

ZMap还支持在扫描之后打印出一个的可grep的汇总信息类似于下面这样可以通过调用--summary来实现。

cnf	target-port				443
cnf	source-port-range-begin			32768
cnf	source-port-range-end			61000
cnf	source-addr-range-begin			1.1.1.4
cnf	source-addr-range-end			1.1.1.8
cnf	maximum-packets				4294967295
cnf	maximum-runtime				0
cnf	permutation-seed			0
cnf	cooldown-period				300
cnf	send-interface				eth1
cnf	rate					45000
env	nprocessors				16
exc	send-start-time				Fri Jan 18 01:47:35 2013
exc	send-end-time				Sat Jan 19 00:47:07 2013
exc	recv-start-time				Fri Jan 18 01:47:35 2013
exc	recv-end-time				Sat Jan 19 00:52:07 2013
exc	sent					3722335150
exc	blacklisted				572632145
exc	first-scanned				1318129262
exc	hit-rate				0.874102
exc	synack-received-unique			32537000
exc	synack-received-total			36689941
exc	synack-cooldown-received-unique		193
exc	synack-cooldown-received-total		1543
exc	rst-received-unique			141901021
exc	rst-received-total			166779002
adv	source-port-secret			37952
adv	permutation-gen				4215763218

结果输出

ZMap可以通过输出模块生成不同格式的结果。默认情况下ZMap只支持csv的输出,但是可以通过编译支持redisjson 。可以使用输出过滤来过滤这些发送到输出模块上的结果。输出模块输出的字段由用户指定。默认情况如果没有指定输出文件ZMap将以csv格式返回结果而不会生成特定结果。也可以编写自己的输出模块;请参阅编写输出模块

-o, --output-file=p

输出写入文件地址

-O, --output-module=p

调用自定义输出模块

-f, --output-fields=p

以逗号分隔的输出的字段列表

--output-filter=filter

对给定的探测指定字段输出过滤

--list-output-modules

列出可用输出模块

--list-output-fields

列出给定的探测的可用输出字段

输出字段

除了IP地址之外ZMap有很多字段。这些字段可以通过在给定探测模块上运行--list-output-fields来查看。

$ zmap --probe-module="tcp_synscan" --list-output-fields
saddr           string: 应答包中的源IP地址
saddr-raw          int: 网络字节格式的源IP地址
daddr           string: 应答包中的目的IP地址
daddr-raw          int: 网络字节格式的目的IP地址
ipid               int: 应答包中的IP识别号
ttl                int: 应答包中的ttl存活时间值
sport              int: TCP 源端口
dport              int: TCP 目的端口
seqnum             int: TCP 序列号
acknum             int: TCP Ack号
window             int: TCP 窗口
classification  string: 包类型
success            int: 是应答包成功
repeat             int: 是否是来自主机的重复响应
cooldown           int: 是否是在冷却时间内收到的响应
timestamp-str   string: 响应抵达时的时间戳使用ISO8601格式
timestamp-ts       int: 响应抵达时的时间戳使用UNIX纪元开始的秒数
timestamp-us       int: 时间戳的微秒部分(例如 从'timestamp-ts'的几微秒)

可以通过使用--output-fields=fields-f来选择选择输出字段,任意组合的输出字段可以被指定为逗号分隔的列表。例如:

$ zmap -p 80 -f "response,saddr,daddr,sport,seq,ack,in_cooldown,is_repeat,timestamp" -o output.csv

过滤输出

在传到输出模块之前探测模块生成的结果可以先过滤。过滤是针对探测模块的输出字段的。过滤使用类似于SQL的简单过滤语法写成通过ZMap的**--output-filter**选项来指定。输出过滤通常用于过滤掉重复的结果,或仅传输成功的响应到输出模块。

过滤表达式的形式为<字段名> <操作符> <值><值>的类型必须是一个字符串或一串无符号整数并且匹配<字段名>类型。对于整数比较有效的操作符是= !=, <, >, <=, >=。字符串比较的操作是=!=。--list-output-fields可以打印那些可供探测模块选择的字段和类型,然后退出。

复合型的过滤操作,可以通过使用&&(逻辑与)和||(逻辑或)这样的运算符来组合出特殊的过滤操作。

示例

书写一则过滤仅显示成功的、不重复的应答

--output-filter="success = 1 && repeat = 0"

过滤出RST分类并且TTL大于10的包或者SYNACK分类的包

--output-filter="(classification = rst && ttl > 10) || classification = synack"

CSV

csv模块将会生成以逗号分隔各个要求输出的字段的文件。例如以下的指令将生成名为output.csv的CSV文件。

$ zmap -p 80 -f "response,saddr,daddr,sport,seq,ack,in_cooldown,is_repeat,timestamp" -o output.csv

#响应, 源地址, 目的地址, 源端口, 目的端口, 序列号, 应答, 是否是冷却模式, 是否重复, 时间戳
response, saddr, daddr, sport, dport, seq, ack, in_cooldown, is_repeat, timestamp
synack, 159.174.153.144, 10.0.0.9, 80, 40555, 3050964427, 3515084203, 0, 0,2013-08-15 18:55:47.681
rst, 141.209.175.1, 10.0.0.9, 80, 40136, 0, 3272553764, 0, 0,2013-08-15 18:55:47.683
rst, 72.36.213.231, 10.0.0.9, 80, 56642, 0, 2037447916, 0, 0,2013-08-15 18:55:47.691
rst, 148.8.49.150, 10.0.0.9, 80, 41672, 0, 1135824975, 0, 0,2013-08-15 18:55:47.692
rst, 50.165.166.206, 10.0.0.9, 80, 38858, 0, 535206863, 0, 0,2013-08-15 18:55:47.694
rst, 65.55.203.135, 10.0.0.9, 80, 50008, 0, 4071709905, 0, 0,2013-08-15 18:55:47.700
synack, 50.57.166.186, 10.0.0.9, 80, 60650, 2813653162, 993314545, 0, 0,2013-08-15 18:55:47.704
synack, 152.75.208.114, 10.0.0.9, 80, 52498, 460383682, 4040786862, 0, 0,2013-08-15 18:55:47.707
synack, 23.72.138.74, 10.0.0.9, 80, 33480, 810393698, 486476355, 0, 0,2013-08-15 18:55:47.710

Redis

Redis的输出模块允许地址被添加到一个Redis的队列而不是保存到文件允许ZMap将它与之后的处理工具结合使用。

注意! ZMap默认不会编译Redis功能。如果你从源码构建ZMap可以在CMake的时候加上-DWITH_REDIS=ON来增加Redis支持。

JSON

JSON输出模块用起来类似于CSV模块只是以JSON格式写入到文件。JSON文件能轻松地导入到其它可以读取JSON的程序中。

注意!ZMap默认不会编译JSON功能。如果你从源码构建ZMap可以在CMake的时候加上-DWITH_JSON=ON来增加JSON支持。

黑名单和白名单

ZMap同时支持对网络前缀做黑名单和白名单。如果ZMap不加黑名单和白名单参数他将会扫描所有的IPv4地址包括本地的保留的以及组播地址。如果指定了黑名单文件那么在黑名单中的网络前缀将不再扫描如果指定了白名单文件只有那些网络前缀在白名单内的才会扫描。白名单和黑名单文件可以协同使用黑名单优先于白名单例如如果您在白名单中指定了10.0.0.0/8并在黑名单中指定了10.1.0.0/16那么10.1.0.0/16将不会扫描。白名单和黑名单文件可以在命令行中指定如下所示

-b, --blacklist-file=path

文件用于记录黑名单子网以CIDR无类域间路由的表示法例如192.168.0.0/16

-w, --whitelist-file=path

文件用于记录限制扫描的子网以CIDR的表示法例如192.168.0.0/16

黑名单文件的每行都需要以CIDR的表示格式书写一行单一的网络前缀。允许使用#加以备注。例如:

# IANA英特网编号管理局记录的用于特殊目的的IPv4地址
# http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
# 更新于2013-05-22

0.0.0.0/8           # RFC1122: 网络中的所有主机
10.0.0.0/8          # RFC1918: 私有地址
100.64.0.0/10       # RFC6598: 共享地址空间
127.0.0.0/8         # RFC1122: 回环地址
169.254.0.0/16      # RFC3927: 本地链路地址
172.16.0.0/12       # RFC1918: 私有地址
192.0.0.0/24        # RFC6890: IETF协议预留
192.0.2.0/24        # RFC5737: 测试地址1
192.88.99.0/24      # RFC3068: IPv6转换到IPv4的任播
192.168.0.0/16      # RFC1918: 私有地址
192.18.0.0/15       # RFC2544: 检测地址
198.51.100.0/24     # RFC5737: 测试地址2
203.0.113.0/24      # RFC5737: 测试地址3
240.0.0.0/4         # RFC1112: 预留地址
255.255.255.255/32  # RFC0919: 限制广播地址

# IANA记录的用于组播的地址空间
# http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml
# 更新于2013-06-25

224.0.0.0/4         # RFC5771: 组播/预留地址ed

如果您只是想扫描因特网中随机的一部分地址,使用抽样检出,来代替使用白名单和黑名单。

**注意!**ZMap默认设置使用/etc/zmap/blacklist.conf作为黑名单文件其中包含有本地的地址空间和预留的IP空间。通过编辑/etc/zmap/zmap.conf可以改变默认的配置。

速度限制与抽样

默认情况下ZMap将以您当前网卡所能支持的最快速度扫描。以我们对于常用硬件的经验这通常是理论上Gbit以太网速度的95-98%这可能比您的上游提供商可处理的速度还要快。ZMap是不会自动的根据您的上游提供商来调整发送速率的。您可能需要手动的调整发送速率来减少丢包和错误结果。

-r, --rate=pps

设置最大发送速率以包/秒为单位

-B, --bandwidth=bps

设置发送速率以比特/秒(支持G,M和K后缀)。这会覆盖--rate参数。

ZMap同样支持对IPv4地址空间进行指定最大目标数和/或最长运行时间的随机采样。由于每次对主机的扫描是通过随机排序生成的限制扫描的主机个数为N就会随机抽选N个主机。命令选项如下

-n, --max-targets=n

探测目标上限数量

-N, --max-results=n

结果上限数量(累积收到这么多结果后退出)

-t, --max-runtime=s

发送数据包时间长度上限(以秒为单位)

-s, --seed=n

种子用以选择地址的排列方式。使用不同ZMap执行扫描操作时将种子设成相同的值可以保证相同的扫描顺序。

举个例子,如果您想要多次扫描同样的一百万个互联网主机,您可以设定排序种子和扫描主机的上限数量,大致如下所示:

zmap -p 443 -s 3 -n 1000000 -o results

为了确定哪一百万主机将要被扫描,您可以执行预扫,只打印数据包而非发送,并非真的实施扫描。

zmap -p 443 -s 3 -n 1000000 --dryrun | grep daddr
	| awk -F'daddr: ' '{print $2}' | sed 's/ |.*//;' 

发送多个数据包

ZMap支持向每个主机发送多个探测。增加这个数量既增加了扫描时间又增加了到达的主机数量。然而我们发现增加的扫描时间每个额外扫描的增加近100远远大于到达的主机数量每个额外扫描的增加近1

-P, --probes=n

向每个IP发出的独立探测个数默认值=1

示例应用

ZMap专为向大量主机发起连接并寻找那些正确响应而设计。然而我们意识到许多用户需要执行一些后续处理如执行应用程序级别的握手。例如用户在80端口实施TCP SYN扫描也许想要实施一个简单的GET请求还有用户扫描443端口可能希望完成TLS握手。

Banner获取

我们收录了一个示例程序banner-grab伴随ZMap使用可以让用户从监听状态的TCP服务器上接收到消息。Banner-grab连接到提供的服务器上发送一个可选的消息然后打印出收到的第一个消息。这个工具可以用来获取banner例如HTTP服务的回复的具体指令telnet登陆提示或SSH服务的字符串。

下面的例子寻找了1000个监听80端口的服务器并向每个发送一个简单的GET请求存储他们的64位编码响应至http-banners.out

$ zmap -p 80 -N 1000 -B 10M -o - | ./banner-grab-tcp -p 80 -c 500 -d ./http-req > out

如果想知道更多使用banner-grab的细节,可以参考examples/banner-grab中的README文件。

注意! ZMap和banner-grab如例子中同时运行可能会比较显著的影响对方的表现和精度。确保不让ZMap占满banner-grab-tcp的并发连接不然banner-grab将会落后于标准输入的读入导致阻塞ZMap的输出写入。我们推荐使用较慢扫描速率的ZMap同时提升banner-grab-tcp的并发性至3000以内注意 并发连接>1000需要您使用ulimit -SHn 100000ulimit -HHn 100000来增加每个进程的最大文件描述符数量。当然这些参数取决于您服务器的性能、连接成功率hit-rate我们鼓励开发者在运行大型扫描之前先进行小样本的试验。

建立套接字

我们也收录了另一种形式的banner-grab就是forge-socket 重复利用服务器发出的SYN-ACK连接并最终取得banner。在banner-grab-tcpZMap向每个服务器发送一个SYN并监听服务器发回的带有SYN+ACK的应答。运行ZMap主机的内核接受应答后发送RST这样就没有与该包关联活动连接。程序banner-grab必须在这之后创建一个新的TCP连接到从服务器获取数据。

在forge-socket中我们利用内核中同名的模块使我们可以创建任意参数的TCP连接。可以通过抑制内核的RST包并重用SYN+ACK的参数取代该包而创建套接字通过这个套接字收发数据和我们平时使用的连接套接字并没有什么不同。

要使用forge-socket您需要forge-socket内核模块github上可以获得。您需要git clone git@github.com:ewust/forge_socket.git至ZMap源码根目录然后cd进入forge_socket目录运行make。以root身份运行insmod forge_socket.ko 来安装该内核模块。

您也需要告知内核不要发送RST包。一个简单的在全系统禁用RST包的方法是使用iptables。以root身份运行iptables -A OUTPUT -p tcp -m tcp --tcp-flgas RST,RST RST,RST -j DROP即可,当然您也可以加上一项--dport X将禁用局限于所扫描的端口X上。扫描完成后移除这项设置以root身份运行iptables -D OUTPUT -p tcp -m tcp --tcp-flags RST,RST RST,RST -j DROP即可。

现在应该可以建立forge-socket的ZMap示例程序了。运行需要使用extended_fileZMap输出模块

$ zmap -p 80 -N 1000 -B 10M -O extended_file -o - | \
    ./forge-socket -c 500 -d ./http-req > ./http-banners.out

详细内容可以参考examples/forge-socket目录下的README。


编写探测和输出模块

ZMap可以通过探测模块来扩展支持不同类型的扫描,通过输出模块增加不同类型的输出结果。注册过的探测和输出模块可以在命令行中列出:

--list-probe-modules

列出安装过的探测模块

--list-output-modules

列出安装过的输出模块

输出模块

ZMap的输出和输出后处理可以通过实现和注册扫描器的输出模块来扩展。输出模块在接收每一个应答包时都会收到一个回调。然而默认提供的模块仅提供简单的输出这些模块同样支持更多的输出后处理例如重复跟踪或输出AS号码来代替IP地址

通过定义一个新的output_module结构来创建输出模块并在output_modules.c中注册:

typedef struct output_module {
    const char          *name;           // 在命令行如何引用输出模块
    unsigned            update_interval; // 以秒为单位的更新间隔
	
    output_init_cb      init;            // 在扫描器初始化的时候调用
    output_update_cb    start;           // 在扫描器开始的时候调用
    output_update_cb    update;          // 每次更新间隔调用,秒为单位
    output_update_cb    close;           // 扫描终止后调用
	
    output_packet_cb    process_ip;      // 接收到应答时调用

    const char          *helptext;       // 会在--list-output-modules时打印在屏幕上
  
} output_module_t;

输出模块必须有名称,通过名称可以在命令行调用,并且通常会实现success_ip和常见的other_ip回调。process_ip的回调由每个收到并经由probe module过滤的应答包调用。应答是否被认定为成功并不确定比如它可以是一个TCP的RST。这些回调必须定义匹配output_packet_cb定义的函数:

int (*output_packet_cb) (

    ipaddr_n_t    saddr,         // 网络字节格式的发起扫描主机IP地址
    ipaddr_n_t    daddr,         // 网络字节格式的目的IP地址
    
    const char*   response_type, // 发送模块的数据包分类
    
    int           is_repeat,     // {0: 主机的第一个应答, 1: 后续的应答}
    int           in_cooldown,   // {0: 非冷却状态, 1: 扫描器处于冷却中}
    
    const u_char* packet,        // 指向IP包的iphdr结构体的指针
    size_t        packet_len     // 包的长度,以字节为单位
);

输出模块还可以通过注册回调,执行在扫描初始化的时候(诸如打开输出文件的任务)、在扫描开始阶段(诸如记录黑名单的任务)、在扫描的常规间隔(诸如状态更新的任务)、在关闭的时候(诸如关掉所有打开的文件描述符)。提供的这些回调可以完整的访问扫描配置和当前状态:

int (*output_update_cb)(struct state_conf*, struct state_send*, struct state_recv*);

这些定义在output_modules.h中。在src/output_modules/module_csv.c中有可用示例。

探测模块

数据包由探测模块构造它可以创建各种包和不同类型的响应。ZMap默认拥有两个扫描模块tcp_synscanicmp_echoscan。默认情况下ZMap使用tcp_synscan来发送TCP SYN包并对每个主机的响应分类如打开时收到SYN+ACK或关闭时收到RST。ZMap允许开发者编写自己的ZMap探测模块使用如下的API

任何类型的扫描都必须通过开发和注册send_module_t结构中的回调来实现:

typedef struct probe_module {
    const char               *name;             // 如何在命令行调用扫描
    size_t                   packet_length;     // 探测包有多长(必须是静态的)
    
    const char               *pcap_filter;      // 对收到的响应实施PCAP过滤
    size_t                   pcap_snaplen;      // libpcap 捕获的最大字节数 
    uint8_t                  port_args;         // 设为1如果ZMap需要用户指定--target-port
    
    probe_global_init_cb     global_initialize; // 在扫描初始化会时被调用一次
    probe_thread_init_cb     thread_initialize; // 每个包缓存区的线程中被调用一次
    probe_make_packet_cb     make_packet;       // 每个主机更新包的时候被调用一次
    probe_validate_packet_cb validate_packet;   // 每收到一个包被调用一次,
                                                // 如果包无效返回0
                                                // 非零则有效。
    
    probe_print_packet_cb    print_packet;      // 如果在预扫模式下被每个包都调用
    probe_classify_packet_cb process_packet;    // 由区分响应的接收器调用
    probe_close_cb           close;             // 扫描终止后被调用

    fielddef_t               *fields            // 该模块指定的字段的定义
    int                      numfields          // 字段的数量
    
} probe_module_t;

在扫描操作初始化时会调用一次global_initialize,可以用来实施一些必要的全局配置和初始化操作。然而,global_initialize并不能访问包缓冲区,那里是线程特定的。代替的,thread_initialize在每个发送线程初始化的时候被调用提供对于缓冲区的访问可以用来构建探测包和全局的源和目的值。此回调应用于构建主机不可知的包结构甚至只有特定值目的主机和校验和需要随着每个主机更新。例如以太网头部信息在交换时不会变更减去校验和是由NIC硬件计算的因此可以事先定义以减少扫描时间开销。

调用回调参数make_packet是为了让被扫描的主机允许探测模块更新主机指定的值同时提供IP地址、一个非透明的验证字符串和探测数目如下所示。探测模块负责在探测中放置尽可能多的验证字符串即便当服务器返回的应答为空时探测模块也能验证它的当前状态。例如针对TCP SYN扫描tcp_synscan探测模块会使用TCP源端口和序列号的格式存储验证字符串。响应包SYN+ACK将包含目的端口和确认号的预期值。

int make_packet(
    void        *packetbuf,  // 包的缓冲区
    ipaddr_n_t  src_ip,      // 网络字节格式源IP
    ipaddr_n_t	dst_ip,      // 网络字节格式目的IP
    uint32_t    *validation, // 探测中的有效字符串
    int         probe_num    // 如果向每个主机发送多重探测,
                             // 该值为我们对于该主机
                             // 正在发送的探测数目
);

扫描模块也应该定义pcap_filtervalidate_packetprocess_packet。只有符合PCAP过滤器的包才会被扫描。举个例子在一个TCP SYN扫描的情况下我们只想要调查TCP SYN / ACK或RST TCP数据包并利用类似tcp && tcp[13] & 4 != 0 || tcp[13] == 18的过滤方法。validate_packet函数将会被每个满足PCAP过滤条件的包调用。如果验证返回的值非零将会调用process_packet函数,并使用fields定义的字段和包中的数据填充字段集。举个例子如下代码为TCP synscan探测模块处理了一个数据包。

void synscan_process_packet(const u_char *packet, uint32_t len, fieldset_t *fs)
{
    struct iphdr *ip_hdr = (struct iphdr *)&packet[sizeof(struct ethhdr)];
    struct tcphdr *tcp = (struct tcphdr*)((char *)ip_hdr 
            + (sizeof(struct iphdr)));

    fs_add_uint64(fs, "sport", (uint64_t) ntohs(tcp->source)); 
    fs_add_uint64(fs, "dport", (uint64_t) ntohs(tcp->dest));
    fs_add_uint64(fs, "seqnum", (uint64_t) ntohl(tcp->seq));
    fs_add_uint64(fs, "acknum", (uint64_t) ntohl(tcp->ack_seq));
    fs_add_uint64(fs, "window", (uint64_t) ntohs(tcp->window));

    if (tcp->rst) { // RST packet
        fs_add_string(fs, "classification", (char*) "rst", 0);
        fs_add_uint64(fs, "success", 0);
    } else { // SYNACK packet
        fs_add_string(fs, "classification", (char*) "synack", 0);
        fs_add_uint64(fs, "success", 1);
    }
}

via: https://zmap.io/documentation.html

译者:martin2011qi 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出