exploitability-analyzer

Assess whether discovered vulnerabilities are actually exploitable in your application context using CVSS scoring, attack path analysis, reachability.

SKILL.md

GitHub

Exploitability Analyzer

Evaluate discovered vulnerabilities in their actual deployment and code context to determine real-world exploitability, enabling teams to focus remediation efforts on vulnerabilities that pose genuine risk rather than chasing every CVE equally.

When to Use This Skill

Use this skill when you need to:

  • Triage a large number of vulnerability scan findings and determine which ones matter
  • Assess whether a specific CVE is exploitable given your application architecture
  • Build a risk-prioritized remediation plan from scanner output
  • Justify risk acceptance decisions to auditors or security reviewers
  • Determine if compensating controls reduce or eliminate exploitability
  • Evaluate attack paths from network boundary to vulnerable component
  • Differentiate between theoretical vulnerabilities and practical threats
  • Prepare vulnerability assessment reports for stakeholders
  • Reduce false-positive noise from automated scanning tools

Trigger phrases: "is this exploitable", "exploitability assessment", "vulnerability triage", "risk prioritization", "CVSS analysis", "attack path analysis", "compensating controls", "false positive analysis", "vulnerability context"

What This Skill Does

Core Capabilities

  • CVSS Contextual Scoring: Adjust base CVSS scores using temporal and environmental metrics specific to your deployment
  • Attack Path Analysis: Map the steps an attacker would need to reach and exploit a vulnerable component
  • Reachability Analysis: Determine if vulnerable code paths are actually invoked by your application
  • Compensating Controls Assessment: Evaluate whether existing security controls mitigate or block exploitation
  • Risk Prioritization: Produce a ranked list of vulnerabilities by actual exploitability, not just severity
  • Exploitation Prerequisite Mapping: Identify what conditions must be true for a vulnerability to be exploitable
  • False Positive Identification: Flag vulnerabilities that cannot be exploited in context

Exploitability Factors Framework

The analysis evaluates each vulnerability across six dimensions:

Factor Description Impact on Exploitability
Attack Vector Network, Adjacent, Local, Physical Remote = higher risk
Attack Complexity Conditions beyond attacker control High complexity = lower risk
Privileges Required Authentication level needed None required = higher risk
User Interaction Does exploitation need user action No interaction = higher risk
Reachability Is vulnerable code actually called Unreachable = not exploitable
Compensating Controls WAF, network segmentation, etc. Strong controls = lower risk

Analysis Methodology

Vulnerability Discovery
        |
        v
Phase 1: Contextual CVSS Scoring
        |
        v
Phase 2: Reachability Analysis
        |
        v
Phase 3: Attack Path Mapping
        |
        v
Phase 4: Compensating Controls Review
        |
        v
Phase 5: Exploitation Prerequisite Check
        |
        v
Phase 6: Risk Prioritization & Reporting

Instructions

Phase 1: Gather Vulnerability Data

Collect all vulnerability findings from scanning tools and normalize them into a consistent format.

Step 1.1: Normalize vulnerability records

Create a structured record for each finding:

{
  "id": "VULN-001",
  "cve": "CVE-2024-29041",
  "component": "express@4.18.2",
  "cvss_base": 6.1,
  "cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
  "vulnerability_type": "Open Redirect",
  "affected_function": "res.redirect()",
  "scanner_source": "npm audit",
  "description": "Express.js open redirect via crafted URL parameter"
}

Step 1.2: Parse the CVSS vector into individual metrics

def parse_cvss_vector(vector: str) -> dict:
    """Parse a CVSS 3.1 vector string into its component metrics."""
    metrics = {}
    parts = vector.replace("CVSS:3.1/", "").split("/")
    metric_names = {
        "AV": "attack_vector",
        "AC": "attack_complexity",
        "PR": "privileges_required",
        "UI": "user_interaction",
        "S": "scope",
        "C": "confidentiality",
        "I": "integrity",
        "A": "availability",
    }
    value_labels = {
        "AV": {"N": "Network", "A": "Adjacent", "L": "Local", "P": "Physical"},
        "AC": {"L": "Low", "H": "High"},
        "PR": {"N": "None", "L": "Low", "H": "High"},
        "UI": {"N": "None", "R": "Required"},
        "S": {"U": "Unchanged", "C": "Changed"},
        "C": {"N": "None", "L": "Low", "H": "High"},
        "I": {"N": "None", "L": "Low", "H": "High"},
        "A": {"N": "None", "L": "Low", "H": "High"},
    }
    for part in parts:
        key, value = part.split(":")
        metrics[metric_names.get(key, key)] = value_labels.get(key, {}).get(value, value)
    return metrics

Phase 2: Reachability Analysis

Determine whether your application actually invokes the vulnerable function or code path.

Step 2.1: Identify the vulnerable function or API

For each CVE, determine the specific function, method, or code path that contains the vulnerability. Consult the CVE description, advisory, and any published proof-of-concept.

Step 2.2: Trace call paths from application entry points

Search the codebase for direct and indirect usage of the vulnerable function:

# Find direct usage of the vulnerable function
grep -rn "res\.redirect" --include="*.js" --include="*.ts" src/

# For dependency vulnerabilities, check if the vulnerable module is imported
grep -rn "require('express')\|from 'express'" --include="*.js" --include="*.ts" src/

# Check if the specific vulnerable method is called
# For transitive dependencies, trace the import chain
grep -rn "import.*from.*vulnerable-package" --include="*.ts" src/

Step 2.3: Build a reachability determination

class ReachabilityResult:
    REACHABLE = "reachable"           # Vulnerable code is directly called
    INDIRECTLY_REACHABLE = "indirect" # Called through wrapper or framework
    UNREACHABLE = "unreachable"       # Vulnerable code path is never invoked
    CONDITIONAL = "conditional"       # Reachable only under specific conditions

def assess_reachability(cve_id: str, vulnerable_function: str, codebase_path: str) -> dict:
    """Assess whether a vulnerable function is reachable from application code."""
    result = {
        "cve": cve_id,
        "vulnerable_function": vulnerable_function,
        "reachability": ReachabilityResult.UNREACHABLE,
        "call_sites": [],
        "evidence": "",
        "conditions": [],
    }

    # Step 1: Search for direct invocations
    # Step 2: Trace through wrappers and abstractions
    # Step 3: Check if the invocation uses vulnerable parameters
    # Step 4: Determine conditional factors

    return result

Step 2.4: Document unreachable vulnerabilities

If the vulnerable function is never called, document the finding as a false positive with evidence:

### VULN-001: CVE-2024-29041 (express open redirect)
- **Reachability**: UNREACHABLE
- **Evidence**: Application never calls `res.redirect()` with user-controlled input. All redirect targets are hardcoded constants in `src/routes/auth.js` lines 42, 67, 89.
- **Recommendation**: Low priority. Update express in next scheduled dependency refresh.

Phase 3: Attack Path Analysis

For reachable vulnerabilities, map the complete attack path from external entry point to vulnerable component.

Step 3.1: Identify entry points

Enumerate all external-facing entry points that could reach the vulnerable component:

entry_points:
  - type: HTTP endpoint
    path: /api/v1/users/redirect
    method: GET
    authentication: required
    input_source: query parameter "url"

  - type: HTTP endpoint
    path: /auth/callback
    method: GET
    authentication: none
    input_source: query parameter "redirect_uri"

Step 3.2: Map the attack path

Document each step an attacker must traverse:

## Attack Path: CVE-2024-29041 via /auth/callback

1. **Entry Point**: GET /auth/callback?redirect_uri=<attacker_url>
2. **Authentication Gate**: None (public endpoint)
3. **Input Validation**: redirect_uri validated against allowlist in middleware
4. **Framework Processing**: Express route handler in auth.js:34
5. **Vulnerable Call**: res.redirect(req.query.redirect_uri) at auth.js:52
6. **Exploitation**: Attacker crafts URL to bypass allowlist validation

### Attack Prerequisites
- Attacker must craft a URL that bypasses the allowlist regex
- The allowlist regex in middleware uses a permissive pattern
- No URL normalization is performed before validation

Step 3.3: Assess attack complexity in context

def assess_attack_complexity(attack_path: dict) -> str:
    """Determine the real-world attack complexity based on path analysis."""
    complexity_factors = []

    if attack_path["authentication"] != "none":
        complexity_factors.append("requires_authentication")

    if attack_path["input_validation"]:
        complexity_factors.append("must_bypass_validation")

    if attack_path["network_restrictions"]:
        complexity_factors.append("network_access_limited")

    if attack_path["rate_limiting"]:
        complexity_factors.append("rate_limited")

    if len(complexity_factors) == 0:
        return "LOW"
    elif len(complexity_factors) <= 2:
        return "MEDIUM"
    else:
        return "HIGH"

Phase 4: Compensating Controls Assessment

Evaluate existing security controls that may reduce or eliminate exploitability.

Step 4.1: Inventory existing controls

Check for each category of compensating control:

compensating_controls:
  network_layer:
    - name: WAF (Web Application Firewall)
      present: true
      rules_relevant: true
      effectiveness: "Blocks known open redirect patterns"

    - name: Network segmentation
      present: true
      details: "Vulnerable service is in private subnet, not internet-facing"

  application_layer:
    - name: Input validation
      present: true
      details: "URL allowlist middleware validates redirect targets"
      bypass_known: false

    - name: Content Security Policy
      present: true
      details: "CSP header restricts navigation targets"

    - name: Authentication requirement
      present: false
      details: "Endpoint is public"

  platform_layer:
    - name: Rate limiting
      present: true
      details: "100 requests per minute per IP"

    - name: Logging and alerting
      present: true
      details: "All redirects logged; anomaly detection active"

Step 4.2: Evaluate control effectiveness

def evaluate_controls(controls: list[dict]) -> dict:
    """Evaluate the combined effectiveness of compensating controls."""
    mitigation_score = 0
    effective_controls = []
    ineffective_controls = []

    for control in controls:
        if control["present"] and control["rules_relevant"]:
            if control.get("bypass_known"):
                ineffective_controls.append(control["name"])
            else:
                effective_controls.append(control["name"])
                mitigation_score += control.get("weight", 1)

    return {
        "mitigation_score": mitigation_score,
        "effective_controls": effective_controls,
        "ineffective_controls": ineffective_controls,
        "overall_mitigation": (
            "STRONG" if mitigation_score >= 3
            else "MODERATE" if mitigation_score >= 1
            else "WEAK"
        ),
    }

Phase 5: Contextual CVSS Rescoring

Adjust the base CVSS score using environmental and temporal metrics based on the analysis.

Step 5.1: Apply environmental metrics

def calculate_environmental_score(base_cvss: float, context: dict) -> float:
    """Adjust CVSS base score with environmental context."""
    adjusted = base_cvss

    # Reduce for strong compensating controls
    if context["compensating_controls"]["overall_mitigation"] == "STRONG":
        adjusted *= 0.6
    elif context["compensating_controls"]["overall_mitigation"] == "MODERATE":
        adjusted *= 0.8

    # Reduce for limited reachability
    if context["reachability"] == "unreachable":
        adjusted = 0.0
    elif context["reachability"] == "conditional":
        adjusted *= 0.7

    # Reduce for high attack complexity in context
    if context["attack_complexity"] == "HIGH":
        adjusted *= 0.75

    # Adjust for asset criticality
    criticality_multiplier = {
        "critical": 1.2,
        "high": 1.0,
        "medium": 0.85,
        "low": 0.7,
    }
    adjusted *= criticality_multiplier.get(context["asset_criticality"], 1.0)

    return min(round(adjusted, 1), 10.0)

Step 5.2: Apply temporal metrics

Consider exploit maturity and patch availability:

temporal_factors:
  exploit_code_maturity:
    not_defined: 1.0
    unproven: 0.91
    proof_of_concept: 0.94
    functional: 0.97
    high: 1.0

  remediation_level:
    not_defined: 1.0
    official_fix: 0.95
    temporary_fix: 0.96
    workaround: 0.97
    unavailable: 1.0

  report_confidence:
    not_defined: 1.0
    unknown: 0.92
    reasonable: 0.96
    confirmed: 1.0

Phase 6: Risk Prioritization and Reporting

Step 6.1: Generate the prioritized vulnerability report

# Exploitability Assessment Report

## Executive Summary
- **Total vulnerabilities scanned**: 47
- **Exploitable in context**: 8 (17%)
- **Partially mitigated**: 12 (26%)
- **Not exploitable / false positive**: 27 (57%)

## Critical Findings (Immediate Action Required)

### VULN-023: CVE-2024-XXXXX - SQL Injection in query builder
| Metric | Value |
|--------|-------|
| Base CVSS | 9.8 |
| Contextual CVSS | 9.2 |
| Reachability | REACHABLE |
| Attack Path | Public API -> Query Builder -> Database |
| Compensating Controls | WEAK (no parameterized queries) |
| Exploit Maturity | Functional exploit available |
| **Recommendation** | **Fix immediately. Apply parameterized queries.** |

## High Priority (Fix within 1 sprint)
...

## Medium Priority (Fix within 1 quarter)
...

## Accepted Risk / False Positives
...

Step 6.2: Build the risk matrix visualization

               EXPLOITABILITY
        Low    Medium    High    Critical
    +--------+---------+--------+---------+
 C  | Accept | Monitor | Plan   | Fix Now |
 r  +--------+---------+--------+---------+
 i  | Monitor| Plan    | Fix    | Fix Now |
 t  +--------+---------+--------+---------+
 i  | Plan   | Fix     | Fix Now| Fix Now |
 c  +--------+---------+--------+---------+
 a  | Fix    | Fix Now | Fix Now| Fix Now |
 l  +--------+---------+--------+---------+

Step 6.3: Generate remediation recommendations

For each exploitable vulnerability, provide specific remediation guidance:

def generate_remediation(vuln: dict) -> dict:
    """Generate specific remediation guidance for an exploitable vulnerability."""
    return {
        "vulnerability": vuln["id"],
        "priority": vuln["contextual_priority"],
        "actions": [
            {
                "type": "immediate",
                "description": "Apply input validation to the affected endpoint",
                "effort": "low",
                "code_location": vuln["call_sites"],
            },
            {
                "type": "short_term",
                "description": f"Upgrade {vuln['component']} to patched version",
                "target_version": vuln["fixed_version"],
                "breaking_changes": vuln.get("breaking_changes", []),
            },
            {
                "type": "long_term",
                "description": "Implement defense-in-depth controls",
                "controls": ["WAF rule", "input sanitization library", "output encoding"],
            },
        ],
    }

Automation Integration

Integrate exploitability analysis into CI/CD pipelines:

# .github/workflows/exploitability-check.yml
name: Exploitability Analysis
on:
  schedule:
    - cron: "0 6 * * 1"  # Weekly Monday 6 AM
  workflow_dispatch:

jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run vulnerability scanner
        run: |
          npm audit --json > scan-results.json

      - name: Assess exploitability
        run: |
          python scripts/exploitability-analyzer.py \
            --scan-results scan-results.json \
            --codebase-path ./src \
            --controls-config ./security/controls.yaml \
            --output report.md

      - name: Fail on critical exploitable findings
        run: |
          python scripts/check-exploitable-threshold.py \
            --report report.md \
            --max-critical 0 \
            --max-high 3

Best Practices

  • Always assess reachability before investing time in remediation; unreachable vulnerabilities carry near-zero practical risk
  • Use contextual CVSS scoring rather than raw base scores; base scores assume worst-case deployment context
  • Document compensating controls thoroughly so that risk acceptance decisions are auditable
  • Re-evaluate exploitability when architecture changes, even if the vulnerability set has not changed
  • Maintain an inventory of compensating controls and review their effectiveness quarterly
  • Automate reachability analysis where possible to scale across large dependency trees
  • Treat exploitability assessment as a living process, not a one-time activity
  • Combine static analysis (code search) with dynamic analysis (runtime tracing) for higher confidence
  • Keep the temporal metrics current; a vulnerability with no known exploit today may have one tomorrow
  • Separate the roles of vulnerability discovery (automated scanners) and exploitability assessment (human-assisted analysis) to avoid confirmation bias

Common Pitfalls

  • Treating all CVEs equally: A CVSS 9.8 vulnerability that is unreachable poses less risk than a CVSS 5.0 vulnerability on a public endpoint. Always contextualize.
  • Ignoring transitive dependencies: The vulnerable function may be called by a library you depend on, not by your code directly. Trace the full call chain.
  • Over-relying on compensating controls: WAF rules and network segmentation are valuable, but they can be bypassed. Do not use them as a permanent substitute for fixing the underlying vulnerability.
  • Confusing "not exploitable now" with "not exploitable ever": Conditions change. A vulnerability that is currently unreachable may become reachable after a code change.
  • Skipping the attack path analysis: Reachability alone is insufficient. The vulnerable function may be reachable but impossible to reach with attacker-controlled input.
  • Ignoring exploit maturity: A vulnerability with a functional public exploit is far more dangerous than one that is only theoretically possible.
  • Failing to document risk acceptance: When you decide not to fix a vulnerability, record the rationale, the conditions under which the decision should be revisited, and the responsible party.
  • Performing analysis in isolation: Exploitability assessment should involve the development team (who understand the code), the security team (who understand threats), and the operations team (who understand the deployment environment).
  • Not updating the assessment: When you patch one vulnerability, the compensating controls picture may change for other vulnerabilities. Re-evaluate related findings.
  • Ignoring the human factor: Some vulnerabilities require social engineering or insider access. Factor in your threat model and the likelihood of these attack vectors for your organization.