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.