
Nimbus Vestige — Updated!
Движок криминалистической реконструкции для реагирования на инциденты в облаке и инциденты, связанные с идентификацией.
Nimbus Vestige (NV)
Механизм форензической реконструкции для реагирования на инциденты в облаке и с идентификационными системами.
На основе фрагментированной телеметрии облака, SaaS и идентификационных систем — журналов плоскости управления, событий входа, активности токенов и согласий — NV реконструирует, как вторжение с наибольшей вероятностью перемещалось между учётными записями и сервисами, перечисляет другие наиболее вероятные пути, которыми оно могло пойти, и сообщает о каждом шаге с калиброванной, объяснимой уверенностью. Он отказывается утверждать то, что не подтверждено доказательствами.
Обнаружение говорит вам, что что-то произошло. Nimbus Vestige говорит вам, как — и что ещё.
Зачем это существует
Современные вторжения не «взламывают». Они входят в систему. Идентификация теперь является основным вектором атак, фигурируя в подавляющем большинстве расследований инцидентов в облаке, и большинство вторжений охватывают несколько поверхностей — идентификация плюс облако плюс SaaS плюс конечные точки. Телеметрия, которая их фиксирует, фрагментирована и противоречива, что уже вынуждает реагирующих восстанавливать картину вручную из неполных данных.
Такая ручная реконструкция медленна и сбоит предсказуемым образом: реагирующий зацикливается на первом правдоподобном сценарии и упускает настоящий. NV автоматизирует реконструкцию и напрямую борется с этим видом сбоя, всегда представляя ранжированное пространство правдоподобных путей, а не единственную историю.
Ключевое: NV делает это с честностью как продуктом. Заявленный враг рынка — уверенность из «чёрного ящика» — инструмент, который утверждает вывод, не показывая свою работу. Каждое число, которое выдаёт NV, прослеживается до конкретного доказательства, которое его заработало, и всё, что он не может подтвердить, опускается, а не угадывается.
Что это такое — и чем не является
NV — это не детектор, не сканер, не аудитор и не симулятор атак. Эти инструменты говорят вам, что что-то произошло, и измеряют это. NV работает в обратную сторону — абдуктивная реконструкция механизма из паттернов, в рамках фрагментированной идентификационной инфраструктуры.
- Не детектор — он не запускает оповещения о действиях; он объясняет, как действия складываются воедино.
- Не SIEM — он входит через узкий клин (реконструкция нарратива после инцидента), а не как платформа для журналов.
- Не механизм правил — когда ничто не соответствует известному паттерну, он всё равно реконструирует, плавно деградируя, а не слепнет.
Почему это остаётся актуальным в долгосрочной перспективе
-
Синяя команда воспроизводится; красная команда коммодитизируется. Наступательные инструменты находят конечный, исправляемый набор дыр и встраиваются в автоматизированные CI/CD конвейеры безопасности. Реконструкция того, как произошло вторжение, никогда не завершается — атакующие продолжают изобретать, поэтому потребность постоянна и самовозобновляема.
-
Движок не зависит от инфраструктуры. Базовая логика — реконструировать механизм из паттернов, с калиброванной уверенностью и стоп-границей — сначала применяется к облаку/идентификации, но позже переносится на сеть, конечные точки и ОТ. Цель может меняться без переписывания тезиса.
-
Реконструкция обеспечивает укрепление защиты. Как только вы знаете, как они проникли — и какие ещё двери были открыты — вы строите защиту. Неиспользованные, но правдоподобные пути — это бэклог для укрепления, часто более ценный, чем сама реконструкция, потому что большинство взломов эксплуатируют предотвратимую уязвимость, а не новую тактику.
-
Он плавно деградирует на новом вторжении — именно на том инциденте, который важнее всего. Сигнатурный/правиловой движок слепнет на zero-day; абдуктивное ядро NV по-прежнему выдаёт наиболее вероятный путь, честно помеченный как имеющий более низкую уверенность.
-
Честность — это ров. Калиброванная, основанная на конкретных случаях уверенность с ранжированными альтернативами — это именно то, что рынок, по его словам, хочет, и то, что конкуренты из «чёрного ящика» структурно не могут предложить без перепроектирования.
Архитектура
Необработанные журналы провайдера → нормализованный граф событий/сущностей → движок реконструкции → JSON → GUI.``` O365 / Entra audit logs nv_extract_identity_events.py AWS CloudTrail (IAM/STS/S3) nv_extract_cloudtrail_events.py (ingest + normalize) ▼ normalized identity events (JSONL) ← one event model, any substrate │ nv/graph.py (typed per-actor timelines) ▼ reconstruction engine ├─ nv/patterns.py known layer: ATT&CK identity pattern library ├─ nv/providers.py provider packs: per-substrate op→ATT&CK vocab (Entra + AWS) ├─ nv/validation.py Phase 3: provenance + integrity gate on every pattern ├─ nv/feeds.py live ATT&CK STIX / TAXII / Sigma clients + scheduler ├─ nv/confidence.py evidence-corroboration scorer (calibratable weights) ├─ nv/calibration.py fit + measure confidence against labeled ground truth ├─ nv/reconstruct.py most-likely chain + ranked competing paths + trust floor └─ nv/scope.py authorized-scope gate (refuses unauthorized tenants/accounts) │ run_nv.py ▼ reconstruction.json ──► nv_gui.html (embedding canvas + two-column view + trust slider)
Графический интерфейс — это **чистый уровень представления** — он отображает выходные данные движка и позволяет аналитику перемещать нижнюю границу доверия. Он не содержит собственной логики реконструкции.
---
## Модель уверенности (достоверность всего продукта)
Каждый шаг несёт показатель уверенности в [0, 1], формируемый путём **подтверждения уликами**:```
confidence = per-technique base rate
+ bonus for each independent corroborating signal
(source IP, device, successful outcome, temporal adjacency, broad-consent flags)
− penalty for missing signals (e.g. no source IP to corroborate origin)
- verified — уверенность ≥ 0.70 и два или более независимых сигнала согласуются.
- pattern-only — выше порога доверия, но слабо или одиночно подтверждён.
- withheld — ниже порога доверия; показывается серым, никогда не угадывается молча и не скрывается.
Оценки путей объединяют составляющие их связи через геометрическое среднее, поэтому одно слабое звено честно тянет путь вниз, а не сглаживается усреднением.
Все веса выше — базовые частоты по техникам, бонусы по сигналам, штрафы за отсутствие — находятся в единой таблице PARAMS в nv/confidence.py. Это априорные значения NV версии v1, и они калибруемы: nv/calibration.py может перенастроить их по размеченным эталонным данным, и движок загрузит результат, возвращаясь к априорным значениям v1, когда калибровка не предоставлена (см. ниже).
Порог доверия
Правило удержания, вшитое в контракт вывода движка, а не только в UI. Ниже порога шаг удерживается. Сдвиг порога — это компромисс честности и покрытия, обретший физическую форму: вверх — для строгого/высокодоверительного режима, вниз — для разрешительного/высокопокрывающего. Порог по умолчанию — это редакторское решение о том, где находится NV, прежде чем аналитик коснётся его.
Калибровка уверенности по эталонным данным
Веса v1 обоснованны, но это априорные значения. nv/calibration.py настраивает их по размеченным эталонным данным — событиям, о которых мы знаем, какие из них были частью вторжения, а какие безвредными, — и, что крайне важно, измеряет, помогла ли настройка на самом деле, поэтому калибровка принимается только если она снижает ошибку калибровки, а не просто передвигает числа.
Калиброванная уверенность означает одно: уверенность NV для шага должна равняться вероятности того, что шаг действительно находился на пути атаки. Поэтому уверенность каждого размеченного события рассматривается как предсказанная вероятность, и испытательный стенд подгоняет:
- базовые частоты по операциям — эмпирическая частота попаданий для этой операции, байесовски сжатая к априорному значению v1, чтобы малые выборки не переобучались; и
- бонусы по сигналам и два штрафа — с помощью ограниченного покоординатного спуска, минимизирующего оценку Брайера с L2-притяжением обратно к значениям v1.
Пороги verified/withheld — это политика, а не калибровка, и они остаются нетронутыми.
Он сообщает оценку Брайера, log-loss, ECE, AUC, таблицу надёжности и развёртку по порогу доверия (полнота истинных шагов против частоты ложных срабатываний на безвредных событиях на каждом пороге), до и после, — чтобы компромисс между честностью и покрытием был наглядным, а не одним числом.```bash
calibrate against a real, labeled BadZure / MAAD-AF run
python3 calibrate.py --labeled run_labeled.jsonl --origin live
--run-label "badzure-2026-07 tenant-x" --out calibration.json
demonstrate on the bundled documented-shape proxy corpus
python3 calibrate.py --out calibration.json
run the engine on calibrated weights (opt-in; v1 priors are the default)
python3 run_nv.py events.jsonl out.json --authorize t.onmicrosoft.com
--calibration calibration.json
**Источник ground truth.** Честный вход — это телеметрия запуска BadZure / MAAD-AF в
реальном тенанте, размеченная путём соединения унифицированного журнала аудита с
собственным журналом активности инструмента атаки (каждое событие, вызванное
инструментом, присутствует в цепочке; всё остальное — безвредно — см.
`LABELED_SCHEMA` в `nv/calibration.py`). Пока вы не направите его на реальный запуск,
`eval/calibration_cases.py` предоставляет **прокси**-корпус с документированной
структурой, и каждый артефакт, подогнанный по нему, помечается `source="proxy"` в
своих провенанс-данных, чтобы прокси-подгонку нельзя было принять за реальную.
`calibration.proxy.json` — это встроенный прокси-артефакт.
На встроенном прокси-корпусе подгонка даёт явное улучшение — Brier 0.398 → 0.045, ECE
0.576 → 0.081, AUC 0.81 → 0.91, а частота ложных срабатываний на безвредной активности
при пороге 0.60 падает с 0.69 до 0.00 (априорные вероятности v1 были сильно завышены
для безвредной активности). Прокси намеренно консервативен; именно запуск в реальном
тенанте даёт веса, которые вам следует реально развернуть.
## Целостность доверия (Фаза 3)
Два первоклассных механизма контроля защищают реконструкцию от плохих входных данных:
- **Шлюз разрешённой области** (`nv/scope.py`) — NV отказывается запускаться на любой
инфраструктуре, не входящей в список разрешённых. Запускайте с `--authorize <tenant-domain>`
для каждой инфраструктуры, которую вам разрешено исследовать.
- **Валидация источников паттернов** (`nv/validation.py`) — ни один паттерн не попадает
в библиотеку, не пройдя: (1) список разрешённых доверенных источников, (2) проверку
контрольной суммы содержимого / защиту от подмены, (3) корректность схемы,
(4) валидный идентификатор техники в стиле ATT&CK. Каждый принятый паттерн сохраняет
запись аудита; отклонённые регистрируются с указанием причины. Отравленная или
повреждённая лента останавливается выше по потоку, прежде чем сможет сфабриковать
ложную реконструкцию. Это тот же скептицизм, который движок применяет к доказательствам,
перенесённый на сами паттерны.
---
## Поддержание знаний в актуальном состоянии (опережая эволюцию атакующих)
Движок реконструкции настолько актуален, насколько актуальна его библиотека паттернов
плюс его способность обрабатывать то, чего библиотека никогда не видела. NV решает
проблему устаревания на **двух независимых направлениях**, по замыслу:
### 1. Известный слой остаётся свежим благодаря проверенному конвейеру обновлений
Библиотека не захардкожена — `nv/patterns.py` загружает каждый паттерн (включая
встроенный набор) через `nv/validation.py`, поэтому внешние ленты входят по тому же
доверенному пути. Живые сетевые клиенты, которые по расписанию забирают эти ленты,
реализованы в `nv/feeds.py` и управляются через `update_feeds.py`. Источники, по
уровням доверия:
- **MITRE ATT&CK (STIX)** — авторитетная таксономия техник. Коннектор читает индекс
ATT&CK STIX, забирает последний enterprise-релиз и обновляет авторитетное название
техники и тактику для каждой операции, о которой рассуждает NV, — отбрасывая те,
чью технику ATT&CK с тех пор отозвал или признал устаревшей. NV сохраняет за собой
владение привязкой операция→техника и априорной базовой частотой; ATT&CK владеет
таксономией.
- **Проверенный CTI через TAXII 2.1** — полноценный клиент discovery → api-root →
collection → objects. Забирает объекты STIX attack-pattern из проверенной
CTI-коллекции и обновляет техники, которые отслеживает NV. Взвешивается ниже ATT&CK.
- **Набор правил сообщества Sigma** — забирается как git-архив; каждое
облачное/identity-правило, которое называет операцию O365/Entra и несёт тег
`attack.tXXXX`, становится кандидатом операция→техника, с базовой частотой,
выведенной из собственного уровня серьёзности `level` правила, а затем уменьшенной
на вес источника сообщества. Правила, которые не отображаются, пропускаются
с указанием причины и никогда не угадываются.
При коллизии операций объединение упорядочивается по возрастанию доверия перед
фиксацией, поэтому авторитетная привязка ATT&CK всегда побеждает сообщественную;
контент нижнего уровня всё равно принимается и записывается в аудит как подтверждение.
`update_library(candidates)` повторно прогоняет весь набор через валидацию и атомарно
пересобирает библиотеку, поэтому отравленная лента никогда не сможет оставить
библиотеку наполовину обновлённой.
**Два списка разрешённых, а не один.** Валидация уже ограничивает по тегу источника;
коннекторы добавляют список разрешённых *хостов* на сетевом уровне, поэтому коннектор
может получать данные только с доверенного хоста по TLS — транспортный эквивалент
списка разрешённых источников и защита от захваченного или набранного с опечаткой
URL ленты. Каждая загрузка записывает sha256 сырой полезной нагрузки, URL и время как
провенанс, поэтому последующий повторный pull может обнаружить изменение выше по потоку.
**Почему слой валидации становится важнее по мере роста лент:** чем больше вы
автоматизируете обновления, тем больше конвейер автоматического обновления становится
скрытым риском для доверия — неверная или отравленная лента внедряет ложные паттерны
и фабрикует ложные реконструкции. NV относится к приёму данных как к враждебному
процессу: список разрешённых, контрольная сумма, схема и журнал аудита для каждого
паттерна и каждого обновления.
### 2. Абдуктивный слой покрывает то, что ещё не назвала ни одна лента
Ленты всегда отстают от новейшего tradecraft. Абдуктивное ядро — это подстраховка:
когда наблюдаемая операция не соответствует **ни одному** паттерну библиотеки, NV не
отбрасывает её — он реконструирует наиболее вероятный путь из первых принципов
и помечает его `no known match` со сниженной уверенностью. Это проверяется в наборе
для оценки (`eval/ground_truth_cases.py`), где намеренно неизвестный шаг кражи токена
управляемого удостоверения обнаруживается, правильно выстраивается в последовательность
и честно понижается ниже порога доверия.
Вместе это означает, что NV никогда полностью не устаревает: известный слой отслеживает
опубликованный передний край через устойчивый к отравлению конвейер, а абдуктивный
слой не даёт инструменту ослепнуть на вторжении, которое передний край ещё не успел
догнать.
### Рекомендуемая периодичность обновлений
`nv/feeds.py` содержит периодичность для каждой ленты, а `update_feeds.py` забирает
только то, что уже подлежит обновлению, поэтому его можно сразу поместить в cron или
systemd-таймер:
- ATT&CK STIX — 90 дней (несколько официальных релизов в год).
- CTI/TAXII — 7 дней (по мере публикации ленты), всегда через валидацию.
- Sigma — 30 дней.```bash
# run whatever is due, keeping state under ./.nv_feeds (cron-friendly)
python3 update_feeds.py
# force all feeds now but only report what would change
python3 update_feeds.py --force --dry-run
# run fully offline against captured fixtures (no network)
python3 update_feeds.py --offline eval/fixtures --force
After any library change, re-run eval/run_eval.py to confirm known attack shapes still
reconstruct, eval/validation_cases.py to confirm the gate still rejects bad input, and
eval/feeds_offline_test.py to confirm the feed path is intact end to end.
Примечание о демонстрационных данных
Реконструкция, показанная в nv_gui.html, построена на общедоступном исследовательском наборе данных
— корпусе унифицированного журнала аудита Office 365 от invictus-ir — а не из данных какого-либо рабочего
тенанта. Он существует, чтобы движок можно было демонстрировать и обкатывать целиком, без
чьей-либо помощи. Для реального использования NV организация подключает собственные данные аудита Entra/M365,
как описано ниже. Никакие проприетарные или пользовательские данные не включены ни в один компонент
этого проекта.
Подключение собственных данных
NV работает там, где вы его запускаете; вашим журналам не нужно покидать вашу среду. Четыре шага:
1 — Экспортируйте журналы аудита. NV читает унифицированный журнал аудита Microsoft 365. Получите его
из Microsoft Purview (Audit search → export CSV), командлета Search-UnifiedAuditLog
в Exchange Online PowerShell, конечных точек Microsoft Graph auditLogs / signIns
или экспорта OfficeActivity и SigninLogs из Sentinel/SIEM.
2 — Нормализуйте их в модель событий, на основе которой NV строит рассуждения:```bash python3 nv_extract_identity_events.py your_audit_export.csv your_events.jsonl
Это сохраняет входы в систему, согласия на предоставление доступа, изменения субъектов-служб и ролей, а также доступ к почтовым ящикам — включая операции с идентификацией, которые NV никогда не называл, поэтому они по-прежнему достигают абдуктивного слоя — и отбрасывает остальное. Он читает CSV со стандартным столбцом `AuditData` или JSON/JSONL, где каждая запись представляет собой объект `AuditData` (в таком виде распространяется исследовательский корпус invictus-ir).
**3 — Реконструкция, ограниченная областью.** Движок отказывается работать с клиентом, который вы явно не авторизовали:```bash
python3 run_nv.py your_events.jsonl reconstruction.json \
--authorize yourtenant.onmicrosoft.com
4 — Просмотр. Откройте nv_gui.html и нажмите ⤒ load reconstruction.json, чтобы указать ему на
файл, который только что создал движок, — тот же экран затем покажет ваш инцидент, ваши
сущности и ваши доверительные интервалы. Каждая точка на канве эмбеддингов повторно запускает
реконструкцию для этой сущности по клику.
Анализ AWS CloudTrail вместо этого
Движок не зависит от подложки — поставщикозависим только словарь операций
(nv/providers.py). Путь AWS — те же три шага, но против CloudTrail:```bash
python3 nv_extract_cloudtrail_events.py cloudtrail.json aws_events.jsonl
python3 run_nv.py aws_events.jsonl reconstruction.json --authorize aws:123456789012
Приём данных сохраняет события IAM/STS/входа в систему (включая новые операции IAM, чтобы они попадали на уровень абдукции), а также чтения объектов S3 и ограничивает область действия уровнем **аккаунта** AWS, а не доменом тенанта. Та же самая абдуктивная основа, модель уверенности и минимальный порог доверия применяются без изменений.
В графическом интерфейсе также есть **баннер демо-данных** и встроенное руководство «Подключение собственных данных», чтобы любой, кто его открывает, понимал, что реконструкция основана на публичных демонстрационных данных, пока они не подключат свои собственные.
## Структура проекта```
run_nv.py reconstruction engine entrypoint (scope-gated)
nv_extract_identity_events.py ingest: M365/Entra unified audit log -> event model
nv_extract_cloudtrail_events.py ingest: AWS CloudTrail -> event model [iteration 3]
update_feeds.py pull + validate threat-intel feeds on a cadence [iteration 1]
calibrate.py fit confidence weights from labeled ground truth [iteration 2]
nv/ the engine package
graph.py patterns.py validation.py confidence.py reconstruct.py scope.py
providers.py per-substrate op->ATT&CK vocabulary packs (Entra + AWS) [iteration 3]
feeds.py ATT&CK STIX / TAXII 2.1 / Sigma clients + scheduler [iteration 1]
calibration.py metrics + parameter fit [iteration 2]
eval/ evals + offline fixtures (no network, no tenant)
nv_gui.html pure view layer (loads engine reconstruction.json)
calibration.proxy.json bundled proxy calibration artifact [iteration 2]
Запускайте всё из корня проекта, чтобы пакет nv был импортируемым.
Запуск```bash
1. normalize raw O365/Entra audit logs into the event model
python3 nv_extract_identity_events.py auditrecords.csv nv_identity_events.jsonl
2. reconstruct (scope-gated; add --calibration calibration.json to use fitted weights)
python3 run_nv.py nv_identity_events.jsonl reconstruction.json
--trust-floor 0.6 --authorize your-tenant.onmicrosoft.com
3. view — open nv_gui.html (renders reconstruction.json)
Поддерживайте библиотеку шаблонов в актуальном состоянии и настраивайте уверенность:```bash
python3 update_feeds.py # pull + validate feeds that are due
python3 calibrate.py --labeled run.jsonl --origin live --out calibration.json
Оценки```bash
python3 eval/run_eval.py # attack shapes reconstruct correctly python3 eval/validation_cases.py # poisoned patterns are rejected python3 eval/feeds_offline_test.py # feed connectors + scheduler (offline) python3 eval/providers_aws_test.py # AWS chain reconstructs through the same engine python3 calibrate.py # calibration harness on the bundled proxy corpus
---
## Статус
**Полностью работает на реальных данных** (набор данных invictus-ir O365) в рамках всего плана:
| Фаза | Пункт | Статус |
|---|---|---|
| 1 | Нормализованная модель событий/сущностей | готово |
| 1 | Приём + нормализация (плацдарм Entra/M365) | готово |
| 2 | Известный слой (библиотека паттернов ATT&CK) | готово |
| 2 | Модель уверенности с подтверждением уликами | готово |
| 2 | Нижняя граница доверия в контракте вывода | готово |
| 2 | Абдуктивное ядро (неизвестный слой) | готово, подтверждено оценкой |
| 2 | Ранжированные конкурирующие гипотезы | готово |
| 3 | Слой валидации источников паттернов | готово |
| 3 | Контроль авторизованной области | готово |
| 3 | Коннекторы живых фидов (ATT&CK STIX / TAXII / Sigma) + планировщик | готово |
| 4 | Двухколоночный экран на реальном выходе движка | готово |
| 4 | Холст эмбеддингов / схожести | готово |
| 4 | Пообъектная реконструкция через холст (клик перезапускает цепочку) | готово |
| 4 | GUI загружает `reconstruction.json` движка (укажите на ваш файл) | готово |
| 4 | Мультипровайдерная основа (AWS CloudTrail, подтверждено оценкой) | готово |
| 5 | Эстетика / оформление | готово |
| 5 | Харнесс калибровки уверенности + интеграция с движком | готово |
### Честно о известных пробелах (следующие итерации)
- **Числа калибровки на живом тенанте.** Калибровочный *харнесс* завершён, а
интерфейс проверен, но поставляемый `calibration.proxy.json` подобран на
прокси-данных документированной формы. Развёртываемые веса требуют прогона
харнесса против реального запуска BadZure / MAAD-AF — операционный шаг для
внедряющей стороны.
- **Рост словаря за счёт фидов.** Коннекторы обновляют операции, которые NV уже
рассматривает; возможность фида *вводить* новую операцию (и включать её в
классификацию initial/pivot/collection) — будущая работа.
- **Глубина второго провайдера.** AWS включён как доказательство независимости
от основы (пакет + приём + оценка), но только IAM/STS/S3. Расширение пакета
AWS, добавление провайдер-специфичных коннекторов фидов и третья основа
(GCP, Okta) — следующие шаги.
---
## Лицензия
Nimbus Vestige доступен в исходном коде в соответствии с **PolyForm Noncommercial License
1.0.0** (см. [LICENSE](https://gitlab.com/dobybaxter127/nimbus-vestige/-/blob/main/LICENSE)). Весь инструмент — движок реконструкции, приём
данных обоих провайдеров, GUI и оценочный харнесс — можно свободно изучать,
запускать и использовать для любых **некоммерческих** целей: личные проекты,
исследования, образование, некоммерческие организации и оценка. Прочтите каждую
строку, прежде чем решать.
**Коммерческое использование требует платной лицензии** — использование в продукте
или услуге, которые вы продаёте или размещаете, внутри производственных или
внутренних систем коммерческой компании, либо в платных работах по реагированию
на инциденты или для клиентов. См.
[COMMERCIAL-LICENSE.md](https://gitlab.com/dobybaxter127/nimbus-vestige/-/blob/main/COMMERCIAL-LICENSE.md).
---
### Автор
Doby Baxter 2026