跳转到内容
Go back

在互联网端口扫描过程中寻找速度和准确度之间的平衡点

更新于:

在互联网端口扫描过程中寻找速度和准确度之间的平衡点

0x00 写在前面

最近一直在开发公司内部使用的 <巡检系统>,在涉及端口扫描这块,为了保证速度和准确性,方案是使用 Masscan+Nmap:Masscan 速度快,先使用 Masscan 进行一遍全端口探测。再使用 Nmap 对已经探测出来的端口进行复扫和 Banner 识别,Nmap 的准确性较高,但速度较慢,所以通过这种方式保证端口扫描的准确性。

这是我对于端口扫描过程中寻找速度与准确度之间平衡点的浅显思路。前几天刚好看到一篇国外的文章,介绍的东西远比我理解的更深入,故翻译出来一起学习。

如下内容为翻译内容,原文链接如下:

inding the Balance Between Speed & Accuracy During an Internet-wide Port Scanning

0x01 介绍

侦察是每个 bug bounty (漏洞赏金) 和渗透测试过程中最重要的阶段,一次好的侦察能决定成败。侦察可以分为两类:主动和被动。在主动侦察期间主要使用的方法之一就是端口扫描。渗透测试人员和 bug hunters (漏洞赏金猎人) 使用端口扫描确定目标主机和网络上的哪些端口是开放的,以及识别在这些端口上运行的服务。

但是,端口扫描总是需要在速度和精度之间进行权衡。在渗透测试期间,测试人员的时间都是有限的;而在 bug bounty 过程中,大家都是争先恐后的发现并提交漏洞,拼的是速度。这些原因迫使我们在端口扫描时优先考虑的是速度,而不是精度。而在于时间赛跑的过程中,我们可能会错过一些开放的端口,而恰巧这些端口可能就存在漏洞,并且能成功利用。

本次研究旨在利用开源和大家熟知的工具在端口扫描期间找到速度和准确度之间的平衡。

0x02 端口扫描概述

端口扫描是侦察期间最常用的技术之一。渗透测试人员和 bug bonty 用于识别主机上可用的开放端口,以及识别这些开放端口上运行的服务。

端口扫描器可以根据他们的操作方式分类为:面向连接(同步模式)扫描器和无连接的(异步模式)扫描器。

面向连接(同步模式)

这种类型的扫描器想目标端口发送请求并等待响应,直到超时时间到期。这种类型扫面器的缺点是性能比较慢,因为扫描器在当前连接关闭之前不会去扫描下一个目标端口或 ip。

面向连接的扫描器好处是它们更准确,因为它们可以识别丢弃的数据包。面向连接扫描器最流行就是我们熟知的 Nmap

无连接(异步模式)

无连接扫描器不依赖于当前被探测端口的完成来启动下一个端口,因为它们有单独的发送和接受线程。这允许它们进行高速扫描。但是,这些扫描器的结果可能不太准确,因为它们无法检测丢失的数据包。

MasscanZmap 是目前最流行的两种无连接扫描器。

0x03 Nmap VS Masscan

本次研究只包括 Nmap 和 Masscan。虽然 Zmap 是一个快速的扫描器,并且扫描结果还不错。但是根据经验,即使同时运行多个扫描任务,Zmap 的扫描速度仍然很慢。

虽然 Nmap 和 Masscan 都提供了良好的性能、特性和扫描结果。但它们 仍然有自己的弱点。下表展示了这两种工具的优缺点。

0x04 研究思路

基于上面列出的工具的有点和缺点,在试图找到速度和准确度之间的平衡时,确定了一下解决方案和问题。

解决方案

以下是基于工具的优点而形成的:

  1. 将 Nmap 的准确性及其其他的功能与 Masscan 的速度相结合。
  2. 使用 Masscan 执行初始端口的扫描,以识别开放的端口和开端口的主机。
  3. 使用 Masscan 的结果(已识别的开放端口和主机)作为 Nmap 的输入,以进行详细的端口扫描。

问题

虽然上面列出的想法很好,但是我们仍然需要解决每个工具的缺点。具体来说,我们需要解决的有:

  1. 当扫描数数万个目标的时候,Nmap 的速度很慢。
  2. Masscan 在高速 (rates) 扫描大端口范围时的不准确性(参见 Github 的 Issues 365)。

0x05 研究配置

目标网络

选择一下子网作为本次研究的网络目标:

测试用例

对于本次研究,两种工具都有自己的一些测试用例。这些测试用例是每种工具中可用的不同选项(参数)的变化。这些测试用例旨在解决工具的缺点,并利用它们的优点在速度和准确性之前找到平衡点。

Masscan

  1. 以不同的速率 (rates) 定期扫描所有的 TCP 端口。
  2. 将 / 16 的目标子网差分为 / 20,并运行 X 个并发 masscan 任务,每个任务的速率为 Y。
  3. 将 1-65535 的端口范围划分为几个范围,并运行 X 个并发的 Masscan 任务,每个任务的速率为 Y。

Nmap

  1. 定期扫描所有的 TCP 端口。
  2. 使用 X 并发任务扫描所有的 TCP 端口。
  3. 扫描 Masscan 识别的开放端口和主机的组合列表。
  4. 扫描 Masscan 识别特定主机上的特定开放端口。

在有限的时间内不可能涵盖所有选项的每个变化 / 组合,因此仅涵盖上述内容。

对于使用并发任务的测试用例,使用了工具 GNU Parallel。如果你对这个工具还是个新手,请查看详细的 教程

0x06 范围和限制

0x07 Masscan 的测试用例和测试结果

本节详细介绍了使用 Masscan 执行的不同测试用例和其测试结果。

测试用例 #1:使用不同的速率定期扫描所有的 TCP 端口

这个测试用例没啥特别之处,这只是 Masscan 的正常扫描,只是速率不同而已。

以下命令用于启动此扫描用例的扫描任务:

masscan -p 1-65535 --rate RATE--wait 0 --open TARGET_SUBNET -oG TARGET_SUBNET.gnmap

rate (扫描速率) 参数的设置:

在实验过程中,我得 VPS 可以运行的最大速率仅为 250kpps 左右。这是因为扫描的机器不支持 PF_RING。

图表(由于最大速率是 250kpps,故图表中的为 250k、100k 和 50k 的对比):

观察:

慢速率会导致发现更多的开放端口,但是代价就是扫描花费的时间更长。

测试用例 #2:将 / 16 的目标子网拆分为 / 20,并运行 X 个 Masscan 并发任务,每个任务的速率为 Y

为了能够运行并发任务,我觉得将 / 16 的目标子网拆分为更小的子网。你可以将其分为更小的子网,例如 / 24。本次研究我拆分为 / 20。

要将目标网络拆分为更小的子网,使用的 python 代码如下:

#!/usr/bin/python3
import ipaddress, sys

target = sys.argv[1]
prefix = int(sys.argv[2])

for subnet in ipaddress.ip_network(target).subnets(new_prefix=prefix):
    print(subnet)

以下是该代码的运行截图:

每项任务所用的速率都是基于扫描机器能够处理的速率最大化思想。在我的例子中,我的扫描机器最大只能处理 250kpps,所以如果我要运行 5 个并行任务,每个任务可使用 50kpps 的速率。

由于机器的最大速率不是“绝对”的(在本次测试中不完全都是 250kpps 的速率),你可以设置每个任务的速率,使总速率等于最大速率的 80%-90%。

对于本项测试,执行了以下命令。通过 split.py 来划分成较小的子网,然后使用 parallel 命令来运行并行任务。

python3 split.py TARGET_SUBNET 20 | parallel -j JOBS "masscan -p 1-65535 --rate RATE--wait 0 --open {} -oG {//}.gnmap"

以下是执行上述命令时的截图。在这种情况下,20 个 Masscan 任务,每个任务的速率为 10kpps,同时运行。

任务数和速率如下:

说明: 1. 大家可以注意到,我上面说的任务数和速率中第一个(5 个任务 / 每个任务的速率是 100kpps),我计算错了。因为它的总速率是 500kpps,而我的机器只能处理 250kpps。尽管如此,这个的测试结果仍然是有价值的,将可以在下面的图表里看到; 2. 其他的组合,例如 10 个任务,每个任务的速率 20kpps,这样是可行的。但是由于时间和预算有限,我不能把所有可能的组合都涵盖了。

图表如下:

观察:

测试用例 #3:将 1-65535 端口范围拆分为多个更小的范围,运行 X 个 Masscan 并发任务,每个任务的扫描速率为 Y

第三个测试用例是为了解决在扫描大端口范围的时候,上文提到的 Masscan 的 问题,特别是整个 1-65535 这样的范围。我的解决方案是将 1-65535 的范围拆分为更小的范围。

就像之前的测试用例一样,所使用的任务数 & 扫描速率组合的总速率是基于机器最大容量的 80-90% 这样的想法。

以下的命令用于本次的测试用例,PORT_RANGES 是包含端口范围列表,然后使用 parallel 命令来运行并行任务。

cat PORT_RANGES | parallel -j JOBS "masscan -p {} --rate RATE --wait 0 --open TARGET_SUBNET -oG {}.gnmap"

1-65535 端口范围分为四种拆分方式,如下所示,每种拆分方式包含任务和速率的组合 / 变化。

拆分方式 #1:拆分为 5 个端口范围

1-13107
13108-26214
26215-39321
39322-52428
52429-65535

任务数和速率如下:

图表如下:

拆分方式 #2:拆分为 2 个端口范围

1-32767
32768-65535

任务数和速率如下:

图表如下:

拆分方式 #3:拆分为 8 个端口范围

1-8190
8191-16382
16383-24574
24575-32766
32767-40958
40959-49151
49152-57343
57344-65535

任务数和速率如下:

图表如下:

拆分方式 #4:拆分为 4 个端口范围

1-16383
16384-32767
32768-49151
49152-65535

任务数和速率如下:

本次测试我之所以只使用了一种任务数 & 速率的组合,是因为我意识到我已经超过了每个月的带宽限制。这样我不得不多付 100 + 美元。

图表如下:

观察:

下面列出的观察结果涵盖了上面提到的所有 4 个拆分方式的方案。

原始数据

下表显示了使用上述不同 Masscan 测试用例进行实验的原始数据:

Masscan 结论

根据使用 Masscan 进行的所有测试用例的结果,得出以下结论:

0x08 Nmap 的测试用例和测试结果

在此阶段,只执行版本扫描。Nmap 的 NSE,OS 探测和其他扫描功能都没有涉及。Nmap 的线程被限制为 T4,等同于如下命令:

--max-rtt-timeout=1250ms --min-rtt-timeout=100ms --initial-rtt-timeout=500ms --max-retries=6 --max-scan-delay=10ms

以下 Nmap 选项也用于模拟 masscan 使用的选项。这些选项应用于所有 Nmap 测试用例。

使用的 Nmap 选项如下:

测试用例 #1:定期扫描所有的 TCP 端口

这个测试用例只是使用 Nmap 的正常扫描,所以没啥特别之处。使用的命令如下:

sudo nmap -sSV -p- -v --open -Pn -n --randomize-hosts -T4 TARGET_SUBNET -oA OUTPUT

观察:

测试用例 #2:使用 X 个并发任务扫描所有的 TCP 端口

在这种情况下,我尝试通过运行并发的 Nmap 扫描任务来解决 Nmap 的低性能问题。通过将目标子网划分为较小的子网块来完成,就像上面 Masscan 测试的那样。同样,下面的代码(split.py)用于拆分目标子网:

#!/usr/bin/python3
import ipaddress, sys

target = sys.argv[1]
prefix = int(sys.argv[2])

for subnet in ipaddress.ip_network(target).subnets(new_prefix=prefix):
    print(subnet)

运行命令如下:

python3 split.py TARGET_SUBNET 20 | parallel -j JOBS "sudo nmap -sSV -p- -v --open -Pn -n --randomize-hosts -T4 {} -oA {//}"

对于这个测试用例,我决定使用两个并发任务实例,如下所示:

使用 5 个并发任务:/16 的目标子网拆分为 / 20 的子网

观察:

使用 64 个并发任务:/16 的目标子网拆分为 / 24 的子网

观察:

测试用例 #3: 扫描 Masscan 识别出的开放端口和主机的组合列表

这个测试用例背后的想法是,首先获得一个主机列表和一个由 Masscan 扫描出的开放端口的组合列表。这个开放端口的组合列表被用作基线(如下图图表中的绿色条所示),以确定下面的 Nmap 测试用例能否能检测出更多或更少的 k 开放端口。

例如,Masscan 检测到 300 个开放端口,而常规 Namp 扫描检测到 320 个开放端口。但是,当使用 5 个并发 Nmap 任务扫描时,仅检测到 295 个开放端口。这意味着常规的 Nmap 扫描是更好的选择。

要从 Masscan 的扫描结果中获得主机列表,使用如下命令:

grep "Host:" MASSCAN_OUTPUT.gnmap | cut -d " " -f2 | sort -V | uniq > HOSTS

下图显示了上述命令的运行情况:

下面的命令用于获取 Masscan 检测到的所有开放端口的组合列表:

grep "Ports:" MASSCAN_OUTPUT.gnmap | cut -d " " -f4 | cut -d "/" -f1 | sort -n | uniq | paste -sd, > OPEN_PORTS

下图显示了上述命令的运行情况:

下面的命令用户 Nmap 的常规扫描:

sudo nmap -sSV -p OPEN_PORTS -v --open -Pn -n --randomize-hosts -T4 -iL HOSTS -oA OUTPUT

以下命令用于运行并发的 Nmap 扫描任务。使用上面命令生成的主机列表和开放端口的组合列表。

cat HOSTS | parallel -j JOBS "sudo nmap -sSV -p OPEN_PORTS -v --open -Pn -n --randomize-hosts -T4 {} -oA {}"

使用的并发任务数:

图表如下:

观察:

运行常规的 Nmap 扫描时,CPU 的利用率仅为 10% 左右;

常规的 Nmap 扫描发现了更多的开放端口,而并发的 Nmap 扫描发现的开放端口较少一些。

与基线(上面图表中的绿色条)相比,在某些目标网络(子网 A)上识别出更多的开放端口,而在其他的网络目标(子网 B 和子网 C)上检测到的开放端口较少,在某些网络目标(子网 D)上没有太大差异。

Nmap 检测到的其他开放端口

先看下面的表格。例如,让我们假设 Masscan 在每台主机上检测到以下的开放端口(表格第 2 列)。在运行 Nmap 扫描时,Masscan 检测到的所有开放端口将用作 Nmap 的目标端口(表格第 3 列)。

在我们的示例中,Nmap 在完成扫描后检测到的新开放的端口(第 4 列中的粗体文字)。这种情况是怎么发生的?Masscan 是一个异步的扫描器,主机 192.168.1.2 和 192.168.1.3 上可能丢失了 22 端口。由于我们合并了每个主机上检测到的开放端口,并将它们作为 Nmap 的目标端口,因此这个丢失的 22 端口将再次进行探测。需要注意的是,无法保证 Nmap 能够将其检测为开放状态,因为还有其他可能影响扫描结果的因素。

测试用例 #4 扫描由 Masscan 识别的特定主机上的特定开放端口

这个与之前的测试用例有点类似。在这个用例中,我没有将 Masscan 检测到的所有开放端口与每个主机组合在一起。无论 Masscan 在特定主机上检测到哪些开放端口,Nmap 都将使用相同的端口作为目标端口。下表说明了我们这个测试用例中的操作:

以下命令用于获取主机列表:

cat MASSCAN_OUTPUT.gnmap | grep Host | awk '{print $2,$5}' | sed 's@/.*@@' | sort -t' ' -n -k2 | awk -F' ' -v OFS=' ' '{x=$1;$1="";a[x]=a[x]","$0}END{for(x in a) print x,a[x]}' | sed 's/, /,/g' | sed 's/ ,/ /' | sort -V -k1 | cut -d " " -f1 > HOSTS

下图显示了上述命令的运行情况:

要从每个主机获取打开的端口列表,执行以下命令:

cat MASSCAN_OUTPUT.gnmap | grep Host | awk '{print $2,$5}' | sed 's@/.*@@' | sort -t' ' -n -k2 | awk -F' ' -v OFS=' ' '{x=$1;$1="";a[x]=a[x]","$0}END{for(x in a) print x,a[x]}' | sed 's/, /,/g' | sed 's/ ,/ /' | sort -V -k1 | cut -d " " -f2 > OPEN_PORTS

下图显示了上述命令的运行情况:

可以看到,上图输出的内容于测试用例 #3 中的不同,而且使用的命令也不一样。我们查询出每个主机的开放端口列表,而不是所有开放端口的组合。

然后使用 parallel 命令的 :::: 选项将上面两个命令查询出的列表,并发执行 Nmap 扫描。

再次强调,如果您不熟悉 GNU Parallel,请查看本 教程

parallel -j JOBS --link "sudo nmap -sSV -p {2} -v --open -Pn -n -T4 {1} -oA {1}" :::: HOSTS :::: OPEN_PORTS

这是个例子,当执行上述 parallel 命令后,并扫描时会发生什么(多条命令同时执行)。

sudo nmap -sSV -p 443 -v --open -Pn -n -T4 192.168.1.2 -oA 192.168.1.2
sudo nmap -sSV -p 80,443,1935,9443 -v --open -Pn -n -T4 192.168.1.5 -oA 192.168.1.5
sudo nmap -sSV -p 80 -v --open -Pn -n -T4 192.168.1.6 -oA 192.168.1.6
sudo nmap -sSV -p 80,443 -v --open -Pn -n -T4 192.168.1.7 -oA 192.168.1.7
sudo nmap -sSV -p 08,443 -v --open -Pn -n -T4 192.168.1.9 -oA 192.168.1.9

下图展示了测试用例执行时,发生的一个片段。如下图所示,使用 parallel 运行 10 个并发的 Nmap 扫描。

使用的并发任务数:

图表如下:

观察:

原始数据

下表显示了使用上述不同的 Nmap 测试用例进行实验的原始数据:

Nmap 结论

根据使用 Nmap 进行的实验结果,得出以下结论:

0x09 研究结论

推荐的扫描方法

根据对 Masscan 和 Nmap 进行的多个测试用例的测试结果,建议采用以下方法在端口扫描期间实现速度和精度之间的平衡:

  1. 首先运行 2 或 3 个并发的 Masscan 任务,所有的 65535 个端口分为 4-5 个更小的范围;
  2. 获取主机列表以及 Masscan 扫描出的开放端口的组合列表;
  3. 使用这些列表作为 Nmap 的扫描目标并执行常规 Nmap 扫描。

注意事项

对于这两种扫描端口的工具,应采用以下的预防措施进行规避,因为它们会导致检测到的开放端口更少:

0x10 最后的想法

虽然这项研究提供了一种如何在互联网端口扫描期间平衡速度和准确性的方法,但读者不应将此结论视为 100%可靠。由于时间和预算有限,研究期间没有涵盖其他的影响因素。最值得注意的是,在整个研究期间仅使用一个 IP 地址进行扫描并不是一个好的设置。因为在我多次扫描相同的目标网络后,机器的 IP 地址可能会以某种方式被拉黑,这可能导致检测到的开放端口数量不太一致。

请重新查看 0x06 范围和限制部分,因为从中可以很好的理解影响本研究结果的一些因素。


分享文章至:

Previous Post
回音室效应(Echo_chamber)
Next Post
如何养成一个良好的渗透记录习惯