Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

الخلاصاتاتصالالخصوصية© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
Nimbus Vestige — A forensic reconstruction engine for cloud and identity incident response. | Kitploit
أدوات/GitLabGitLab/dobybaxter127/nimbus-vestige
ReconnaissanceDigital ForensicsPenetration TestingCloud SecurityThreat IntelligenceIdentity & Access Management (IAM)Learning & EducationIncident ResponseLog Analysis
GitLabdobybaxter127/nimbus-vestige

Nimbus Vestige

A forensic reconstruction engine for cloud and identity incident response.

236منذ 21س 24دلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة
عرض المستودع
المحتوى غير متوفر باللغة المطلوبة. عرض النسخة الإنجليزية.
# Nimbus Vestige (NV)

**A forensic reconstruction engine for cloud and identity incident response.**

Given fragmented cloud, SaaS, and identity telemetry — control-plane logs, sign-in
events, token and consent activity — NV reconstructs how an intrusion most likely
moved across accounts and services, enumerates the other most-likely paths it could
have taken, and reports every step with calibrated, explainable confidence. It
refuses to assert what the evidence cannot support.

> Detection tells you *that* something happened. Nimbus Vestige tells you *how* — and *what else*.

---

## Why this exists

Modern intrusions do not "break in." They log in. Identity is now the primary attack
vehicle, implicated in the large majority of cloud IR investigations, and most
intrusions span multiple surfaces — identity plus cloud plus SaaS plus endpoint.
The telemetry that records them is fragmented and inconsistent, which already forces
responders to reconstruct the story by hand from incomplete data.

That manual reconstruction is slow, and it fails in a predictable way: the responder
anchors on the first plausible narrative and misses the real one. NV automates the
reconstruction and directly attacks that failure mode by always presenting the ranked
space of plausible paths, not a single story.

Crucially, NV does this with **honesty as the product**. The market's stated enemy is
black-box confidence — a tool that asserts a conclusion without showing its work.
Every number NV produces is traceable to the specific evidence that earned it, and
anything it cannot support is withheld rather than guessed.

---

## What it is — and is not

NV is **not** a detector, a scanner, an auditor, or an attack-runner. Those tools
tell you *that* something happened and measure it. NV works **backward** — abductive
reconstruction of the *mechanism* from patterns, across a fragmented identity estate.

- **Not a detector** — it doesn't fire alerts on activity; it explains how activity fits together.
- **Not a SIEM** — it enters through a narrow wedge (post-incident narrative reconstruction), not as a log platform.
- **Not a rule engine** — when nothing matches a known pattern, it still reconstructs, degrading gracefully instead of going blind.

---

## Why it is valid long-term

1. **Blue team regenerates; red team commoditizes.** Offensive tooling finds a finite,
   patchable set of holes and is folding into automated CI/CD safety pipelines.
   Reconstructing *how* an intrusion happened never resolves — attackers keep inventing,
   so the need is permanent and self-renewing.

2. **The engine is substrate-independent.** The core logic — reconstruct the mechanism
   from patterns, with calibrated confidence and a stop rail — is committed to
   cloud/identity first, but ports to network, endpoint, and OT later. The target can
   change without rewriting the thesis.

3. **Reconstruction enables hardening.** Once you know how they got in — and which other
   doors were open — you build the defenses. The untaken-but-plausible paths are a
   hardening backlog, often worth more than the reconstruction itself, because most
   breaches exploit preventable exposure, not novel tradecraft.

4. **It degrades gracefully on the novel intrusion** — the very incident that matters
   most. A signature/rule engine goes blind on a zero-day; NV's abductive core still
   produces a most-likely path, honestly flagged as lower-confidence.

5. **Honesty is a moat.** Calibrated, case-based confidence with ranked alternatives is
   exactly what the market says it wants and what black-box competitors structurally
   cannot offer without redesign.

---

## Architecture

Raw provider logs → normalized event/entity graph → reconstruction engine → 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)
```

The GUI is a **pure view layer** — it renders engine output and lets the analyst move
the trust floor. It contains no reconstruction logic of its own.

---

## The confidence model (the credibility of the whole product)

Every step carries a confidence in [0, 1], built by **evidence corroboration**:

```
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** — confidence ≥ 0.70 *and* two or more independent signals agree.
- **pattern-only** — above the trust floor but weakly or singly supported.
- **withheld** — below the trust floor; shown greyed, never silently guessed or hidden.

Path scores compose their links by geometric mean, so one weak link honestly drags a
path down rather than being averaged away.

Every weight above — the per-technique base rates, the per-signal bonuses, the missing
penalties — lives in a single `PARAMS` table in `nv/confidence.py`. Those are NV's v1
priors, and they are *calibratable*: `nv/calibration.py` can refit them against labeled
ground truth and the engine will load the result, falling back to the v1 priors when no
calibration is supplied (see below).

### The trust floor

The withhold rule, baked into the engine's output contract — not just the UI. Below the
floor, a step is withheld. Dragging the floor is the **honesty-versus-coverage tradeoff
made physical**: up for strict/high-trust, down for permissive/high-coverage. The
default floor is an editorial decision about where NV sits before the analyst touches it.

---

## Calibrating confidence against ground truth

The v1 weights are defensible, but they are priors. `nv/calibration.py` tunes them
against **labeled ground truth** — events where we know which ones were part of the
intrusion and which were benign — and, crucially, measures whether tuning actually
helped, so a calibration is adopted only if it lowers calibration error rather than just
moving numbers around.

Calibrated confidence means one thing: NV's confidence for a step should equal the
probability that the step really was on the attack path. So each labeled event's
confidence is treated as a predicted probability, and the harness fits:

- **per-operation base rates** — the empirical hit rate for that operation, Bayesian-
  shrunk toward the v1 prior so small samples don't overfit; and
- **per-signal bonuses and the two penalties** — by bounded coordinate descent that
  minimizes Brier score with an L2 pull back toward the v1 values.

The verified/withheld thresholds are policy, not calibration, and are left untouched.

It reports **Brier score, log-loss, ECE, AUC**, a **reliability table**, and a
**trust-floor sweep** (true-step recall vs benign false-positive rate at each floor),
before and after — so the honesty-versus-coverage tradeoff is legible rather than a
single number.

```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 source.** The honest input is telemetry from a BadZure / MAAD-AF run in a
real tenant, labeled by joining the unified audit log against the attack tool's own
activity record (every event the tool caused is on-chain; everything else is benign — see
`LABELED_SCHEMA` in `nv/calibration.py`). Until you point it at a real run,
`eval/calibration_cases.py` supplies a documented-shape **proxy** corpus, and every
artifact fitted from it is stamped `source="proxy"` in its provenance so a proxy fit can
never be mistaken for a live one. `calibration.proxy.json` is that bundled proxy artifact.

On the bundled proxy corpus the fit is a clear improvement — Brier 0.398 → 0.045, ECE
0.576 → 0.081, AUC 0.81 → 0.91, and the benign false-positive rate at the 0.60 floor
collapses from 0.69 to 0.00 (the v1 priors were badly over-confident on benign activity).
The proxy is deliberately conservative; a live-tenant run is what produces weights you
should actually deploy.

## Trust integrity (Phase 3)

Two first-class controls protect the reconstruction from bad inputs:

- **Authorized-scope gate** (`nv/scope.py`) — NV refuses to run on any estate not on the
  authorized list. Run with `--authorize <tenant-domain>` per estate you are permitted
  to investigate. A row the gate cannot place in any estate (an actor with no domain
  and no tenant or account) is refused too, because it cannot be shown to be in scope.
  Pass `--allow-unscoped` only if you are permitted to investigate those rows; they are
  still counted in the output.

- **Pattern-source validation** (`nv/validation.py`) — no pattern enters the library
  without passing: (1) trusted-source allowlist, (2) a required content checksum,
  (3) schema well-formedness, (4) valid ATT&CK-style technique id. Every admitted
  pattern keeps an audit record; rejects are logged with a reason. A malformed or
  out-of-policy feed entry is stopped upstream before it can manufacture a false
  reconstruction. This is the same skepticism the engine applies to evidence, moved to
  the patterns themselves.

  The checksum is a consistency check: it is computed from a pattern's own content when
  the pattern is built, so it catches content that changed afterward (an edited cache or
  committed library file). It cannot catch a feed poisoned at the source. For that, pin
  the raw sha256 of a feed payload you trust with `update_feeds.py --pin URL=SHA256`, and
  any other payload at that URL is refused before validation.

---

## Keeping the knowledge current (staying ahead of attacker evolution)

A reconstruction engine is only as current as its pattern library plus its ability to
handle what the library has never seen. NV addresses staleness on **two independent
fronts**, by design:

### 1. The known layer stays fresh through a validated update pipeline

The library is not hardcoded — `nv/patterns.py` loads every pattern (including the
builtin set) through `nv/validation.py`, so external feeds enter by the same trusted
path. The live network clients that pull these feeds on a schedule are implemented in
`nv/feeds.py` and driven by `update_feeds.py`. Sources, in trust tiers:

- **MITRE ATT&CK (STIX)** — the authoritative technique taxonomy. The connector reads
  the ATT&CK STIX index, pulls the latest enterprise release, and refreshes the
  authoritative technique name and tactic for every operation NV reasons over —
  dropping any whose technique ATT&CK has since revoked or deprecated. A retired
  technique is dropped from every source, the builtin set included, so its operations
  fall back to the abductive layer (`no known match`) instead of carrying stale
  taxonomy. NV keeps ownership of the operation→technique binding and the base-rate
  prior; ATT&CK owns the taxonomy.
- **Vetted CTI via TAXII 2.1** — a full discovery → api-root → collection → objects
  client. Pulls STIX attack-pattern objects from a vetted CTI collection and refreshes
  the techniques NV tracks. Weighted below ATT&CK.
- **Sigma community ruleset** — pulled as a git archive; each cloud/identity rule that
  names an O365/Entra operation and carries an `attack.tXXXX` tag becomes an
  operation→technique candidate, with a base rate derived from the rule's own severity
  `level` and then scaled down by the community source weight. Rules that don't map are
  skipped with a reason, never guessed.

On an operation collision the union is ordered by ascending trust before commit, so an
authoritative ATT&CK binding always wins over a community one; lower-tier content is
still admitted and recorded in the audit as corroboration. `update_library(candidates)`
re-ingests the whole set through validation and rebuilds the library atomically, so a
poisoned feed can never leave the library half-updated.

**From update to reconstruction.** A committed run saves the library to
`.nv_feeds/library.json`. The engine uses it when you pass `--feeds-state .nv_feeds`
to `run_nv.py`; without the flag it uses the builtin library. Either way the startup
output says which library ran, and `reconstruction.json` records it under `library`.
The saved patterns go back through validation on load, and if any fail (the file
changed after it was committed) the engine refuses to run rather than use part of it.

**Two allowlists, not one.** Validation already gates on the source tag; the connectors
add a network-layer *host* allowlist, so a connector can only fetch from a trusted host
over TLS — the transport equivalent of the source allowlist, and a guard against a
hijacked or mistyped feed URL. Every fetch records the raw-payload sha256, URL and time
as provenance, and `update_feeds.py` prints each sha256 so you can pin a payload you
trust. A pinned URL that returns anything else fails its pull, and the library keeps
the last good candidates for that feed.

**Why the validation layer matters more as the feeds grow:** the more you automate
updates, the more an auto-update pipeline becomes a trust risk in disguise — a wrong or
poisoned feed injects false patterns and manufactures false reconstructions. NV treats
ingestion as adversarial: allowlist, checksum, schema, and audit trail on every pattern,
every update.

### 2. The abductive layer covers what no feed has yet named

Feeds always lag the newest tradecraft. The abductive core is the hedge: when an
observed operation matches **no** library pattern, NV does not drop it — it reconstructs
the most-likely path from first principles and flags it `no known match` at reduced
confidence. This is validated in the eval suite (`eval/ground_truth_cases.py`), where a
deliberately-unknown managed-identity token-theft step is caught, sequenced correctly,
and honestly down-weighted below the trust floor.

Together these mean NV never fully goes stale: the known layer tracks the published
frontier through a poisoning-resistant pipeline, and the abductive layer keeps the tool
from going blind on the intrusion the frontier hasn't caught up to yet.

### Suggested update cadence

`nv/feeds.py` carries a per-feed cadence and `update_feeds.py` only pulls what is due,
so it can be dropped straight into cron or a systemd timer:

- ATT&CK STIX — 90 days (a few official releases a year).
- CTI/TAXII — 7 days (as the feed publishes), always through validation.
- Sigma — 30 days.

```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

# refuse an ATT&CK release unless it matches the sha256 you trust
# (each run prints the sha256 and URL of every payload it fetched)
python3 update_feeds.py --feed attack-stix --force --pin URL=SHA256

# reconstruct with the committed library instead of the builtin one
python3 run_nv.py your_events.jsonl reconstruction.json \
    --authorize yourtenant.onmicrosoft.com --feeds-state .nv_feeds
```

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.

---

## A note on the demo data

**The reconstruction shown in `nv_gui.html` is built from a public research dataset**
— the invictus-ir Office 365 unified audit log corpus — **not from any company's live
tenant.** It exists so the engine can be demonstrated and dogfooded end-to-end without
anyone's cooperation. To use NV for real, an organization wires in its own Entra/M365
audit data, as described below. No proprietary or customer data is included anywhere in
this project.

## Connecting your own data

NV runs wherever you run it; your logs never have to leave your environment. Four steps:

**1 — Export your audit logs.** NV reads the Microsoft 365 unified audit log. Source it
from Microsoft Purview (Audit search → export CSV), the `Search-UnifiedAuditLog`
Exchange Online PowerShell cmdlet, the Microsoft Graph `auditLogs` / `signIns`
endpoints, or a Sentinel/SIEM export of `OfficeActivity` and `SigninLogs`.

**2 — Normalize it** into the event model NV reasons over:

```bash
python3 nv_extract_identity_events.py your_audit_export.csv your_events.jsonl
```

This keeps sign-ins, consent grants, service-principal and role changes, and mailbox
access — including identity operations NV has never named, so they still reach the
abductive layer — and drops the rest. It reads a CSV carrying the standard `AuditData`
column, or JSON/JSONL where each record is an `AuditData` object (the shape the
invictus-ir research corpus ships in).

**3 — Reconstruct, scope-gated.** The engine refuses to run on a tenant you haven't
explicitly authorized:

```bash
python3 run_nv.py your_events.jsonl reconstruction.json \
    --authorize yourtenant.onmicrosoft.com
```

Service principals and other actors without an email-style name have no domain, so the
ingest scopes them by the tenant id their record carries, as `m365:<OrganizationId>`.
If the engine lists one of these as unauthorized, add it with a second `--authorize`.

**4 — View.** Open `nv_gui.html` and click **⤒ load reconstruction.json** to point it at
the file the engine just produced — the same screen then shows your incident, your
entities, and your confidence bands. Every dot on the embedding canvas re-runs the
reconstruction for that entity on click.

### Analyzing AWS CloudTrail instead

The engine is substrate-independent — only the operation vocabulary is provider-specific
(`nv/providers.py`). The AWS path is the same three steps against 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
```

The ingest keeps IAM/STS/sign-in events (novel IAM operations included, so they reach the
abductive layer) plus S3 object reads, and scope-gates on the AWS **account** rather than a
tenant domain. The same abductive core, confidence model, and trust floor apply unchanged.

The GUI also carries a **demo-data banner** and an in-app "Connecting your own data"
guide, so anyone opening it understands the reconstruction is public sample data until
they wire in their own.

## Project layout

```
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]
```

Run everything from the project root so the `nv` package is importable.

## Running it

```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)
```

Keep the pattern library current and tune confidence:

```bash
python3 update_feeds.py                       # pull + validate feeds that are due
python3 calibrate.py --labeled run.jsonl --origin live --out calibration.json
```

### Evals

```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 eval/scope_test.py          # scope gate refuses other and unscoped estates
python3 eval/entities_test.py       # every canvas entity reconstructs on click
python3 eval/label_join_test.py     # tool activity log joins into labeled ground truth
python3 calibrate.py                # calibration harness on the bundled proxy corpus
```

Each `eval/` script above exits nonzero when a check fails, and CI runs all of them on
every merge request and on the default branch.

---

## Status

**Working end-to-end on real data** (invictus-ir O365 dataset), across the full plan:

| Phase | Item | Status |
|---|---|---|
| 1 | Normalized event/entity model | done |
| 1 | Ingest + normalization (Entra/M365 beachhead) | done |
| 2 | Known layer (ATT&CK pattern library) | done |
| 2 | Evidence-corroboration confidence model | done |
| 2 | Trust floor in the output contract | done |
| 2 | Abductive core (unknown layer) | done, eval-proven |
| 2 | Ranked competing hypotheses | done |
| 3 | Pattern-source validation layer | done |
| 3 | Authorized-scope control | done |
| 3 | Live feed connectors (ATT&CK STIX / TAXII / Sigma) + scheduler | done |
| 4 | Two-column screen on real engine output | done |
| 4 | Embedding / similarity canvas | done |
| 4 | Canvas-driven per-entity reconstruction (click re-runs the chain) | done |
| 4 | GUI loads engine `reconstruction.json` (point it at your file) | done |
| 4 | Multi-provider substrate (AWS CloudTrail, eval-proven) | done |
| 5 | Aesthetic / theming | done |
| 5 | Confidence-calibration harness + engine integration | done |

### Honest known gaps (next iterations)

- **Live-tenant calibration numbers.** The calibration *harness* is complete and the
  interface is proven, but the shipped `calibration.proxy.json` is fitted on documented-
  shape proxy data. Deployable weights require running the harness against a real
  BadZure / MAAD-AF run — an operational step for the adopter.
- **Feed-driven vocabulary growth.** The connectors refresh the operations NV already
  reasons over; letting a feed *introduce* a new operation (and wire it into the
  initial/pivot/collection classification) is future work.
- **Second-provider depth.** AWS is in as a proof of substrate-independence (pack +
  ingest + eval), but only IAM/STS/S3. Widening the AWS pack, adding provider-specific
  feed connectors, and a third substrate (GCP, Okta) are the next steps.

---


## License

Nimbus Vestige is **source-available** under the **PolyForm Noncommercial License
1.0.0** (see [LICENSE](https://gitlab.com/dobybaxter127/nimbus-vestige/-/blob/main/LICENSE)). The whole tool — reconstruction engine, both
provider ingests, GUI, and evaluation harness — is free to inspect, run, and use
for any **noncommercial** purpose: personal projects, research, education,
non-profits, and evaluation. Read every line before you decide.

Nimbus Vestige is an open project, not a commercial product. The license covers
noncommercial use only.

---

### Author

Doby Baxter 2026
تنزيل الأداة