# Пошаговое руководство: как провести оценку внешней поверхности атаки с помощью ИИ в CVEngine

> AI Release · @ai_release1 · https://ai-release.org/guides/poshagovoe-rukovodstvo-kak-provesti-otsenku-vneshney-po.html

## TL;DR

- Время выполнения: 60–90 минут на периметр до 50 хостов
- Уровень сложности: Intermediate
- Понадобится: Ubuntu 22.04+/Debian 12, macOS 13+ или Windows 11 с WSL2; пакеты `curl`, `jq`, `dnsutils` (в нём `dig`), `nmap`, `openssl`; письменное разрешение на сканирование (RoE); доступ к CVEngine — адрес API и способ авторизации сверьтесь с документацией

> В исходных материалах канала нет спецификации CVEngine (эндпоинты, имена полей, лимиты). Всё, что относится к самому продукту, помечено как «сверьтесь с документацией»; остальные шаги — стандартные и проверяемые.

## Требования и подготовка
![Иллюстрация к разделу «Требования и подготовка»](https://ai-release.org/guides/img/poshagovoe-rukovodstvo-kak-provesti-otsenku-vneshney-po-ru-1.jpg?v=1792962553)


- ОС: `uname -a` — Ubuntu 22.04+/Debian 12, macOS 13+ либо Windows 11 с WSL2.
- Права: обычный пользователь; `sudo` нужен только для установки пакетов. SYN-скан (`nmap -sS`) требует root, поэтому ниже используется connect-скан `-sT` — он работает без root и не открывает raw-сокеты.
- Пакеты: `sudo apt update && sudo apt install -y curl jq dnsutils nmap openssl`.
- Документ RoE (Rules of Engagement) — письменный список доменов и IP, которые разрешено сканировать. Без него активная разведка юридически равна атаке.
- API-ключ CVEngine в переменной окружения, не в git.
- Свободное место: ~200 МБ под XML/JSON по 50 хостам (`df -h /`).

**Пример:** проверка окружения одной командой.

```bash
curl --version | head -n1; jq --version; nmap --version | head -n1; dig -v
```

Ожидаемый вывод — четыре строки с версиями, например `jq-1.6`. Если `jq` не найден, установка выше не выполнилась.

Если CVEngine или сканер вы разворачиваете на своём железе, порядок подготовки машины описан в [гайде по подключению ИИ к домашнему серверу](https://ai-release.org/guides/kak-podklyuchit-ii-k-domashnemu-serveru-poshagovoe-ruko.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide).

Чек-лист перед стартом:

- [ ] RoE подписан, список хостов совпадает с `scope.txt`
- [ ] `scope.txt` содержит только домены/IP из RoE
- [ ] переменные `CVENGINE_API_BASE` и `CVENGINE_TOKEN` заданы
- [ ] `nmap` запускается без sudo

## Шаг 1. Инвентаризация внешних активов и проверка DNS

**Что сделать:** создать рабочую папку, файл периметра `asm/scope.txt` и таблицу `asm/out/resolved.csv` с парами «хост → IP».

**Зачем:** поверхность атаки (attack surface) — это все ваши активы, доступные из интернета. ИИ-слой анализирует только то, что вы ему подали, поэтому мусор в списке хостов = мусор в отчёте.

```bash
mkdir -p asm/raw asm/out
cat > asm/scope.txt <<'EOF'
www.example.com
api.example.com
vpn.example.com
EOF
```

**Пример:** `www.example.com` — плейсхолдер. Замените на домены из своего RoE, по одному в строке, без `https://`.

Теперь разрешите имена в IP:

```bash
while read -r h; do
  ip=$(dig +short "$h" | tail -n1)
  echo "$h,${ip:-NXDOMAIN}"
done < asm/scope.txt | tee asm/out/resolved.csv
```

Ожидаемый вывод:

```
www.example.com,93.184.216.34
api.example.com,93.184.216.35
vpn.example.com,NXDOMAIN
```

**Как проверить:** `grep -c NXDOMAIN asm/out/resolved.csv`. Если счётчик больше нуля — эти хосты не существуют в DNS или опечатка в имени; исправьте до перехода к шагу 2, иначе `curl` и `nmap` упадут на них с ошибкой.

Почему это работает: `dig +short` отдаёт только ответ резолвера, `tail -n1` берёт последнюю A-запись, а `${ip:-NXDOMAIN}` подставляет заглушку вместо пустой строки, чтобы CSV не «съехал».

## Шаг 2. Сбор данных сканирования и передача в ИИ-модуль CVEngine
![Иллюстрация к разделу «Шаг 2. Сбор данных сканирования и передача в ИИ-модуль CVEngine»](https://ai-release.org/guides/img/poshagovoe-rukovodstvo-kak-provesti-otsenku-vneshney-po-ru-2.jpg?v=1792962564)


**Что сделать:** снять порты и версии сервисов, добавить HTTP-заголовки и данные TLS-сертификата, собрать всё в `assets.json` и отправить в CVEngine.

**Зачем:** ИИ-слой сопоставляет версии сервисов с известными уязвимостями, убирает дубли и ранжирует находки по критичности. Вручную сверять 50 баннеров с базами CVE — это часы; машина делает это за секунды. Точный эндпоинт и имена полей запроса — **сверьтесь с документацией** CVEngine.

Шаг 2.1 — сканирование портов и версий:

```bash
nmap -sT -sV --top-ports 100 -T4 \
  -iL <(cut -d, -f1 asm/out/resolved.csv | grep -v NXDOMAIN) \
  -oX asm/raw/scan.xml
```

`-sT` — connect-скан без root, `-sV` — определение версий, `--top-ports 100` — 100 самых частых портов, `-oX` — результат в XML для машинной обработки.

Шаг 2.2 — конвертация XML в JSON штатной библиотекой Python:

```python
import json, xml.etree.ElementTree as ET

root = ET.parse("asm/raw/scan.xml").getroot()
assets = []
for host in root.findall("host"):
    addr = host.find("address").get("addr")
    for port in host.findall("ports/port"):
        state = port.find("state").get("state")
        if state != "open":
            continue
        svc = port.find("service")
        assets.append({
            "host": addr,
            "port": int(port.get("portid")),
            "service": svc.get("name") if svc is not None else "unknown",
            "version": (svc.get("product", "") + " " + svc.get("version", "")).strip(),
        })
json.dump({"assets": assets}, open("asm/out/assets.json", "w"), indent=2)
print(f"собрано активов: {len(assets)}")
```

Запуск: `python3 to_json.py`. Ожидаемый вывод — `собрано активов: 37` (число зависит от вашего периметра).

Шаг 2.3 — обогащение HTTP-заголовками и сертификатами:

```bash
for h in $(grep -v NXDOMAIN asm/out/resolved.csv | cut -d, -f1); do
  echo "== $h"
  curl -sSI --max-time 10 "https://$h" | head -n 5
  echo | openssl s_client -connect "$h:443" -servername "$h" 2>/dev/null \
    | openssl x509 -noout -subject -dates
done | tee asm/out/http_tls.txt
```

Результат — блоки вида `== api.example.com`, строка `Server: nginx`, затем `notAfter=Mar 1 12:00:00 2026 GMT`. Просроченный `notAfter` — сам по себе находка.

Шаг 2.4 — отправка в CVEngine:

```bash
export CVENGINE_API_BASE="https://<адрес-вашего-инстанса>"
export CVENGINE_TOKEN="<токен-из-личного-кабинета>"

curl -sS -X POST "$CVENGINE_API_BASE/<эндпоинт-загрузки-активов>" \
  -H "Authorization: Bearer $CVENGINE_TOKEN" \
  -H "Content-Type: application/json" \
  --data @asm/out/assets.json \
  -o asm/out/ai_report.json -w "%{http_code}\n"
```

**Как проверить:** код ответа `200` и валидный JSON в отчёте — `jq 'keys' asm/out/ai_report.json`. Должен вернуться массив ключей, например `["findings","summary"]`; имена зависят от версии продукта — сверьтесь с документацией. Если вместо JSON пришёл HTML, см. раздел проблем.

## Шаг 3. Проверка результата: подтверждение находок и приоритизация
![Иллюстрация к разделу «Шаг 3. Проверка результата: подтверждение находок и приоритизация»](https://ai-release.org/guides/img/poshagovoe-rukovodstvo-kak-provesti-otsenku-vneshney-po-ru-3.jpg?v=1792962571)


**Что сделать:** разобрать `ai_report.json`, вручную подтвердить 3–5 верхних находок и зафиксировать итоговую таблицу.

**Зачем:** ИИ выдаёт гипотезы, а не доказательства. Версия `nginx 1.18.0` в баннере может быть подменена или устареть — приоритет ставится только после ручного подтверждения.

Выборка топ-находок:

```bash
jq -r '.findings | sort_by(-.score) | .[:5][] | "\(.host):\(.port) \(.service) — \(.score)"' \
  asm/out/ai_report.json
```

**Пример:** что должно появиться на экране.

```
93.184.216.34:443 nginx 1.18.0 — 8.7
93.184.216.35:22 openssh 8.2p1 — 6.1
93.184.216.35:8443 unknown — 5.4
```

Подтверждение одной находки:

```bash
nmap -sV -p 443 93.184.216.34
curl -sI https://api.example.com | grep -iE 'server|strict-transport'
```

Ожидаемый результат: та же версия в выводе `nmap`, что и в отчёте ИИ, и отсутствие `Strict-Transport-Security` в заголовках — это подтверждает находку, а не пересказывает её.

Итоговая таблица, которую вы отдаёте владельцу периметра:

| Приоритет | Актив | Признак | Чем подтвердили |
|---|---|---|---|
| 1 | 93.184.216.34:443 | устаревший `nginx 1.18.0` | `nmap -sV -p 443` |
| 2 | 93.184.216.35:22 | SSH открыт в интернет | `nmap -sT -p 22` |
| 3 | api.example.com | нет HSTS, сертификат истекает | `curl -sI`, `openssl x509` |

**Как проверить:** чек-лист приёмки.

- [ ] у каждой находки в топ-5 есть подтверждающая команда и её вывод
- [ ] ложные срабатывания помечены `false_positive` в трекере
- [ ] `ai_report.json` и `scan.xml` сохранены в `asm/out/` с датой в имени
- [ ] повторный запуск даёт тот же список хостов (нет «пропавших» активов)

Регулярное пересканирование (раз в неделю) удобно вешать на мониторинг с уведомлением в Telegram — схема разобрана в [гайде по мониторингу VPS через Telegram-бота](https://ai-release.org/guides/kak-nastroit-monitoring-vps-cherez-telegram-bota-s-mini.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide).

## Возможные проблемы и решения

**Проблема. Хост в списке есть, а данных по нему нет**

- Ошибка: `curl: (6) Could not resolve host: vpn.example.com`
- Причина: поддомен не публикуется в DNS (внутренний VPN-шлюз) или в `scope.txt` опечатка.
- Решение: `dig +short vpn.example.com` и `dig @1.1.1.1 +short vpn.example.com`. Если оба пусты — уберите строку из `scope.txt` или запросите у владельца корректное имя. Строку `NXDOMAIN` в `assets.json` не отправляйте.

**Проблема. Ответ CVEngine не разбирается**

- Ошибка: `jq: error (at asm/out/ai_report.json:0): Cannot index string with string "findings"`
- Причина: вместо JSON пришла строка — обычно HTML-страница ошибки или `{"error":"unauthorized"}`. Чаще всего истёк токен или неверный эндпоинт.
- Решение: `head -c 200 asm/out/ai_report.json` — увидите фактический ответ. Проверьте `echo $CVENGINE_TOKEN` (не пустой, без кавычек), повторите запрос с `-w "%{http_code}\n"`: `401` — ключ, `404` — путь эндпоинта, сверьтесь с документацией.

**Проблема. Сканирование падает сразу**

- Ошибка: `You requested a scan type which requires root privileges. QUITTING!`
- Причина: в команде остался `-sS` (SYN-скан) или `-O`, они требуют raw-сокетов.
- Решение: замените на `-sT`: `nmap -sT -sV --top-ports 100 -T4 -iL asm/scope.txt -oX asm/raw/scan.xml`. Проверка — `whoami` не должен требовать sudo для запуска.

## Резюме и следующие шаги

Вы собрали инвентарь периметра, обогатили его данными `nmap`/`curl`/`openssl`, прогнали через ИИ-слой CVEngine и подтвердили верхние находки руками. Дальше — поставьте пересканирование по расписанию и сравните два отчёта, чтобы видеть только новые активы и новые находки.

## FAQ

**Чем оценка поверхности атаки отличается от пентеста?**
ASM ищет и инвентаризирует всё, что торчит в интернет, и ранжирует риски без эксплуатации уязвимостей; пентест — это санкционированная попытка реально проэксплуатировать найденное.

**Нужен ли root для `nmap -sT`?**
Нет. Connect-скан устанавливает обычное TCP-соединение через системный вызов, поэтому работает от обычного пользователя. Root нужен для `-sS` и `-O`.

**Можно ли сканировать чужие домены, чтобы «проверить»?**
Нельзя. Сканируйте только активы из подписанного RoE; остальное — неправомерный доступ, даже если вы ничего не эксплуатировали.

**Что делать с находками, которые ИИ выдал, а `nmap` не подтвердил?**
Помечайте как ложное срабатывание и сохраняйте пару «находка + вывод проверки» — это обучающий материал для настройки правил на следующей итерации; конкретные настройки скоринга сверьте с документацией CVEngine.
