Risk-Projector Case Study

Vulnerability lists lie by omission. A scanner tells you a host runs an unpatched service; a ticket queue tells you 4,812 findings are open this quarter; a compliance dashboard tells you mean-time-to-remediate for "critical" is 27 days. None of those artifacts tell you what a competent attacker does next, or how environmental risk shifts week over week as patches land, configurations drift, and new code ships.

This post walks through a risk projector built inside CelvexGroup's continuous-validation platform: a graph-based model that ingests CVE feeds, asset context, exposure signals, and prior exploitation telemetry, then projects the marginal risk each finding contributes to a defined crown-jewel outcome. We used it to re-triage a mid-sized enterprise's backlog of roughly 12,000 open vulnerabilities. The projector's ranking disagreed with CVSS-only prioritization on 71% of the top 100 findings. Validation payloads against the reprioritized list confirmed that the projector's top decile contained 4.3× more genuinely reachable exploit chains than the CVSS top decile.

Below we explain how the projector is structured, walk through three findings where it materially changed the answer, and share the pipeline components we can safely open-source.

Why CVSS-Only Prioritization Keeps Failing

The base CVSS score encodes vendor-supplied worst-case assumptions. It knows nothing about whether the vulnerable path is exposed to the network you care about, whether authentication is enforced upstream, or whether the finding sits on a host with read access to a secret store. Two findings from the environment illustrate the failure mode in both directions:

CVSS treated the first as a yawn and the second as worth a ticket. Both calls were wrong. The projector's job is to close that gap.

The Projector, End to End

The projector is a directed multigraph. Nodes are assets, identities, secrets, and outcomes. Edges are capabilities: "if you control node A, here is the cost and probability of reaching node B." Vulnerabilities are edge-weight modifiers — a fresh unauthenticated RCE lowers the cost of an edge; a mitigating control (EDR with a known-good detection for the technique) raises it.

The scoring loop:

# projector/score.py — simplified
def project_risk(graph, findings, crown_jewels, samples=10_000):
    apply_finding_modifiers(graph, findings)   # mutate edge weights
    baseline = monte_carlo_reachability(graph, crown_jewels, samples)

    contributions = {}
    for f in findings:
        with graph.snapshot():
            revert_finding_modifiers(graph, [f])   # pretend f is fixed
            counterfactual = monte_carlo_reachability(
                graph, crown_jewels, samples
            )
            # Marginal risk = how much reachability drops if we fix f
            contributions[f.id] = baseline - counterfactual
    return sorted(contributions.items(), key=lambda kv: -kv[1])

The Monte Carlo step samples adversary paths under a configurable initial-access distribution (phishing on a corp laptop, exposed edge service, third-party SaaS token compromise, insider). Each finding's score is the counterfactual drop in expected reachability from remediating that finding alone. This is what "risk-based prioritization" is supposed to mean and rarely does: not a static severity label, but a differentiable objective the security team can push down.

Three Findings That Flipped Rank

Finding A: CVE-2019-10869 — a path traversal / unrestricted upload in the Ninja Forms WordPress plugin's Uploads add-on. NVD score: 8.1. Original ticket priority: high. Projector priority: low. The affected WordPress instance was a marketing microsite in an isolated VPC with no egress to the identity provider and no shared credentials with production. Remediating it moved reachability by 0.4%.

Finding B: CVE-2018-1154 — a username-enumeration issue in an older SecurityCenter build. CVSS: informational-adjacent. Projector priority: critical. The same host advertised its SSO endpoint on the internet, and the customer's lockout policy allowed 20 attempts per 30 minutes. Enumerated usernames plus a lenient lockout policy plus known credential-reuse patterns produced a 6-hop path to a domain admin token with 38% simulated success. Reachability dropped 11 points when the enumeration was patched and the lockout policy tightened.

Finding C: CVE-2018-8727 — a path traversal in a video-management gateway. CVSS medium. The finding appeared isolated (OT-adjacent, segmented), but the projector's exposure ingester found the management interface routed through a misconfigured reverse proxy. It was assigned to the segmentation team, not the patching team — the right fix was the proxy rule, not the vendor patch.

What We Changed in the Model — and What We Did Not

Six months of running the projector against live environments produced three durable lessons:

  1. Do not let CVSS leak into the objective. It is fine as one feature among many, but regressing your model against CVSS rediscovers CVSS. Ground truth for training must come from validated exploitation attempts, red-team retros, and incident data.
  2. Counterfactuals beat scores. "Marginal risk reduction" is intuitive for engineers and executives alike. It also composes: you can sum contributions across a sprint's tickets and honestly report "this sprint reduced modeled reachability by 8.7 points."
  3. Explainability is not optional. Every projector output ships with the sampled paths that produced it. A finding ranked "critical" that a defender cannot trace to a concrete attack path will be ignored — and rightly so.

The projector does not replace the humans who write payloads and read PCAPs. It makes their work legible to the rest of the organization and ensures their morning triage targets the 10 findings that actually move risk — not the top of an alphabetized CVE list.

If your prioritization pipeline still ends at the CVSS column, you are not doing risk-based vulnerability management. You are doing severity-based ticket generation and calling it strategy.

Verifiable security.