【导语】:Masscan 是一个批量端口扫描器,程序运行在单机上,每秒传输 1 千万个数据包,可以在 6 分钟之内扫完整个互联网。它是迄今为止,速度最快的端口扫描器。

1.4 万 Star!迄今为止速度最快的端口扫描器-LMLPHP

简介

Masscan 的输入输出类似于 nmap,如果对工具的性能将信将疑,可以手动去尝试一把。

该工具使用异步传输,类似于scanrand、unicornscan 和 ZMap 等扫描器。并且它更加灵活,允许自定义端口和 IP 地址范围。

注意:masscan 使用它自己的 TCP/IP 栈,可能会和其他的端口扫描器冲突。这意味着,你要么使用 --src-ip 选项来配置一个与本地不同的 IP 地址 ,要么使用 --src-port 选项来配置 Massscan 使用的源端口。也可以配置内部防火墙(比如pf或者iptables)将操作系统的其他端口墙在外面。

这个工具免费,但是大家的爱心捐助可以帮助它更好的发展;比特钱包地址如下:

1MASSCANaHUiyTtR3bJ2sLGuMw5kDBaj4T

项目地址:

https://github.com/robertdavidgraham/masscan

构建

在Debian/Ubuntu,使用如下命令安装:

$ sudo apt-get install git gcc make libpcap-dev
$ git clone https://github.com/robertdavidgraham/masscan
$ cd masscan
$ make

Massscan程序默认在masscan/bin子目录下。如果你想把这个程序安装在系统的其他地方,例如/usr/local/bin,需要自己手动复制。

该项目由大量的小文件组成,可以使用多线程来完成快速构建:

$ make -j

虽然 Linux 是该工具的主要运行平台,但是它也可以在其他的操作系统上很好的运行。

下面是一些其他的构建信息:

  • Windows w/ Visual Studio: 使用 VS10

  • Windows w/ MingGW: 使用make

  • Windows w/ cygwin: 不支持

  • Mac OS X /w XCode: 使用 XCode4

  • Mac OS X /w cmdline: 使用make

  • FreeBSD: 使用gmake

  • 其他系统: 尝试把所有的文件一起编译

PF_RING驱动安装

如果发包量要超过20万/秒,需要因特尔的网卡,网卡要求10Gbps。除此之外,还需要一个驱动,名字叫PF_RING ZC。

要想使用PF_RING ZC,还需要安装下面的组件:

  • libpfring.so (安装在/usr/lib/libpfring.so)

  • pf_ring.ko (PF-RING自己的内核驱动)

  • ixgbe.ko (因特尔10-gbps网卡驱动)

此外,还需要构建libpcap.so文件。当Masscan探测到网卡为zc:enp1s0时,就会自动切换到PF_RING ZC模式。

回归测试

这个工具内嵌自测组件,可以自己进行测试:

$ make regress
bin/masscan --regress
selftest: success!

以上的回归测试涵盖大量的复杂代码,你应该在构建后再尝试。

性能测试

为了测试工具性能,可以执行下面的命令:

$ bin/masscan 0.0.0.0/4 -p80 --rate 100000000 --router-mac 66-55-44-33-22-11

选项--router-mac虚拟出来一个假的路由mac地址,所以发送的数据包都在本地,不会跑到互联网上。

你也可以测试offline模式,看看在没有网络开销的情况下,这个程序能跑多快:

$ bin/masscan 0.0.0.0/4 -p80 --rate 100000000 --offline

在offline模式下,进行性能测试,这个程序就会使用PF_RING,几乎没有网络开销。

工具的使用

这个工具使用类似nmap。比如扫描某网段的一些端口:

# masscan -p80,8000-8100 10.0.0.0/8

以上的命令完成下面3个事情:

  • 扫描10.x.x.x子网,一共160万地址

  • 扫描端口80,还有8000-8100范围内的端口,一共102个

  • 扫描结果可以输出到指定文件

使用--echo,可以看到工具完整的选项列表。命令会抛出当前配置并退出。这个输出也可以用作程序的输入。

# masscan -p80,8000-8100 10.0.0.0/8 --echo > xxx.conf
# masscan -c xxx.conf --rate 1000

banner信息获取

Masscan不仅可以探测端口的开放状态,它也可以完成TCP连接以及和对应端口应用之间进行交互。通过跟应用程序交互,可以获取到简单的banner信息。

这是因为masscan拥有自己的TCP/IP协议栈,跟程序所在操作系统的协议栈是不同的。当本地系统接收到被探测目标的SYN-ACK包时,它会回应一个RST包。但是之前masscan为了获取banner信息而建立的连接,会在此时关闭。

解决这个问题最简单的办法就是,为masscan指定一个IP地址。比如下面的命令:

# masscan 10.0.0.0/8 -p80 --banners --source-ip 192.168.1.200

这个指定的地址必须要在本地地址的子网中,否则不能在其他系统上使用。

在一些场景中,比如wifi,这种操作就不起作用了。在这些场景中,你也可以用防火墙来解决上面连接中断的问题。防火墙会阻止本地的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和masscan端口冲突的问题。把linux使用的端口划定一个范围:

/proc/sys/net/ipv4/ip_local_port_range

在2018年8月的kali中,这个范围是32768 to 60999,因此,选择的端口应该在这些端口之外。

可以设置一个iptables规则,时间限制到下次机器重启之前。如何保存这个配置取决于你的linux发行版。一般是用iptables-save或者是iptables-persistant。

在Mac OS X和BSD上,也是类似的步骤。可以使用下面的命令来查找这个范围:

# sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last

在FreeBSD和更早的Mac OS版本上,使用如下的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 X版本还有OpenBSAD上,使用pf包过滤器。在/etc/pf.conf文件中加入下面一行:

block in proto tcp from any to any port 40000

然后启用防火墙,运行下面的命令:

# pfctrl -E

如果防火墙已经启用,要么重启机器,要么使用下面的命令,重新导入这个规则:

# pfctl -f /etc/pf.conf

Window不会回应RST包,以上的方式都是无效的。但是,masscan仍然可以使用它自己的IP地址很好的运行。因此,如果可能的话,你应该使用为masscan指定IP的方法。

对于比如像心脏滴血漏洞(选项为--heartbleed)的探测,也是banner信息探测的一种,是十分必要的。

如何扫描整个互联网

虽然masscan对于内网来说十分有效,但是当初设计也考虑到了整个互联网。所以,它可以使用如下的命令,直接扫描整个互联网:

# masscan 0.0.0.0/0 -p0-65535

扫描整个互联网是不好的。一方面,有一部分被扫描的信息资产不会返回有效的信息。另一方面,一些站点会跟踪扫描,禁止你再访问。因此,你要想扫描很多大量的地址,可以设置黑名单或者例外IP范围。如下:

# 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

上面的命令把扫描速率设置到了10万数据包/秒,这个设置扫描整个互联网的IP,速率大概是每10个小时一个端口。如果想扫描整个端口,那就需要655360个小时。

以上所有的命令都跟nmap兼容。除此之外,还有一些隐形设置选项跟nmap也是一样的。例如:-sS ,-Pn, -n ,--randomize-hosts, --send-eth等。同样的,扫描结果输出的xml文件也是跟nmap兼容的。

当然,masscan跟nmap还有有很多不同的地方。本质上来说,masscan是异步传输的实现方式,这导致它跟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

启用以上配置:

# masscan -c myscan.conf

当你重复扫描时,以上的方式将会让扫描更加简单。

默认的,masscan首次启动就会导入配置文件:/etc/masscan/masscan.conf。但之后的配置参数修改都会覆盖之前的默认文件。整个工作都是自动化完成的。

结果输出

默认的,masscan会产生同样大小的文本文件。同时这个文件也可以很容易的转换成其他格式。下面有五种可以支持的格式:

  • xml: 使用参数-oX <filename>. 或者使用参数 --output-format xml 和--output-filename <filename>.

  • binary: 这是masscan内建指令. 它输出的文件更小, 扫描整个互联网可以用这个参数,不至于把硬盘塞满。它还需要解析. 命令选项--readscan将会读取这个二进制扫描文件。使用--readscan再搭配-oX选项就会输出一个xml的扫描结果文件。

  • grepable: 这是nmap的-oG选项实现的,输出结果很容易被命令行工具解析。只需要使用参数-oG <filename>或者使用--output-format grepable和 --output-filename <filename>.

  • json: 输出结果保存为json,使用参数-oJ <filename>或者--output-format json 和--output-filename <filename>。

  • list: 这是一个list,每行一个ip和端口对儿。使用参数-oL <filename>或者--output-format list和--output-filename <filename>. 格式是:

<port state> <protocol> <port number> <IP address> <POSIX timestamp>
open tcp 80 XXX.XXX.XXX.XXX 1390380064

与Nmap的比较

尽管masscan跟nmap从根本上来说是完全不同的,但是开发者尽了最大的努力让namp的用户使用masscan时会觉得比较熟悉。下面是这两款工具之间最重要的两个不同点:

  • 不指定默认扫描端口, 你必须明确扫描端口:-p <ports>

  • 目标主机是IP地址或者IP地址范围, 而不是DNS名或者nmap使用的子网范围(比如10.0.0-255.0-255).

你可以认为masscan的以下选项是长期有效的:

-sS: 只进行SYN探测 (目前是这样,将来会改进)
-Pn: 不首先ping主机,这是异步传输的基本要求
-n: 不进行DNS解析
--randomize-hosts: 完全随机化扫描
--send-eth: 发送原生数据包如果你想了解其他和nmap兼容的设置,使用下面的命令:

# masscan --nmap

传输速率(重要!!!)

这个程序发送数据包非常快。在windows或者是虚拟机中,可以达到30万/秒的速率。在linux(不是虚拟机)上,可达到160万/秒。速度快到可以摧毁大部分的网络。

注意,这个工具只能用来自己破坏自己的网络。该工具在使用时随机选择目标IP,不应该用来破坏任何一个远程网络。

默认的扫描速率是100数据包/秒。可以使用下面的选项来提高速率到一百万:--rate 1000000。

以下几部分将主要介绍Masscan的设计问题,帮助你了解为什么本工具会有这么强大的性能。感兴趣的同学,可继续往下阅读。

Code Layout

main.c文件包含了main()函数,也包含了transmit_thread()和 receive_thread()函数。这些函数刻意写的比较通俗易懂,且有大量的注释。你可以逐行阅读这些代码来了解这个程序的设计。

异步传输

masscan是一个异步传输的设计。换句话说,它和nmap的关系就好比nginx和apache的关系。它的发送和接收线程是分开的,相互之间具有极大的独立性。在scanrand, unicornscan和ZMap中也有同样的设计。

由于它的异步性,包允许传输多块,该工具就可以运行多块。

随机选择

Masscan和其他扫描器之间一个最关键的区别在于它的目标地址是随机的。

最基本的原则是定义一个从0开始的索引变量,在每次探测时,每次增加1。用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。然后,后来的160万个索引对应的目标地址是10.x.x.x。最后,其余的索引对应的目标地址范围是172.16.x.x。

上面的例子我们只有3个范围。当扫描整个互联网的时候,实际上会超过100个范围。那是因为你必须设置黑名单还有很多排除在外的子网范围。这个把想扫描的范围分割成了成百个更小的范围。

这就导致程序运行变慢。我们每秒传输100万个包,并且必须每次传输包和探测都需要将索引变量转换成IP地址。我们通过使用二分法来解决这个问题,而且占用很少的内存。包传输在这种速率下,缓存效率开始影响算法效率。

理论上还存在大量更有效的技术手段,但是,在实际应用中,这些手段都因为占用过多内存而导致速度变的更慢。

我们声明一个函数pick(),用来实现把索引转换成IP地址。实现如下:

for (i = 0; i < range; i++) {
    ip = pick(addresses, i);
    scan(ip);
}

Masscan不仅支持IP范围,也支持端口范围。这意味着,我们需要从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);
}

这又增加了代码的另一个负担。在X86架构的CPU上,除法/取模运算指令大约需要90个时钟周期或者30纳秒。当以100万包/秒的速率传输时,每个包仅仅只有100纳秒的时间。我找不到更加优化的方式了。

幸运的是,两个这样的操作可以同时进行。因此,做上面的两个运算不会比做一个增加负担。

事实上,对于上述的运算问题,存在着更优的选择。但是逐一增加索引变量要通过C++实现。实际上,我们需要随机化这个变量。我们要随机化目标IP地址的扫描顺序,否则由于扫描速度过快,会破坏目标网络。我们要均匀的将流量分摊到目标地址上。

我们随机化的方式非常简单,就是加密索引变量。根据定义,加密是随机的,并且在原始的索引变量和输出之间增加一个一对一的mapping。

这意味着,我们线性的完成了这个范围,但是输出的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的倍数,我们就不能用比较节约资源的二进制技术,比如&和^。相反,我们就必须使用%操作符。在我当前的基准下,加密一个变量需要花费40纳秒的时间。

这个架构允许很多比较酷的特性。例如,支持分片。你可以选择5个机器,每个机器设置五分之一的扫描,或者使用范围数/分片数。

分片可以通过多台机器实现,或者单台机器多个网卡实现。如果你想,也可以在一个网卡上分配多个IP地址来实现分片。

或者,你也可以在加密函数上使用一个seed或一个key,以此,每次扫描时,你可以得到一个不同的顺序。比如x = encrypt(seed, i)。

我们也可以通过退出程序来暂停扫描,并且记住当前的i值,之后再重启扫描。在整个开发过程中,我做了很多。我注意到,在互联网扫描时,出现了错误,我就去终止扫描。然后在我修复bug后,再重启。

另一个特点就是重发。数据包在网络传输中有时会丢失,因此你可以接连发两个包。但是,有时会出现一个包丢失,另一个紧跟着的包也丢失的情况。

因此,你想要发送数据的二分之一的副本,这很简单。我们有一个rate变量,这个变量是每秒钟传输数据包的数量,因此重新传输的函数就是在索引的基础上,简单用i+rate即可。

C10扩展

异步传输技术因为解决了C10K问题而被熟知。Masscan的设计是为了下一个量级扩展:C10M问题。C10M问题的解决方式就是绕过内核。在Masscan中,主要有三种内核绕过方式:

  • 自定义网络驱动

  • 用户模式TCP栈

  • 用户模式异步传输

Masscan可以使用PF_RING DNA驱动器。这个驱动采用直接内存访问的方式传输数据包。数据包传输没有内核的干预,直接从用户模式内存到网络驱动器。

这种方式允许软件以硬件可支持的最大速率传输数据包,即使是一个低速CPU。如果一台电脑上有8个10-gbps个网卡,这意味着,你的传输速度可以达到一亿/秒。

Masscan有它自己内建的TCP栈,这是为了可以从TCP连接中获取banner。这意味着如果电脑有足够的内存,它可以轻易的支持一百万并发的TCP连接。

Masscan没有互斥锁。现代的互斥锁(aka. futexes)主要是用户模式,但是他们都存在两个问题。

第一个问题是,他们造成了缓存链在CPU之间来回跳动;第二个问题是,当存在资源争夺时,他们会做一个系统声明,进入内核。这会杀掉性能。

互斥锁在程序的速度之路上,严重限制了程序的可扩展性。相反,Masscan 使用“环”来同步处理这个事情,比如,当用户模式下的TCP栈在接收一个线程时,这个线程传输的包不能被这个传输线程干涉。

移植性

这个代码可以很好的在linux,windows还有mac os X上运行。所有重要位都基于C90标准。因此可以使用微软的Visual Studio 编译。还可以使用Clang/LLVM在Mac OS X上编译,也可以用GCC在linux上完成编译。

windows和Mac上不能设置传输包的数量,只能是默认值30万/秒,然而linux上可以做到150万/秒。不管怎样,都会比你想象中要快。

代码安全

该项目为脆弱性发现提供了奖金,具体信息查看文件 VULNINFO.md。

这个项目使用了安全函数strcpy_s(),strcpy()函数不安全。

这个项目有自动化单元回归测试。

兼容性

为了让每个使用端口扫描的人都有种熟悉的感觉,该工具的开发中做了很多的努力,让它的输入/输出更像是nmap。

作者

这个工具由Robert Graham开发。
邮箱: [email protected]
twitter: @ErrataRob

项目地址: https://github.com/robertdavidgraham/masscan

04-11 12:34