auraecosystem/masscan
GitHub: auraecosystem/masscan
Masscan 是一款高效的异步 TCP 端口扫描器,用于快速扫描整个网络。
Stars: 0 | Forks: 0
[](https://github.com/robertdavidgraham/masscan/actions/workflows/unittests.yml/?branch=master)
# MASSCAN: 大规模IP端口扫描器
这是一个互联网规模的端口扫描器。它可以在5分钟内扫描整个互联网,
每秒传输1000万个数据包,从一台机器上。
它的用法(参数,输出)与最著名的端口扫描器 `nmap` 类似。
如有疑问,请尝试以下功能--支持广泛扫描许多机器的功能,
而单机深度扫描则不支持。
内部,它使用异步传输,类似于 `scanrand`、`unicornscan` 和 `ZMap` 这样的端口扫描器。
它更灵活,允许任意端口和地址范围。
注意:masscan 使用自己的 **临时 TCP/IP 堆栈**。除了简单的端口扫描之外,
还可能与其他本地 TCP/IP 堆栈发生冲突。这意味着您需要使用 `--src-ip` 选项
从不同的 IP 地址运行,或者使用 `--src-port` 来配置 masscan 使用的源端口,
然后配置内部防火墙(如 `pf` 或 `iptables`)来防火墙这些端口。
# 构建
在 Debian/Ubuntu 上,它可能如下所示。它实际上没有其他依赖项,
除了 C 编译器(例如 `gcc` 或 `clang`)。
```
sudo apt-get --assume-yes install git make gcc
git clone https://github.com/robertdavidgraham/masscan
cd masscan
make
```
这会将程序放入 `masscan/bin` 子目录。
要安装它(在 Linux 上)运行:
```
make install
```
由于源代码由许多小文件组成,因此使用多线程构建可以更快地构建。
这需要在树莓派上超过2gigs(并且会崩溃),因此您可能需要使用较小的数字,
例如 `-j4` 而不是所有可能的线程。
```
make -j
```
虽然 Linux 是主要的目标平台,但代码在许多其他系统(Windows、macOS 等)上运行良好。
以下是一些额外的构建信息:
* Windows 与 Visual Studio:使用 VS10 项目
* Windows 与 MinGW:只需键入 `make`
* Windows 与 cygwin:无法工作
* Mac OS X /w XCode:使用 XCode4 项目
* Mac OS X /w 命令行:只需键入 `make`
* FreeBSD:键入 `gmake`
* 其他:尝试将所有文件一起编译,`cc src/*.c -o bin/masscan`
在 macOS 上,x86 二进制文件似乎在 ARM 模拟下运行得同样快。
# 使用
用法类似于 `nmap`。要扫描网络段的一些端口:
```
# masscan -p80,8000-8100 10.0.0.0/8 2603:3001:2d00:da00::/112
```
这将:
* 扫描 `10.x.x.x` 子网和 `2603:3001:2d00:da00::x` 子网
* 扫描两个子网上的端口 80 和 8000 到 8100 范围,或总共 102 个端口
* 将输出打印到 ``,可以重定向到文件
要查看完整的选项列表,请使用 `--echo` 功能。这将输出当前配置并退出。
此输出可以用作程序的输入:
```
# masscan -p80,8000-8100 10.0.0.0/8 2603:3001:2d00:da00::/112 --echo > xxx.conf
# masscan -c xxx.conf --rate 1000
```
## 标题检查
Masscan 可以做的不仅仅是检测端口是否开放。它还可以完成与该端口的 TCP 连接和交互,
以获取简单的“标题”信息。
Masscan 支持以下协议的标题检查:
* FTP
* HTTP
* IMAP4
* memcached
* POP3
* SMTP
* SSH
* SSL
* SMBv1
* SMBv2
* Telnet
* RDP
* VNC
问题是,masscan 包含自己的 TCP/IP 堆栈,
与您运行的系统分开。当本地系统收到来自被探测目标的 SYN-ACK 时,
它会响应一个 RST 数据包,在 masscan 抓取标题之前终止连接。
最容易防止这种情况的方法是为 masscan 分配一个单独的 IP 地址。
这看起来像以下示例之一:
```
# masscan 10.0.0.0/8 -p80 --banners --source-ip 192.168.1.200
# masscan 2a00:1450:4007:810::/112 -p80 --banners --source-ip 2603:3001:2d00:da00:91d7:b54:b498:859d
```
您选择的地址必须位于本地子网中,并且不应被其他系统使用。
Masscan 将警告您已犯错误,但您可能会使其他机器的通信中断几分钟,所以请小心。
在某些情况下,例如 WiFi,这可能不可行。
在这种情况下,您可以防火墙 masscan 使用的端口。
这阻止了本地 TCP/IP 堆栈看到数据包,但 masscan 仍然可以看到它,因为它绕过了本地堆栈。
对于 Linux,这看起来像:
```
# iptables -A INPUT -p tcp --dport 61000 -j DROP
# masscan 10.0.0.0/8 -p80 --banners --source-port 61000
```
您可能希望选择不与 Linux 可能选择的源端口号冲突的端口。
您可以通过查看以下文件来查看 Linux 使用的范围,并重新配置该范围:
```
/proc/sys/net/ipv4/ip_local_port_range
```
在最新的 Kali Linux(2018 年 8 月)版本中,该范围是 32768 到 60999,因此
您应该选择低于 32768 或 61000 以上的端口。
设置 `iptables` 规则仅在重新启动后持续。您需要查找如何根据您的发行版保存配置,
例如使用 `iptables-save` 和/或 `iptables-persistent`。
在 Mac OS X 和 BSD 上,有类似的步骤。
要找出要避免的范围,请使用以下命令:
```
# sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last
```
在 FreeBSD 和较旧的 MacOS 上,使用 `ipfw` 命令:
```
# sudo ipfw add 1 deny tcp from any to any 40000 in
# masscan 10.0.0.0/8 -p80 --banners --source-port 40000
```
在较新的 MacOS 和 OpenBSD 上,使用 `pf` 数据包过滤器实用程序。
编辑 `/etc/pf.conf` 文件,添加以下行:
```
block in proto tcp from any to any port 40000:40015
```
然后,要启用防火墙,请运行以下命令:
```
# pfctl -E
```
如果防火墙已经运行,则重新启动或使用以下命令重新加载规则:
```
# pfctl -f /etc/pf.conf
```
Windows 不响应 RST 数据包,因此这些技术都不是必需的。
但是,masscan 仍然设计为最好使用自己的 IP 地址,因此当可能时,
即使不是严格必需的,也应这样运行。
对于其他检查,例如 `--heartbleed` 检查,这也只是标题检查的一种形式。
## 如何扫描整个互联网
虽然对于较小的内部网络很有用,但该程序实际上是为整个互联网设计的。
它可能看起来像这样:
```
# masscan 0.0.0.0/0 -p0-65535
```
扫描整个互联网是坏事。一方面,互联网的一部分对扫描反应不佳。
另一方面,一些网站跟踪扫描并将您添加到黑名单中,
这将阻止您访问互联网的有用部分。
因此,您想要排除很多范围。
要黑名单或排除范围,请使用以下语法:
```
# masscan 0.0.0.0/0 -p0-65535 --excludefile exclude.txt
```
这仅将结果打印到命令行。您可能希望将它们保存到文件中。
因此,您想要如下所示:
```
# masscan 0.0.0.0/0 -p0-65535 -oX scan.xml
```
这将结果保存到 XML 文件中,允许您轻松地将结果导入数据库或其他内容。
但是,这仅以每秒 100 个数据包的默认速率进行,这将永远扫描互联网。
您需要将其速度提高如下:
```
# masscan 0.0.0.0/0 -p0-65535 --max-rate 100000
```
这将速率提高到每秒 100,000 个数据包,这将扫描整个互联网(减去排除项)。
注意此命令行中的内容,这些都是 `nmap` 兼容选项。
此外,还为您设置了与 `nmap` 兼容的“不可见”选项:
`-sS -Pn -n --randomize-hosts --send-eth`。
同样,XML 文件的格式也受到 `nmap` 的启发。
当然,由于程序 *异步* 的本质,导致与 `nmap` 有很多不同之处。
上面的命令行有点繁琐。
与其将所有内容放在命令行上,不如将其存储在文件中。
上面的设置看起来像这样:
```
# My Scan
rate = 100000.00
output-format = xml
output-status = all
output-filename = scan.xml
ports = 0-65535
range = 0.0.0.0-255.255.255.255
excludefile = exclude.txt
```
要使用此配置文件,请使用 `-c`:
```
# masscan -c myscan.conf
```
这也使得重复扫描时更容易。
默认情况下,masscan 首先加载配置文件 `/etc/masscan/masscan.conf`。
任何后续配置参数都将覆盖默认配置文件中的内容。
这就是我将我的“excludefile”参数放在那里的原因,这样我就永远不会忘记它。
它只是自动工作。
## 获取输出
默认情况下,masscan 生成相当大的文本文件,但很容易将它们转换为其他格式。
有五种支持输出格式:
1. xml:只需使用参数 `-oX `。
或者,使用参数 `--output-format xml` 和 `--output-filename `。
2. binary:这是 masscan 内置格式。它生成更小的文件,这样当我在扫描互联网时,
我的磁盘就不会填满。但是需要解析。命令行选项 `--readscan` 将读取二进制扫描文件。
使用 `--readscan` 与 `-oX` 选项将生成结果文件的 XML 版本。
3. grepable:这是 Nmap -oG 输出的实现,可以很容易地被命令行工具解析。
只需使用参数 `-oG `。
或者,使用参数 `--output-format grepable` 和 `--output-filename `。
4. json:将结果保存为 JSON 格式。只需使用参数 `-oJ `。
或者,使用参数 `--output-format json` 和 `--output-filename `。
5. list:这是一个简单的列表,每行一个主机和端口对。
只需使用参数 `-oL `。
或者,使用参数 `--output-format list` 和 `--output-filename `。
格式如下:
open tcp 80 XXX.XXX.XXX.XXX 1390380064
## 与 Nmap 的比较
在合理的情况下,已经尽力使程序对 `nmap` 用户熟悉,
尽管它与 `nmap` 基本上不同。Masscan 调优用于大量机器的广泛扫描,
而 nmap 是为单机或小范围密集扫描而设计的。
两个重要差异是:
* 没有默认端口要扫描,您必须指定 `-p `
* 目标主机是 IP 地址或简单范围,而不是 DNS 名称,也不是 `nmap` 可以使用的
(例如 `10.0.0-255.0-255`)的奇特子网范围。
您可以将 `masscan` 视为以下设置始终启用:
* `-sS`:这仅进行 SYN 扫描(目前,将来会更改)
* `-Pn`:不首先ping主机,这是异步操作的基本原则
* `-n`:不进行 DNS 解析
* `--randomize-hosts`:始终完全随机扫描,您无法更改此设置
* `--send-eth`:使用原始 `libpcap` 发送
如果您想获取额外的 `nmap` 兼容设置列表,请使用以下命令:
```
# masscan --nmap
```
## 传输速率(重要!)
此程序以非常快的速度发送数据包。在 Windows 或虚拟机上,
它可以达到每秒 300,000 个数据包。在 Linux(无虚拟化)上,
它将达到每秒 1600 万个数据包。这足以熔化大多数网络。
请注意,它只会熔化您自己的网络。
它随机化目标 IP 地址,这样它就不会压倒任何远程网络。
默认情况下,速率设置为每秒 100 个数据包。要将速率提高到一百万,
请使用类似 `--rate 1000000` 的内容。
在扫描 IPv4 互联网时,您将扫描许多子网,
因此尽管发送出去的数据包速率很高,但每个目标子网接收到的入站数据包速率仍然很小。
但是,在 IPv6 扫描中,您通常会关注单个目标子网,该子网有数十亿个地址。
因此,您的默认行为将压倒目标网络。
网络通常会在 masscan 产生的负载下崩溃。
# 设计
本节描述了程序的主要设计问题。
## 代码布局
文件 `main.c` 包含预期的 `main()` 函数。它还包含 `transmit_thread()` 和 `receive_thread()` 函数。
这些函数被故意简化并大量注释,以便您可以通过逐行阅读这些函数来简单地了解程序的设计。
## 异步
这是一个 *异步* 设计。换句话说,它与 `nmap` 的关系就像 `nginx` 网络服务器与 `Apache` 的关系一样。
它有独立的发送和接收线程,它们在很大程度上是独立的。
它与 `scanrand`、`unicornscan` 和 `ZMap` 中找到的设计类似。
由于它是异步的,因此它以底层数据包传输允许的速度运行。
## 随机化
Masscan 与其他扫描器之间的一个关键区别是它随机化目标的方式。
基本原理是有一个单个索引变量,它从零开始,并在每次探测时增加一个。
在 C 代码中,这表示为:
```
for (i = 0; i < range; i++) {
scan(i);
}
```
我们必须将索引转换为 IP 地址。假设您想扫描所有“私有”IP 地址。
这将是一个范围表,如下所示:
```
192.168.0.0/16
10.0.0.0/8
172.16.0.0/12
```
在这个例子中,前 64k 个索引附加到 192.168.x.x 以形成目标地址。
然后,下一个 1600 万个索引附加到 10.x.x.x。
该范围内的剩余索引应用于 172.16.x.x。
在这个例子中,我们只有三个范围。
当扫描整个互联网时,实际上有超过 100 个范围。
这是因为您必须黑名单或排除许多子范围。
这将所需范围分割成数百个更小的范围。
这导致代码中最慢的部分之一。
我们以每秒 1000 万个数据包的速率发送数据包,并且必须将每个探测的索引变量转换为 IP 地址。
我们通过在少量内存中进行“二分搜索”来解决这个问题。
在这个数据包速率下,缓存效率开始超过算法效率。
在理论上有很多更有效的方法,但它们都需要太多的内存,以至于在实践中会变慢。
我们将从索引变量转换为 IP 地址的函数称为 `pick()` 函数。
在使用时,它看起来像:
```
for (i = 0; i < range; i++) {
ip = pick(addresses, i);
scan(ip);
}
```
Masscan 不仅支持 IP 地址范围,还支持端口范围。
这意味着我们需要从索引变量中同时选择一个 IP 地址和一个端口。
这相当简单:
```
range = ip_count * port_count;
for (i = 0; i < range; i++) {
ip = pick(addresses, i / port_count);
port = pick(ports, i % port_count);
scan(ip, port);
}
```
这导致代码中的另一个昂贵部分。
除法/模运算指令在大约 90 个时钟周期内,或 30 纳秒内完成,
在 x86 CPU 上。以每秒 1000 万个数据包的速率发送时,我们每个数据包只有 100 纳秒。
我看不到任何方法可以更好地优化它。
幸运的是,不过,两个这样的操作可以同时执行,所以像上面那样执行两个,
并不执行一个更昂贵。
实际上,对于上述性能问题有一些简单的优化方法,
但它们都依赖于 `i++`,即索引变量在扫描过程中逐个增加的事实。
实际上,我们需要随机化这个变量。
我们需要随机化我们扫描的 IP 地址的顺序,否则我们会猛烈地攻击那些没有为这种速度而构建的目标网络。
我们需要将流量均匀地分布在目标上。
我们随机化的方式很简单,就是通过加密索引变量。
根据定义,加密是随机的,并在原始索引变量和输出之间创建 1 对 1 的映射。
这意味着虽然我们线性地遍历范围,但输出 IP 地址是完全随机的。
在代码中,这看起来像:
```
range = ip_count * port_count;
for (i = 0; i < range; i++) {
x = encrypt(i);
ip = pick(addresses, x / port_count);
port = pick(ports, x % port_count);
scan(ip, port);
}
```
这也带来了巨大的成本。
由于范围的大小是不可预测的,而不是漂亮的 2 的幂次方,
因此我们无法使用像 AND (&) 和 XOR (^) 这样的便宜二进制技术。
相反,我们必须使用像 MODULUS (%) 这样的昂贵操作。
在我的当前基准测试中,加密变量需要 40 纳秒。
这种架构允许很多酷炫的功能。
例如,它支持“碎片”。您可以设置 5 台机器,每台机器执行五分之一的扫描,
或者 `range / shard_count`。
碎片可以是多台机器,也可以是同一台机器上的多个网络适配器,
甚至(如果您想的话)同一网络适配器上的多个 IP 源地址。
或者,您可以使用加密函数的“种子”或“密钥”,这样每次扫描时都会得到不同的顺序,
例如 `x = encrypt(seed, i)`。
我们还可以通过退出程序来暂停扫描,并简单地记住 `i` 的当前值,
稍后重新启动它。我在开发过程中经常这样做。
我看到我的互联网扫描出了问题,所以我按 `` 停止扫描,然后在修复了错误后重新启动它。
另一个功能是重传/重试。
数据包有时会在互联网上丢失,因此您可以连续发送两个数据包。
但是,丢失一个数据包的东西可能会丢失紧接着的数据包。
因此,您希望将副本发送大约 1 秒钟。
这很简单。
我们已经有了一个 `rate` 变量,它是我们每秒发送的数据包数。
因此,重传函数只是使用 `i + rate` 作为索引。
有一天我将研究互联网,并使用这种方法区分“连续”、“1 秒”、“10 秒”和“1 分钟”重传,
以查看是否有任何差异。
## C10 可扩展性
异步技术被称为“c10k 问题”的解决方案。
Masscan 是为下一级可扩展性“C10M 问题”而设计的。
C10M 解决方案是绕过内核。
Masscan 中有三个主要的内核绕过:
* 自定义网络驱动程序
* 用户模式 TCP 堆栈
* 用户模式同步
Masscan 可以使用 PF_RING DNA 驱动程序。
此驱动程序将数据包直接从用户模式内存传输到网络驱动程序,无需内核参与。
这允许软件,即使 CPU 慢,也可以以硬件允许的最大速率发送数据包。
如果您在计算机中放置了 8 个 10-gbps 网络卡,
这意味着它可以以每秒 1 亿个数据包的速率发送。
Masscan 有自己的内置 TCP 堆栈,用于从 TCP 连接中抓取标题。
这意味着它可以轻松地支持 1000 万个并发 TCP 连接,当然前提是计算机有足够的内存。
Masscan 没有互斥锁。
现代互斥锁(也称为 futex)大多是用户模式的,
但有两个问题。第一个问题是它们会导致缓存行在 CPU 之间快速来回跳动。
第二个问题是当存在竞争时,它们会进行系统调用到内核,这会杀死性能。
程序快速路径上的互斥锁会严重限制可扩展性。
相反,Masscan 使用“环形”来同步事物,例如当接收线程中的用户模式 TCP 堆栈需要发送数据包而不会干扰发送线程时。
## 可移植性
代码在 Linux、Windows 和 Mac OS X 上运行良好。
所有重要部分都在标准 C(C90)中。
因此,它可以在 Visual Studio 上使用 Microsoft 的编译器编译,
在 Mac OS X 上使用 Clang/LLVM 编译器,在 Linux 上使用 GCC。
Windows 和 Macs 没有针对数据包传输进行调优,并且每秒只能达到大约 300,000 个数据包,
而 Linux 可以达到每秒 1500 万个数据包。这可能比您想要的要快。
## 安全代码
提供赏金以奖励漏洞,有关更多信息,请参阅 VULNINFO.md 文件。
此项目使用安全函数 `safe_strcpy()` 而不是不安全的函数 `strcpy()`。
此项目具有自动单元回归测试(`make regress`)。
## 兼容性
已经投入了大量精力使输入/
标签:DNS查询工具, IP扫描, Nmap, SYN扫描, TCP/IP, TCP扫描, 云存储安全, 域名解析, 大规模扫描, 客户端加密, 异步扫描, 数据统计, 日志审计, 正则表达式, 源IP, 源端口, 端口扫描, 端口扫描器, 系统工具, 编译, 网络安全, 网络扫描, 虚拟驱动器, 防火墙配置, 隐私保护