Перейти к содержимому

Nuclei кастомные шаблоны API: пишем YAML-детект логических уязвимостей от нуля до срабатывания

Nuclei кастомные шаблоны API: пишем YAML-детект логических уязвимостей от нуля до срабатывания
Время чтения: 11 мин.

На Bug Bounty одного финтех-сервиса я вручную проверял IDOR в пяти API-эндпоинтах — подменял user_id в GET-запросах, сравнивал ответы, нашёл одну BOLA, получил баунти. Потом запустил Nuclei со стандартными шаблонами по тому же скоупу — ноль находок. Стандартные шаблоны из репозитория ProjectDiscovery заточены под известные CVE и мисконфигурации, а логические уязвимости конкретного API для них невидимы: эндпоинт отдаёт 200 OK с валидным JSON, формально всё в порядке. Масштабировать такие проверки на сотни эндпоинтов без кастомного YAML-шаблона, который учитывает бизнес-логику приложения, не получится. Дальше — путь от пустого файла до работающего BOLA-детекта.

Место Nuclei в цепочке атаки на API

Прежде чем писать YAML, стоит разобраться, на каком этапе Nuclei включается в работу и что происходит до и после.

В терминологии MITRE ATT&CK (открытая база тактик и техник атак; T-коды вроде T1190 — её идентификаторы) запуск сканера уязвимостей — техника T1595.002 «Vulnerability Scanning» на этапе разведки (reconnaissance). Кастомные шаблоны помогают найти цели, подверженные T1190 «Exploit Public-Facing Application» на этапе initial access — первоначального проникновения в систему.

Типичная цепочка при тестировании API:

  1. Сбор эндпоинтов — Swagger/OpenAPI-спецификация, перехват трафика через Burp Suite, сканирование с помощью katana или ffuf. На выходе: список URL-ов с методами (GET, POST, PUT, DELETE).
  2. Автоматизированное сканирование — запуск Nuclei с кастомными шаблонами. Этот этап мы и разбираем.
  3. Ручная верификация — каждое срабатывание проверяется руками: реально ли это BOLA, или сервер отдаёт одинаковые данные всем пользователям by design.
  4. Отчёт и эксплуатация — документирование impact, написание отчёта для Bug Bounty или заказчика.

Nuclei не заменяет ручной анализ — он масштабирует его. Вместо перебора 200 эндпоинтов вручную запускаете шаблон и получаете shortlist из 5–10 подозрительных точек для детального исследования.

Кастомные шаблоны Nuclei для API-логики работают и на внешнем пентесте (публичный API в интернете), и на внутреннем (микросервисы за VPN). Разница — в аутентификации: внешний API чаще защищён OAuth2/JWT, внутренний может обходиться без токенов или с базовой HTTP-аутентификацией.

Написание шаблонов Nuclei: анатомия YAML для API-тестирования

Nuclei (проект ProjectDiscovery, GitHub: более 20 000 звёзд, стабильные релизы несколько раз в год, активный мейнтенинг на 2025 год) использует YAML-шаблоны как единый формат описания проверок. Каждый шаблон — текстовый файл .yaml, который описывает три вещи: что за проверка, какой HTTP-запрос отправить и как интерпретировать ответ.

Обязательные секции шаблона:

  • id — уникальный идентификатор без пробелов. По нему Nuclei маркирует находку в консоли и отчёте.
  • info — метаданные: имя, автор, severity (критичность: info / low / medium / high / critical), теги для фильтрации при запуске.
  • http — описание HTTP-запросов: метод, путь, заголовки, тело.
  • matchers — правила, по которым Nuclei решает: сработал шаблон или нет. Здесь описывается разница между «уязвимым» и «нормальным» ответом.

Опциональные:

  • extractors — извлечение данных из ответа (токены, ID, значения полей). Нужны для multi-step шаблонов, где результат одного запроса идёт в следующий.
  • payloads — словари для подстановки, аналог Burp Intruder.

Динамические переменные — механизм подстановки реальных адресов при запуске. Основные: {{BaseURL}} заменяется на полный URL цели (например, http://target.com), {{Hostname}} — хост с портом. Переменные заключены в двойные фигурные скобки и чувствительны к регистру.

Поиск логических уязвимостей API: пишем BOLA-детект

Требования к окружению

Перед практикой убедитесь, что среда готова:

  • ОС: Linux (Kali, Ubuntu 22.04+), macOS или WSL2. Nuclei — бинарник на Go, работает на всех платформах.
  • Nuclei v3.0+: проверка — nuclei -version. Установка: go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest или через менеджер инструментов pdtm от ProjectDiscovery.
  • RAM: 2 ГБ минимум, 4 ГБ рекомендуется для параллельного сканирования.
  • Тестовая цель: VAmPI — специально уязвимое API-приложение. Запуск: git clone https://github.com/erev0s/VAmPI && cd VAmPI && docker compose up, инициализация БД: curl http://127.0.0.1:5002/createdb (в ответ придёт {"message": "Database populated."}).
  • Текстовый редактор с подсветкой YAML (VS Code с расширением YAML или любой аналог).

Что такое BOLA и почему стандартные шаблоны слепы

BOLA (Broken Object Level Authorization) — уязвимость, при которой API не проверяет, имеет ли текущий пользователь право доступа к запрашиваемому объекту. По классификации OWASP API Security Top 10 (2023) — реестра десяти критичных рисков для API — это угроза номер один: API1:2023. Старое название той же проблемы — IDOR (Insecure Direct Object Reference), и под этим именем вы встретите её в большинстве старых отчётов.

Как выглядит на практике: пользователь A отправляет GET /api/users/42 и видит свой профиль. Меняет ID на 43 — видит профиль пользователя B. Сервер не проверил, что A имеет право запрашивать данные B.

Стандартные шаблоны Nuclei из публичного репозитория не детектируют BOLA по трём причинам:

  1. Сервер возвращает 200 OK — нет аномального статус-кода.
  2. Ответ содержит валидный JSON — нет ошибок парсинга или stack trace.
  3. Контент отличается только данными — сканер не знает, какие данные «чужие», а какие «свои».

Для обнаружения нужен шаблон, учитывающий контекст конкретного API.

Пишем первый шаблон: шаг за шагом

Сценарий: VAmPI имеет эндпоинт GET /users/v1/{username}, возвращающий данные пользователя. Проверяем, отдаёт ли он чужой профиль без авторизации.

Шаг 1. Изучить эндпоинт. Отправьте запрос curl http://127.0.0.1:5002/users/v1/admin через Burp Suite или терминал. Посмотрите, какие поля есть в JSON-ответе. Для VAmPI это username и email. Запомните: эти поля станут критерием для matchers.

Шаг 2. Создать файл шаблона. Создайте файл bola-user-profile.yaml:

id: bola-user-profile
info:
  name: BOLA - доступ к чужому профилю
  author: yourname
  severity: high
  tags: api,bola,owasp-api1
http:
  - method: GET
    path:
      - "{{BaseURL}}/users/v1/admin"
    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200
      - type: word
        part: body
        words:
          - "admin"
          - "email"
        condition: and

Разберём построчно:

  • id: bola-user-profile — имя, которое Nuclei покажет в консоли при срабатывании. Используйте осмысленные имена: при 50 шаблонах template-1 превратится в хаос.
  • severity: high — BOLA с доступом к чужим данным = нарушение конфиденциальности, высокая критичность.
  • tags: api,bola,owasp-api1 — позволяют запускать группу шаблонов командой nuclei -tags bola.
  • matchers-condition: and — ВСЕ matchers должны сработать одновременно. Без and Nuclei засчитает совпадение ЛЮБОГО matcher, и вы утонете в ложных срабатываниях.
  • Первый matcher проверяет HTTP-статус 200 — запрос успешен, сервер не вернул 401 или 403.
  • Второй matcher ищет в теле ответа (part: body) слова admin и email одновременно (condition: and). Оба найдены — сервер отдал данные профиля admin.

Шаг 3. Запустить. Команда: nuclei -t bola-user-profile.yaml -u http://127.0.0.1:5002. Ожидаемый результат: строка с [bola-user-profile] и меткой [high] в выводе — оба matcher сработали, сервер вернул 200 OK с данными admin.

Если вывод пустой — шаблон не сработал. Добавьте флаг -debug: nuclei -t bola-user-profile.yaml -u http://127.0.0.1:5002 -debug покажет полный HTTP-запрос и ответ. По ним увидите, почему matchers не совпали — может, поле называется не email, а mail, или сервер вернул 403.

Matchers и multi-step шаблоны Nuclei для автоматизации поиска уязвимостей

Типы matchers для логики API

В примере выше использованы два типа matchers — status и word. Nuclei поддерживает шесть типов, но для логических уязвимостей API регулярно нужны три:

status — проверка HTTP-кода ответа. Базовый фильтр: 200 = данные отданы, 403 = доступ запрещён. Используется почти в каждом шаблоне.

word — поиск строки в ответе. Параметр part задаёт область поиска: body (тело), header (заголовки), all (везде). Параметр condition: and требует наличия ВСЕХ перечисленных слов, condition: or — хотя бы одного.

dsl — выражения на встроенном мини-языке Nuclei (DSL — Domain Specific Language, язык для описания условий). Позволяют проверять длину ответа, комбинировать условия, делать арифметику. Пример: status_code == 200 && contains(body, "email") && !contains(body, "error") — статус 200, в теле есть email, но нет error.

Типичная ошибка новичка: шаблон с единственным matcher type: status, status: 200 сработает на ЛЮБОЙ странице, отвечающей 200 OK. Бесполезно — сотни ложных срабатываний. Всегда комбинируйте status с word или dsl через matchers-condition: and.

Вторая частая ошибка: слишком общие слова в word matcher. Слово "user" встретится в ответе почти любого API. Берите специфичные маркеры: конкретные имена полей ("phone_number", "balance", "ssn"), значения конкретного пользователя, уникальные строки, которых не будет при ошибке авторизации.

Аутентифицированный BOLA-детект с extractors

Простой шаблон выше проверяет неаутентифицированный доступ. Большинство реальных API требуют токен. Для проверки BOLA в таком API нужен multi-step шаблон: сначала логин и извлечение токена, затем запрос к чужому ресурсу с этим токеном.

Nuclei поддерживает цепочки запросов через raw-формат — запросы описываются целиком, включая заголовки и тело, и выполняются последовательно:

id: bola-authenticated
info:
  name: BOLA - authenticated access
  author: yourname
  severity: high
  tags: api,bola,auth
http:
  - raw:
      - |
        POST {{BaseURL}}/users/v1/login HTTP/1.1
        Content-Type: application/json

        {"username":"attacker","password":"attacker123"}
      - |
        GET {{BaseURL}}/users/v1/admin HTTP/1.1
        Authorization: Bearer {{token}}
    cookie-reuse: true
    extractors:
      - type: regex
        name: token
        part: body
        regex:
          - '"auth_token":\s*"([^"]+)"'
        internal: true
    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200
      - type: word
        part: body
        words:
          - "admin"
          - "email"
        condition: and

Что здесь происходит по шагам:

  1. Первый запросPOST /users/v1/login с кредами пользователя attacker. Nuclei отправляет его и получает JSON с токеном.
  2. Extractor с type: regex выхватывает значение токена из ответа по регулярке. internal: true означает: сохранить в переменную token для следующих запросов, но не показывать в финальном отчёте.
  3. Второй запросGET /users/v1/admin с заголовком Authorization: Bearer {{token}}. Nuclei подставляет извлечённый токен и запрашивает данные пользователя admin от имени attacker.
  4. Matchers проверяют результат второго запроса: статус 200 и наличие слов admin + email в теле. Оба условия выполнены — авторизация сломана, BOLA подтверждена.

cookie-reuse: true сохраняет куки между запросами в цепочке — пригодится для API, использующих сессионные куки вместо JWT.

Запуск: nuclei -t bola-authenticated.yaml -u http://127.0.0.1:5002. Ожидаемый вывод — строка [bola-authenticated] [high] с адресом цели.

Ограничения кастомных шаблонов при API security тестировании

Кастомные шаблоны Nuclei — инструмент с конкретными границами. Знать эти границы не менее ценно, чем уметь писать шаблоны.

Критерий Кастомные шаблоны Nuclei Ручной тест (Burp Suite) Коммерческие сканеры
Скорость на 100+ эндпоинтах Высокая Низкая Средняя
Обнаружение BOLA/IDOR Да (при шаблоне под API) Да Частично (эвристики)
Race conditions, state-баги Нет Да Частично
Стоимость Бесплатно Бесплатно / платно (Pro) Платно
Время подготовки 15–60 мин на шаблон Минуты на запрос Минуты на настройку

Когда кастомные шаблоны Nuclei НЕ работают:

  • Сложные схемы авторизации. OAuth2 с PKCE, mTLS или HMAC-подписи запросов — настройка аутентификации в шаблоне превращается в мучение. Проще перехватить токен через Burp и передать в Nuclei через переменную окружения или заголовок (-H "Authorization: Bearer <token>").
  • UUID-идентификаторы. Если API использует UUID вместо числовых ID, подстановка «чужого» идентификатора невозможна без знания конкретного UUID. Шаблон не сгенерирует валидный UUID другого пользователя — нужна предварительная разведка.
  • Rate limiting и WAF. Nuclei по умолчанию шлёт до 150 запросов в секунду. API с лимитами (OWASP API4:2023 — Unrestricted Resource Consumption: без лимитов атакующий может вызвать DoS или накрутить расходы) заблокирует сканер. Используйте флаг -rl 3 для ограничения до 3 запросов в секунду.
  • Бинарные или зашифрованные ответы. Если API отдаёт не JSON, а protobuf, gRPC или зашифрованный payload — matchers word бесполезны. Придётся работать с dsl и проверкой длины ответа, а это повышает долю false positive.
  • Многошаговая бизнес-логика. Уязвимости, завязанные на состояние (race conditions, TOCTOU), на порядок вызовов нескольких микросервисов или на тайминги — Nuclei не покрывает. Тут только ручной пентест.

По данным Verizon DBIR 2025, веб-атаки составили 26% всех подтверждённых нарушений безопасности в 2024 году. Кастомные шаблоны Nuclei закрывают ту часть этих атак, которая поддаётся формализации: типовые BOLA, IDOR, утечки данных через предсказуемые эндпоинты — то, что чаще всего находят на Bug Bounty.

По моим наблюдениям, соотношение примерно 60 на 40. Шесть из десяти багов в API, которые я нахожу, поддаются автоматизации через Nuclei после первого ручного обнаружения. Остальные четыре — сложная логика, которую видишь только при ручной работе в Burp. Но даже 60% — это десятки сэкономленных часов на каждом крупном скоупе.

Есть мнение, что шаблонные сканеры — инструмент для начинающих, а «настоящие пентестеры» работают исключительно руками. Я вижу ситуацию иначе: написание кастомного шаблона требует более глубокого понимания API-логики, чем разовая ручная проверка. Вы не напишете работающий matcher, пока не разберётесь, как API авторизует запросы, какие поля возвращает и чем «свой» ответ отличается от «чужого». Шаблон — это формализация экспертного знания. Если не получается описать баг в YAML, значит, вы его не до конца понимаете.

Именно поэтому для тех, кто переходит в API-пентест, первый кастомный шаблон — не упражнение на автоматизацию, а тест на понимание. Возьмите одну конкретную уязвимость (BOLA подходит идеально), напишите шаблон, добейтесь срабатывания на тестовом стенде. Разница между «запускаю чужие шаблоны» и «написал свой и нашёл баг» — тот самый порог, который отделяет оператора сканера от исследователя. На курсе WAPT эту цепочку — от ручного обнаружения BOLA до автоматизации через кастомный шаблон — проходят в модулях с лабами, где можно сломать и починить всё без последствий.

Эту тему и смежные навыки разбирают на практике в курсе «Тестирование веб-приложений на проникновение (WAPT)» Codeby Academy.