Continuous Adversary is my summer-internship project: a continuous adversary-emulation and security-validation platform. It answers a question most vulnerability scanners can only guess at — "Is this CVE in our software actually exploitable on our systems, and would our monitoring catch it if someone tried?" — and it answers it safely, without ever touching production or exposing proprietary source code to the internet.

It is a defensive-security research project built end-to-end during the internship, with a formal design report, a working validated baseline, and a hardened, auditable architecture.


The problem it solves

Continuous vulnerability assessment joins two facts that are usually managed separately:

A scanner correlates the two and produces a list: "package v is affected by advisory c." But that match is only a hypothesis. It does not tell you whether the vulnerable code path is reachable in your deployment, whether the exploit really works against your exact build, or — most importantly for a blue team — whether your detections would fire if it were exploited.

Turning that hypothesis into a verified, evidence-backed answer normally means one of two bad options: run untrusted exploit code against production, or copy proprietary application source onto some internet-facing testing box. Both are unacceptable. Continuous Adversary's whole design exists to close that gap without either risk.


Architecture

Two isolated network segments sit either side of the Kali control host. The asset segment holds the real Windows target; the attack segment holds the disposable sandbox and the SIEM. The target and the sandbox have no route to each other — code only ever runs where it is safe to run.

Continuous Adversary architecture: an asset segment with the Windows target and agent, a Kali orchestrator running receiver, CVE matching, worker loop and read-only dashboard, and an attack segment with the disposable sandbox and Wazuh SIEM.

What it does, end to end

Continuous Adversary runs a strict, auditable lifecycle. Every step below is a control, not just a stage:

  1. Inventory (outbound-only agent). A Windows agent runs as a scheduled SYSTEM task and pushes an authenticated software inventory to the control host every 15 minutes. The target opens every connection; the control host never connects inbound and never supplies a filesystem path. This shrinks the target's attack surface to almost nothing.
  2. Deterministic matching. Installed package versions are matched to advisories through OSV (exact ecosystem/name/version), and standalone products against local NVD/CPE data. Exact dependency matches become candidates; fuzzy product matches stay flagged as "needs review."
  3. Human approval gate. Nothing proprietary moves automatically. A transfer requires an explicit, expiring campaign approved by two distinct people — the service owner and a security approver.
  4. Safe source transfer. Once approved, the agent gets a freshly generated key pair that expires within five minutes, scrubs configuration and secrets out of the bundle, and uploads it over restricted SFTP. The control host quarantines, hashes, and validates the bundle without executing it.
  5. Disposable sandbox. A worker restores a clean Windows Server "golden snapshot," rebuilds the exact vulnerable service inside it, and validates the finding there — never on the real asset.
  6. Detection validation. When the optional Wazuh integration is enabled, Continuous Adversary correlates the bounded test window against wazuh-alerts-* through a dedicated read-only account — so you learn not just "is it exploitable" but "did the SOC see it."
  7. Guaranteed cleanup. The sandbox snapshot is restored again in a finally path — after success, failure, and not-exploitable runs alike. Nothing persists between tests.

Why the design is the point

This is a defense-in-depth, zero-trust system, and the security is in the invariants, not the features:

Design decision Why it matters
Outbound-only agent protocol The protected machine exposes no inbound management service — the single biggest reduction in attack surface.
Two isolated networks, no route between target and sandbox The real asset and the place code actually runs can never reach each other.
Two-person, time-boxed approval No single operator (or compromised account) can exfiltrate source or launch a test.
Short-lived Ed25519 keys + strict host-key checking A leaked credential is worthless minutes later; every identity is pinned.
Fail-closed leases and re-validation before every job Policy, dependency evidence, expiry, and freshness are all re-checked at the last moment.
Read-only, authenticated dashboard Operational visibility that cannot approve a bundle or start a test — separation of duties enforced in the UI.
Snapshot-restore isolation Every run starts from a known-clean state and leaves nothing behind.

The internship also produced a formal control-plane specification (FastAPI, PostgreSQL, OpenID Connect, React/TypeScript, hardened Docker Compose) that replaces the manual commissioning bridge with an auditable, identity-backed campaign workflow — with signed, replay-resistant connector commands and staged rollout — while keeping all source and raw inventory local to the control host.


The operator's view

The dashboard is authenticated and strictly read-only. It gives the SOC full visibility — queue depth, the test running right now, verified findings, and alerts needing attention — but by design it cannot approve a bundle or launch a test. That separation of duties is enforced in the UI itself.

Continuous Adversary overview dashboard showing queue depth, active test, verified exploits, alerts and a recent-activity table of CVEs with their service, status and severity.

The inventory view is where public advisories meet local reality: each running service on the target is listed with its ecosystem, exact dependency count, dependency-scan status, and whether it has an automatic sandbox adapter — i.e. whether Continuous Adversary can reproduce and validate it safely.

Continuous Adversary inventory dashboard listing the target's running services with type, state, ecosystem, dependency count and a testable flag.


Why it's a strong project


🛠️ Stack & Techniques