将 nmap 服务扫描转化为按风险排序的 CVE、漏洞利用及野外攻击目标列表。
一个 NSE 脚本,它获取 nmap 已识别的软件信息,向 Vulners 数据库查询已知情报,并将结果打印在扫描报告中——按实际可利用性从高到低排列。
对公开可扫描主机的真实扫描。先无密钥,再有密钥,最后是 Web 端口:nmap 的 -sV 报告了 Coyote 横幅,扫描识别出其背后的 Tomcat 和 jQuery。
对于每个开放端口,它会查询 nmap 识别的软件信息——即 -sV 生成的 CPE——并打印 Vulners 已知的相关情报:按实际被利用情况而非仅按评分从高到低排列,每一行都链接到对应的详情页面。
在 HTTP 端口上,它还会对 Web 技术栈本身进行指纹识别,从而识别出 -sV 无法看到的软件——例如应用框架、CMS、反向代理背后的 PHP 版本。721 条规则会读取响应中携带版本信息的各个部分:Server 头、X-Powered-By、Cookie、页面标题、<meta> 标签、<script src> 文件名以及响应正文。这些识别出的身份信息也会被查询,并发布到该端口上,供扫描的其他部分使用。
除扫描之外,它还有两项额外功能:
-sV 无法识别服务时,原始横幅会与针对 FTP、SMTP、SSH、MySQL、DNS、NTP、LDAP 等的规则进行匹配。这不会产生额外的请求开销,而且这正是端口在其他情况下将一无所获的场景。/CHANGELOG.txt、Joomla 的 /administrator/manifests/files/joomla.xml。如果主机未运行这六种被探测的产品中的任何一种,则不会发送任何额外请求。```sh
nmap -sV --script vulners 这就是整个界面。它无需 API 密钥即可工作;有了密钥,它会告诉你更多信息。这里没有模式切换。```
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.7 ((Ubuntu))
| vulners: cpe:/a:apache:http_server:2.4.7 272 findings, 56 exploitable
| SEVERITY CVSS AI FLAGS LINK
| ======== ==== ==== ======= ==============================================================
| CRITICAL 10.0 8.8 EXP https://vulners.com/gitee/3E6BA608-776F-5B1F-9BA5-589CD2A5A351
| CRITICAL 9.8 9.9 EXP https://vulners.com/zdt/1337DAY-ID-39214
| CRITICAL 9.8 9.6 EXP https://vulners.com/packetstorm/PACKETSTORM:171631
| CRITICAL 9.8 9.9 https://vulners.com/cve/CVE-2021-44790
| CRITICAL 9.8 9.8 https://vulners.com/cve/CVE-2023-25690
|_ 262 more not shown; -v shows all, -vv adds where each was found
使用密钥时,对同一主机的相同扫描会给出不同答案——KEV 表示
CISA 已将该漏洞记录为已在野外被利用,而 EPSS 是
其将被利用的已发布概率:```
| vulners: cpe:/a:apache:http_server:2.4.7 272 findings, 78 exploitable
| SEVERITY CVSS EPSS FLAGS LINK
| ======== ==== ==== ======= ==============================================================
| CRITICAL 9.1 >99% KEV EXP https://vulners.com/cve/CVE-2024-38475
| CRITICAL 9.0 >99% KEV EXP https://vulners.com/cve/CVE-2021-40438
| CRITICAL 9.1 >99% KEV https://vulners.com/cnvd/CNVD-2024-36387
| CRITICAL 10.0 71% EXP https://vulners.com/gitee/3E6BA608-776F-5B1F-9BA5-589CD2A5A351
| CRITICAL 9.8 97% EXP https://vulners.com/cve/CVE-2021-44790
|_ 262 more not shown; -v shows all, -vv adds where each was found
同样的 272 项发现,顺序不同,顶行不同——而且其中 22 项已知可被利用,因为每个漏洞利用都通过一个键关联到它所利用的 CVE。这两个都是来自 vulners.com 的真实答案,是针对展示该横幅的本地服务器捕获的。
## 排序
事实优先于预测。发现按以下顺序排列:
1. **CISA KEV** —— 记录为已在野外被利用
2. **SSVC `active`** —— 协调方判断利用正在发生
3. **存在漏洞利用** —— 代码已发布,针对此漏洞或其命名的 CVE
4. **高 EPSS** —— 模型预期会被利用
5. 其他所有情况
CVSS 仅在带内打破平局,而非跨带:一个已被利用的 7.5 比一个未被利用的 9.8 问题更严重,而这个顺序正是表达这一点的。
列跟随数据。答案未携带的信号会失去其列,而不是显示空单元格,因为空白的 EPSS 读起来像“安静”,而这是缺失字段无法支持的断言。
## API 密钥带来的差异
| | |
|---|---|
| **无密钥** | nmap 发现的每个 CPE 都会在免费端点上查询。发现、评分、漏洞利用标志以及 Vulners 自身的 AI 评分。无需积分,无需账户 |
| **有密钥,无积分** | 每项发现都会获得 id 端点所知的额外信息:标题、日期、上游公告或漏洞利用页面、漏洞利用到 CVE 的关联、CISA KEV,以及——取决于许可证——EPSS 和 SSVC |
| **有密钥,一个积分** | 免费路径完全无法命名的软件会从其原始横幅中被识别出来。这是这里唯一需要花费任何费用的部分,且仅针对没有 CPE 的服务 |
已带有 CPE 的端口永远不会消耗积分:跨四个产品测量,免费查询对某个 CPE 返回的 **CVE 列表** 与付费查询相同。积分购买的是识别能力,而非更多漏洞。
免费密钥位于 [vulners.com/userinfo](https://vulners.com/userinfo)。
## 安装
**macOS、Linux、Kali、WSL** —— 一行命令,无需参数:```sh
curl -fsSL https://raw.githubusercontent.com/vulnersCom/nmap-vulners/master/install.sh | sh
Windows - 以管理员身份运行 PowerShell:```powershell irm https://raw.githubusercontent.com/vulnersCom/nmap-vulners/master/install.ps1 | iex
安装程序会询问 nmap 其数据存放位置,将脚本及其数据文件复制到那里,重建脚本数据库,然后检查 `--script vulners` 是否确实解析到刚刚安装的内容——nmap 自带一个 `vulners.nse`,此操作会将其替换。
<details>
<summary><b>无 root 权限及其他选项</b></summary>```sh
# into ~/.nmap, no sudo; the installer prints the NMAPDIR line to add to your profile
curl -fsSL https://raw.githubusercontent.com/vulnersCom/nmap-vulners/master/install.sh | sh -s -- --user
# a specific directory
./install.sh --prefix /usr/local/share/nmap
# a specific release
./install.sh --ref v2.0
# remove everything it installed
./install.sh --uninstall
PowerShell 采用相同的选项:-User、-Prefix、-Ref、-Uninstall。
单个文件:将 vulners.nse 复制到 <nmap data dir>/scripts/ 并运行
sudo nmap --script-updatedb。无需放置其他内容——该脚本会在扫描时下载其字典,且不会向磁盘写入任何内容——因此也不会遗漏任何东西,而遗漏曾会导致脚本运行后一无所获,却对原因只字不提。
如果你是从 1.x 升级,请同时删除该目录下的 vulners_enterprise.nse 和
http-vulners-regex.nse。残留的 http-vulners-regex.nse 仍带有 default 类别,并会在普通的 -sC 下持续扫描目标。安装程序会为你完成此操作。
nmap 数据目录通常是 /usr/share/nmap(Debian、Ubuntu、Kali)、
/usr/local/share/nmap(从源码构建)、/opt/homebrew/share/nmap
(Homebrew)或 C:\Program Files (x86)\Nmap(Windows)。要确定具体位置,请询问 nmap:```sh
nmap -d2 --script-help probe 2>&1 | grep nse_main.lua
存放 `nse_main.lua` 的目录就是此 nmap 所使用的目录。
</details>
## 脚本参数
| 参数 | 默认值 | 含义 |
|---|---|---|
| `vulners.mincvss` | `0` | 隐藏评分低于此值的发现结果。无评分的公告以及任何已知漏洞利用的内容,无论阈值如何都会显示 |
| `vulners.paths` | 已发布的 939 条路径 | Web 扫描的路径:一个 Lua 列表、一个字符串(指向每行一条路径的文件),或 `none` 以关闭扫描。您指定的文件若无法读取,会停止扫描并说明原因,而不是回退到已发布的列表 |
| `vulners.width` | `80` | 表格布局所针对的终端宽度 |
| `vulners.max_items` | `32` | 整个扫描计费项目的上限 |
| `vulners.api_key` | - | API 令牌。有泄露风险:nmap 会将其自身命令行复制到 `-oX` 中 |
| `vulners.api_key_file` | - | 文件的绝对路径,其第一行为令牌 |
| `vulners.api_host` | `vulners.com` | API 的主机名 |
| `vulners.api_port` | `443` | `api_host` 上的端口 |
裸名称同样有效,因此 `--script-args mincvss=7` 就足够了。
1.x 版本的参数前缀——`vulners_enterprise.*` 和
`http-vulners-regex.paths`——在一个版本内仍被接受,并会打印
弃用通知。
## API 密钥存放位置
按优先级顺序:
1. `~/.nmap/vulners.key`,单行,权限 600——安装程序会提供写入选项
2. 环境变量中的 `VULNERS_API_KEY`
3. `--script-args vulners.api_key_file=/absolute/path`
4. `--script-args vulners.api_key=<token>`
最后一种方式方便但有泄露风险:nmap 会将其自身命令行复制到每份
报告中,因此令牌会出现在 `-oX` 输出的 `args` 属性中以及您的
shell 历史记录里。脚本本身绝不会将令牌写入任何位置,包括其
调试输出——有一项回归测试对此进行了验证。
您明确指定的密钥文件若无法读取,会停止运行而不是静默
回退——操作员指定了文件就意味着要用该文件——并且报告会
说明是哪个文件。拼写错误的路径不会看起来像一次干净的扫描。
## 指纹来源
脚本本身不携带指纹。每次扫描时,它会在接触第一台主机之前
下载三份字典:```
https://raw.githubusercontent.com/vulnersCom/nmap-vulners/catalog/
index.json what exists, at which serial
fingerprints.json 721 product and version rules
paths.json 939 paths the sweep requests
probes.json targeted version probes
每次扫描会发出四个请求——每个文件一个,而不是每个主机或每个端口——在 nmap 的预扫描阶段,从 250 KB 的 JSON 中压缩出 40 KB。这是针对已发布分支进行测量的。因此,已安装的脚本无需更新即可获取新的指纹。
它不会向你的文件系统写入任何内容。 字典仅在扫描期间保留,随后即被丢弃,这与 nmap 随附的所有脚本行为一致:在 611 个脚本中,有 26 个会打开文件进行写入,且每个都只写入脚本参数指定的位置。没有脚本保留缓存,本脚本也不例外。
如果无法下载,扫描仍会运行。 字典仅用于 Web 指纹识别,不涉及其他功能,因此无法访问 GitHub 的机器只会失去该能力,其余功能保持不变:nmap 自身识别出的软件仍会被查询,报告会说明缺失了哪项能力,而不是让你把空结果误读为干净的网络。
| 参数 | 作用 |
|---|---|
vulners.catalog_url=<url> | 改为从镜像获取——适用于隔离网络。主机名或方括号中的 IPv6 地址,如 http://[fd00::1]/catalog/ |
vulners.catalog=none | 完全不获取;仅查询 nmap 命名的内容 |
在 HTTP 端口上,脚本会请求目录发布的每个路径——共 939 个路径——并将全部 721 条规则与每个响应进行匹配。这些路径来自 WhatWeb、nuclei 和 FingerprintHub:即产品被识别的位置,而非猜测。即使某个路径属于你并未运行的软件,也值得发出请求,因为响应仍会携带 Server、X-Powered-By、cookie 和标题,而规则正是从这些信息中找到其背后的技术栈。
扫描速度取决于你的 -T 设置,而非我们的配置。 列表不会缩短;变化的是速率:
| 批次 | 批次间等待 | 实测,单个 Web 端口 | |
|---|---|---|---|
-T0 paranoid | 188 x 5 | 2 秒 | 11 分 22 秒 |
-T1 sneaky | 94 x 10 | 1 秒 | 1 分 56 秒 |
-T2 polite | 38 x 25 | 0.5 秒 | 26.2 秒 |
-T3 normal | 10 x 100 | 0.1 秒 | 7.6 秒 |
-T4 aggressive | 4 x 250 | 无 | 6.7 秒 |
-T5 insane | 1 | 无 | 6.6 秒 |
以上六项均在同一次运行中针对同一本地服务器测得。仅使用 -sV 针对该服务器耗时 6.1 秒,因此在默认设置下,扫描额外花费约一秒半。两行较慢的是 -T0 和 -T1,它们正在履行职责:188 和 94 个批次,每批之间有意等待。
请求通过 34-48 条连接进行流水线处理,同时最多保持四个打开,这使用的是 nselib 的 pipeline_go——与 nmap 自身的 http-enum 所用机制相同,会遵循服务器的 Keep-Alive: max= 以及 --script-args http.max-pipeline=N。
--script-args vulners.paths=none 可完全关闭扫描。
nmap -sV | +-- service fingerprint --> port.version.cpe --------------+ | | +-- vulners.nse | reads nmap's banner for services -sV could not name | on an HTTP port: requests the path list in one | pipeline, matches 721 rules against the header, the | title, the meta tags, the scripts and the body | probes for a version when a product hid it | publishes everything it recognised ------------------+ | v GET /api/v3/burp/software/ per identity free, no key, CDN-cached | POST /api/v3/search/id/ per 100 findings free, needs a key: enrichment | POST /api/v4/audit/smart only for a service with no CPE: 1 credit | ranked, filtered, printed
答案会针对整个扫描进行缓存,并按身份标识作为键,因此一百台相同的服务器只需一次查询。富集结果会按发现 ID 缓存,因此两个运行重叠软件的 Web 端口只需为每个文档获取一次。
## 机器可读输出
所有打印的内容也都是结构化的,因此 `-oX` 无需处理人类可读文本即可解析。脚本 ID、两个表格层级以及五个原始元素键均与 1.x 保持一致,这正是 DefectDojo、Faraday、nmap2csv 和 raven 所读取的内容:```xml
<script id="vulners">
<elem key="schema">2.0</elem>
<elem key="mode">keyed</elem>
<table key="cpe:/a:apache:http_server:2.4.7">
<table>
<elem key="id">CVE-2021-40438</elem>
<elem key="type">cve</elem>
<elem key="severity">CRITICAL</elem>
<elem key="cvss">9.0</elem>
<elem key="cvss_type">cvss3.1</elem>
<elem key="is_exploit">false</elem>
<elem key="exploit_known">true</elem>
<elem key="kev">true</elem>
<elem key="epss">0.99612</elem>
<elem key="exploitation">active</elem>
<elem key="title">Apache HTTP Server SSRF in mod_proxy</elem>
<elem key="href">https://vulners.com/cve/CVE-2021-40438</elem>
<elem key="source_href">https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2021-40438</elem>
</table>
</table>
</script>
2.0 新增:schema、mode、severity、exploit_known、kev、epss、
epss_percentile、exploitation、ai_score、title、published、href、
source_href 和 found_on。每个字段要么存在要么缺失,绝不会为空。
嵌套层级不会比之前更深,因为第三层表结构对所有检查过的导入器都是不可见的。
href 在两种模式下始终指向该发现的 vulners.com 页面。端点自身的
href 是上游地址——CVE 对应 nvd.nist.gov,抓取的漏洞利用对应
github.com——该地址单独作为 source_href 传递,因此两个字段的含义
都不依赖于生成它们的模式。
结构化输出始终携带所有通过 mincvss 的发现,即使冗长级别阶梯
从文本中隐藏了它们——因此自动化不会因未传递 -v 而丢失发现。
渲染文本是一个断点。 *EXPLOIT* 和 *HAS EXPLOIT* 标记
以及制表符分隔的布局已消失,取而代之的是上述对齐表格。每行的
vulners.com 链接得以保留:它是最后一列,也是布局不会缩短的唯一单元格,
因为半个 URL 不是 URL。依赖文本抓取的消费者需要更新;-oX 消费者则不需要。
对网络的扫描向 API 提出的请求远少于表面所见:
测试、卫生门禁和 CONTRIBUTING.md 仅存在于 git 仓库中;发布归档
附带脚本及其数据。八个门禁,除注明外全部离线运行,CI 恰好执行这些门禁:```sh
nmap -sn -Pn --script ./tests/run.nse --script-args testdir=tests,root=. 127.0.0.1
python3 tests/e2e/run_e2e.py
python3 tools/check.py
python3 tools/catalog.py --check
python3 tools/xml_contract.py --selftest
python3 tools/fingerprints/selftest.py
python3 tools/catalog_diff.py --selftest
python3 tools/nmap_style.py
270 个单元用例在 nmap 内部针对真实的 NSE 库运行;64 个端到端用例驱动真实的 nmap 二进制文件,针对本地 Web 服务器和强制执行与真实 API 相同规则的 Vulners 替身 API 进行测试;卫生门禁确保密钥、扫描输出和编辑器杂项不会进入代码树,并拒绝 NSE 会变成丢失结果的全局读取;最后四个用例保存数据和发布数据的工具——目录的结构、每个导入器读取的 XML 契约、模式转换器,以及决定重建是否可以安全发布的门禁。最后一个用例将整个仓库约束到
[Nmap 自己的代码标准](https://secwiki.org/w/Nmap/Code_Standards),`HACKING` 将其指定为希望进入 nmap 代码树的脚本的权威标准:无制表符、无尾随空白、行宽低于 80 列、无分号、以 `--;` 开头的私有 NSEdoc、Python 遵循 PEP 8。
`python3 tests/e2e/run_e2e.py --live` 会添加针对真实服务的检查。
参见 [CONTRIBUTING.md](https://github.com/vulnerscom/nmap-vulners/blob/master/CONTRIBUTING.md)。
## 常见问题
**它会利用漏洞吗?** 不会。它读取横幅和页面并查询数据库——它不发送任何载荷,也不尝试任何凭据。它被归类为 `discovery, intrusive, vuln, external` 而非 `safe`,原因只有一个:路径扫描会请求 Web 端口的 939 个路径,而 nmap 对 `safe` 的定义排除了使用大量带宽的脚本。nmap 自己的 `http-enum` 会请求 2 204 个路径并带有相同的标签。你的 `-T` 决定这些请求发出的速度,而绝不会决定数量,`--script-args vulners.paths=none` 可以完全关闭扫描。
**没有 API 密钥也能工作吗?** 可以,完全能。没有密钥时它使用免费端点,该端点对 CPE 返回的漏洞与付费端点相同。密钥会为每个发现添加详细信息,并且可以命名免费路径无法识别的软件。
**为什么 `-sC` 不再发现 Web 软件?** 因为 `vulners` 不再属于 nmap 的 `default` 类别,曾经位于 `http-vulners-regex.nse` 中的指纹扫描也是如此。将目标软件的身份发送给第三方应该是你主动要求的事情:运行 `--script vulners`。
**为什么一个漏洞显示 `cvss2.0` 而另一个显示 `cvss3.1`?** 该标签标明分数所在的分值体系。Vulners 返回的是来源发布的那个版本;v2 的 9.3 分并不等于 v3 的 9.3 分。
**为什么我设置了 `mincvss` 后仍显示低分条目?** 因为它有已知的利用方式,或者因为来源从未给它评分。两者都是有意为之。
**报告说目录无法下载。** 那么 Web 指纹识别没有运行,而其他一切都运行了:nmap 自己命名的软件仍然被查询。正因如此它才会被明确说明——一项未运行的能力会被解读为一项未发现任何内容的能力。两种原因有各自的措辞:“could not be downloaded” 指网络问题,而 “answered, but one of its dictionaries could not be read” 指你指向的镜像源。
**它在我确定存在漏洞的主机上什么都没发现。** 使用 `-d2` 运行:它会记录它查询的每个身份。通常 nmap 命名了服务但没有命名其版本,因此没有版本可供查询。
## 许可证
这些脚本的许可证与 Nmap 本身相同——参见 [LICENSE](https://github.com/vulnerscom/nmap-vulners/blob/master/LICENSE) 了解 Nmap 公共源代码许可证,以及
[nmap.org/npsl](https://nmap.org/npsl/) 了解其含义。
漏洞数据来自 [Vulners](https://vulners.com),并受其条款约束。
## 相关项目
* [vulners.com](https://vulners.com) - 这些脚本背后的数据库
* [vulnersCom/api](https://github.com/vulnersCom/api) - Python 客户端
* [vulnersCom/burp-vulners-scanner](https://github.com/vulnersCom/burp-vulners-scanner) - Burp Suite 中的相同数据
* [nmap.org/book/nse.html](https://nmap.org/book/nse.html) - NSE 脚本的工作原理
---
<div align="center">
由 Vulners 团队维护 <[email protected]>
`#nmap` `#nse` `#vulnerability-scanner` `#cve` `#cvss` `#cpe` `#vulners`
`#security-tools` `#pentest` `#infosec` `#network-scanner` `#exploit-database`
`#lua` `#vulnerability-detection` `#security-automation`
</div>