
Скрипт NSE для Nmap, который запрашивает API Vulners для выявления известных уязвимостей (CVE) для обнаруженных сетевых служб, расширяя стандартное сканирование портов автоматизированным поиском CVE и оценкой CVSS.
<div align="center">
# nmap-vulners
**Превращает сканирование сервисов nmap в ранжированный список CVE, эксплойтов и того, что атакуется в дикой природе.**
Один NSE-скрипт, который берёт программное обеспечение, уже идентифицированное nmap, запрашивает базу данных [Vulners](https://vulners.com) о том, что о нём известно, и выводит ответ внутри отчёта сканирования — сначала худшее, по тому, что реально эксплуатируемо.
[](https://github.com/vulnersCom/nmap-vulners/actions)
[](LICENSE)
[](https://nmap.org)
[](https://vulners.com)
[](https://github.com/vulnersCom/nmap-vulners/stargazers)
<img src="https://assets.kitploit.com/production/public/readmes/1680/0a17fe209e2ad4d8bbd8bf49c72243a8c8b3f0fb9e603317b21c8a2cd6982b0f.gif" alt="Три сканирования: SSH-порт без API-ключа, тот же порт с ключом и веб-порт, где обход называет Tomcat за баннером Coyote" width="900">
<sub>Реальные сканирования хостов, опубликованных для сканирования. Без ключа, затем с ключом, затем веб-порт: nmap <code>-sV</code> сообщает о баннере Coyote, а обход называет Tomcat и jQuery за ним.</sub>
</div>
---
## Что он делает
Для каждого открытого порта он ищет программное обеспечение, идентифицированное nmap — CPE, которые создал `-sV`, — и выводит то, что Vulners знает о нём: сначала худшее по тому, что эксплуатируется, а не только по оценке, и каждая строка — ссылка на страницу за ней.
На HTTP-порту он также снимает отпечатки самого веб-стека, что называет программное обеспечение, которое `-sV` не может увидеть — фреймворк приложения, CMS, версию PHP за обратным прокси. 721 правило читает части ответа, несущие версию: заголовок `Server`, `X-Powered-By`, cookie, заголовок страницы, теги `<meta>`, имена файлов `<script src>` и тело. Эти идентичности также ищутся и публикуются на порту, чтобы остальная часть сканирования могла их использовать.
Две вещи, которые он делает помимо обхода:
* **он читает собственный баннер сервиса nmap.** Когда `-sV` не смог назвать сервис, сырой баннер сопоставляется с правилами для FTP, SMTP, SSH, MySQL, DNS, NTP, LDAP и других. Это не стоит дополнительного запроса, и это тот случай, когда порт в противном случае не сообщил бы вообще ничего.
* **он спрашивает продукт, который не скажет.** CMS, которая называет себя и скрывает свою версию, — обычное дело, и никакое сопоставление с образцом не извлечёт число, которого нет на странице. Когда продукт распознан, а версия нет, один запрос идёт в место, которое отвечает — `/CHANGELOG.txt` для Drupal, `/administrator/manifests/files/joomla.xml` для Joomla. Хосту, на котором не запущен ни один из шести проверяемых продуктов, ничего лишнего не отправляется.```sh
nmap -sV --script vulners <target>
```
Это весь интерфейс. Он работает без 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
| | |
|---|---|
| **Без ключа** | Каждый CPE, найденный nmap, проверяется на бесплатном endpoint. Находки, оценки, флаги эксплойтов и собственный AI-скор Vulners. Без кредитов, без аккаунта |
| **Ключ, без кредитов** | Каждая находка получает то, что знает id endpoint: заголовки, даты, ссылку на upstream-совет или страницу эксплойта, связь эксплойт-к-CVE, CISA KEV и — в зависимости от лицензии — EPSS и SSVC |
| **Ключ, один кредит** | ПО, которое бесплатный путь вообще не мог назвать, идентифицируется по его сырому баннеру. Это единственное здесь, что что-то стоит, и только для сервиса без CPE |
Порт, который уже несёт CPE, никогда не стоит кредита: по замерам на четырёх
продуктах бесплатный поиск возвращает те же **CVE**, что и платный, для CPE.
Что покупает кредит — это идентификация, а не больше уязвимостей.
Бесплатные ключи — на [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`.
</details>
<details>
<summary><b>Из локальной копии репозитория</b></summary>```sh
git clone https://github.com/vulnersCom/nmap-vulners
cd nmap-vulners
./install.sh
```
Установщик использует файлы, находящиеся рядом с ним, поэтому устанавливается именно то, что вы
склонировали. Запуск скриптов напрямую из рабочей копии также работает:```sh
nmap -sV --script "$PWD/vulners.nse" <target>
```
> При запуске из рабочей копии используйте **абсолютный** путь. nmap сначала
> разрешает относительный `--script ./vulners.nse` через собственную `script.db`
> и незаметно запускает копию, поставляемую с nmap, а не вашу.
</details>
<details>
<summary><b>Вручную</b></summary>
Один файл: скопируйте `vulners.nse` в `<каталог данных nmap>/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 путей | Пути для веб-обхода: список 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 копирует собственную командную строку в каждый
отчёт, поэтому токен попадает в атрибут `args` вывода `-oX` и в вашу
историю 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
```
Это четыре запроса на сканирование — по одному на файл, а не на хост и не на порт —
для 40 КБ сжатых данных из 250 КБ JSON, на этапе предварительного сканирования nmap. Измерено
относительно опубликованной ветки. Поэтому установленный скрипт подхватывает новые
отпечатки без обновления.
**Он ничего не записывает в вашу файловую систему.** Словари хранятся в течение
сканирования и затем отбрасываются — так ведёт себя каждый скрипт, поставляемый с nmap:
из 611 таких скриптов 26 открывают файл для записи, и каждый пишет только туда, куда
указал аргумент скрипта. Ни один не хранит кэш, и этот тоже.
**Если их нельзя загрузить, сканирование всё равно выполняется.** Словари питают
веб-распознавание отпечатков и больше ничего, поэтому машина без маршрута к 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`**, а не наша настройка. Список никогда не
сокращается; меняется скорость:
| | пакеты | ожидание между ними | измерено, один веб-порт |
|---|---|---|---|
| `-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 соединений с максимум четырьмя открытыми
одновременно, что является `pipeline_go` из nselib — тот же механизм, который использует
собственный `http-enum` nmap, учитывающий `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
```
Ответы кэшируются для всего сканирования, привязываясь к каждой идентичности, поэтому сотня
одинаковых серверов стоит одного запроса. Обогащение кэшируется по идентификатору находки, поэтому два
веб-порта с перекрывающимся программным обеспечением загружают каждый документ один раз.
## Машиночитаемый вывод
Всё, что выводится, также структурировано, поэтому `-oX` можно разбирать, не касаясь
человекочитаемого текста. Идентификатор скрипта, два уровня таблиц и пять исходных
ключей элементов не изменились с версии 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` конечной точки — это **вышестоящий** адрес: nvd.nist.gov для CVE,
github.com для собранного эксплойта — и он передаётся отдельно, как
`source_href`, так что смысл ни одного из полей не зависит от того, какой режим его создал.
Структурированный вывод всегда содержит каждую находку, прошедшую `mincvss`, даже
те, которые лестница подробности скрывает из текста, — так что ни одна автоматизация не
теряет находки, не передавая `-v`.
**Отображаемый текст — это разрыв.** Токены `*EXPLOIT*` и `*HAS EXPLOIT*`
и табулированная раскладка исчезли, заменённые выровненной таблицей выше.
Ссылка на vulners.com в каждой строке осталась: это последний столбец, и это единственная ячейка,
которую раскладка не будет сокращать, потому что половина URL — это не URL. Потребителям,
скрейпящим текст, нужно обновление; потребителям `-oX` — нет.
## Вежливое сканирование
Сканирование сети запрашивает у API гораздо меньше, чем кажется:
* один запрос на каждую идентичность ПО, и ответы кэшируются на всё сканирование
* уже выполняющийся поиск ожидается, а не повторяется
* обнаружение идёт на конечную точку с **CDN-кэшем**, без ключа, даже когда он
настроен, так что оно остаётся на общем кэше вместо обращения к источнику
* обход 939 путей выполняется через несколько **конвейерных соединений**, а не
по одному на путь, запрашивает
сжатые страницы и прекращает сопоставление, как только исчерпает свой бюджет байтов
* ограничение скорости или сбой останавливают эту часть сканирования вместо повторов на
каждый хост, а отклонённый ключ переходит на бесплатный путь, а не заставляет сканирование замолчать
* кредиты тратятся только там, где бесплатный путь вообще не может ответить
## Разработка
Тесты, шлюз гигиены и `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 против локального веб-сервера и подставного
API Vulners, который принудительно применяет то же, что и реальный; гигиенический шлюз не допускает
секреты, вывод сканирования и мусор редактора в дерево и отказывает в глобальном чтении,
которое NSE превратило бы в потерянный результат; а последние четыре содержат данные и
инструменты, которые их публикуют — форму каталога, XML-контракт, который читает каждый
импортёр, транслятор шаблонов и шлюз, который решает, что пересборку безопасно публиковать. Последний удерживает весь репозиторий в соответствии с
[собственными стандартами кода Nmap](https://secwiki.org/w/Nmap/Code_Standards), которые
`HACKING` называет авторитетом для скрипта, желающего жить в дереве nmap:
никаких табуляций, никаких завершающих пробелов, строки короче 80 столбцов, никаких точек с запятой,
приватный NSEdoc, открывающийся с `--;`, PEP 8 для Python.
`python3 tests/e2e/run_e2e.py --live` добавляет проверки против реального сервиса.
См. [CONTRIBUTING.md](https://github.com/vulnerscom/nmap-vulners/blob/master/CONTRIBUTING.md).
## FAQ
**Эксплуатирует ли он что-либо?** Нет. Он читает баннеры и страницы и обращается к
```
---
[Read more](https://github.com/vulnerscom/nmap-vulners)