Files
git.stella-ops.org/docs/guides/epss-integration.md
StellaOps Bot b058dbe031 up
2025-12-14 23:20:14 +02:00

291 lines
7.7 KiB
Markdown

# EPSS Integration Guide
## Overview
EPSS (Exploit Prediction Scoring System) is a FIRST.org initiative that provides probability scores for vulnerability exploitation within 30 days. StellaOps integrates EPSS as a risk signal alongside CVSS and KEV (Known Exploited Vulnerabilities) to provide more accurate vulnerability prioritization.
## How EPSS Works
EPSS uses machine learning to predict the probability that a CVE will be exploited in the wild within the next 30 days. The model considers:
- Vulnerability characteristics (CVSS metrics, CWE, etc.)
- Social signals (Twitter mentions, GitHub issues, etc.)
- Exploit database entries
- Historical exploitation patterns
EPSS outputs two values:
- **Score** (0.0-1.0): Probability of exploitation in next 30 days
- **Percentile** (0-100): Ranking relative to all other CVEs
## How EPSS Affects Risk Scoring in StellaOps
### Combined Risk Formula
StellaOps combines CVSS, KEV, and EPSS signals into a unified risk score:
```
risk_score = clamp01(
(cvss / 10) + # Base severity (0-1)
kevBonus + # +0.20 if in CISA KEV
epssBonus # +0.02 to +0.10 based on percentile
)
```
### EPSS Bonus Thresholds
| EPSS Percentile | Bonus | Rationale |
|-----------------|-------|-----------|
| >= 99th | +10% | Top 1% most likely to be exploited; urgent priority |
| >= 90th | +5% | Top 10%; high exploitation probability |
| >= 50th | +2% | Above median; moderate additional risk |
| < 50th | 0% | Below median; no bonus applied |
### Example Calculations
| CVE | CVSS | KEV | EPSS Percentile | Risk Score |
|-----|------|-----|-----------------|------------|
| CVE-2024-1234 | 9.8 | Yes | 99.5th | 1.00 (clamped) |
| CVE-2024-5678 | 7.5 | No | 95th | 0.80 |
| CVE-2024-9012 | 6.0 | No | 60th | 0.62 |
| CVE-2024-3456 | 8.0 | No | 30th | 0.80 |
## Implementation Reference
### IEpssSource Interface
```csharp
// Location: src/RiskEngine/StellaOps.RiskEngine/StellaOps.RiskEngine.Core/Providers/IEpssSources.cs
public interface IEpssSource
{
/// <summary>
/// Returns EPSS data for the given CVE identifier, or null if unknown.
/// </summary>
Task<EpssData?> GetEpssAsync(string cveId, CancellationToken cancellationToken);
}
public sealed record EpssData(double Score, double Percentile, DateTimeOffset? ModelVersion = null);
```
### Risk Providers
**EpssProvider** - Uses EPSS score directly as risk (0.0-1.0):
```csharp
// Location: src/RiskEngine/StellaOps.RiskEngine/StellaOps.RiskEngine.Core/Providers/EpssProvider.cs
public const string ProviderName = "epss";
```
**CvssKevEpssProvider** - Combined provider using all three signals:
```csharp
// Location: src/RiskEngine/StellaOps.RiskEngine/StellaOps.RiskEngine.Core/Providers/EpssProvider.cs
public const string ProviderName = "cvss-kev-epss";
```
## Policy Configuration
### Enabling EPSS Integration
```yaml
# etc/risk-engine.yaml
risk:
providers:
- name: cvss-kev-epss
enabled: true
priority: 1
epss:
enabled: true
source: database # or "api" for live FIRST API
cache_ttl: 24h
# Percentile-based bonus thresholds
thresholds:
- percentile: 99
bonus: 0.10
- percentile: 90
bonus: 0.05
- percentile: 50
bonus: 0.02
```
### Custom Threshold Configuration
Organizations can customize EPSS bonus thresholds based on their risk tolerance:
```yaml
# More aggressive (higher bonuses for high-risk vulns)
epss:
thresholds:
- percentile: 99
bonus: 0.15
- percentile: 95
bonus: 0.10
- percentile: 75
bonus: 0.05
# More conservative (smaller bonuses)
epss:
thresholds:
- percentile: 99
bonus: 0.05
- percentile: 95
bonus: 0.02
```
## EPSS in Lattice Decisions
EPSS influences VEX lattice state transitions for vulnerability triage:
| Current State | EPSS >= 90th Percentile | Recommended Action |
|---------------|-------------------------|-------------------|
| SR (Static Reachable) | Yes | Escalate to CR (Confirmed Reachable) priority |
| SU (Static Unreachable) | Yes | Flag for review - high exploit probability despite unreachable |
| DV (Denied by Vendor VEX) | Yes | Review denial validity - exploit activity contradicts vendor |
| U (Unknown) | Yes | Prioritize for reachability analysis |
### VEX Policy Example
```yaml
# etc/vex-policy.yaml
lattice:
transitions:
- from: SR
to: CR
condition:
epss_percentile: ">= 90"
action: auto_escalate
- from: SU
to: REVIEW
condition:
epss_percentile: ">= 95"
action: flag_for_review
reason: "High EPSS despite static unreachability"
```
## Offline EPSS Data
EPSS data is included in offline risk bundles for air-gapped environments.
### Bundle Structure
```
risk-bundle-2025-12-14/
├── manifest.json
├── kev/
│ └── kev-catalog.json
├── epss/
│ ├── epss-scores.csv.zst # Compressed EPSS data
│ └── epss-metadata.json # Model date, row count, checksum
└── signatures/
└── bundle.dsse.json
```
### EPSS Metadata
```json
{
"model_date": "2025-12-14",
"row_count": 248732,
"sha256": "abc123...",
"source": "first.org",
"created_at": "2025-12-14T00:00:00Z"
}
```
### Importing Offline EPSS Data
```bash
# Import risk bundle (includes EPSS)
stellaops offline import --kit risk-bundle-2025-12-14.tar.zst
# Verify EPSS data imported
stellaops epss status
# Output:
# EPSS Data Status:
# Model Date: 2025-12-14
# CVE Count: 248,732
# Last Import: 2025-12-14T10:30:00Z
```
## Accuracy Considerations
| Metric | Value | Notes |
|--------|-------|-------|
| EPSS Coverage | ~95% of NVD CVEs | Some very new CVEs (<24h) not yet scored |
| Model Refresh | Daily | Scores can change day-to-day |
| Prediction Window | 30 days | Probability of exploit in next 30 days |
| Historical Accuracy | ~85% AUC | Based on FIRST published evaluations |
### Limitations
1. **New CVEs**: Very recent CVEs may not have EPSS scores yet
2. **Model Lag**: EPSS model updates daily; real-world exploit activity may be faster
3. **Zero-Days**: Pre-disclosure vulnerabilities cannot be scored
4. **Context Blind**: EPSS doesn't consider your specific environment
## Best Practices
1. **Combine Signals**: Always use EPSS alongside CVSS and KEV, not in isolation
2. **Review High EPSS**: Manually review vulnerabilities with EPSS >= 95th percentile
3. **Track Changes**: Monitor EPSS score changes over time for trending threats
4. **Update Regularly**: Keep EPSS data fresh (daily in online mode, weekly for offline)
5. **Verify High-Risk**: For critical decisions, verify EPSS data against FIRST API
## API Usage
### Query EPSS Score
```bash
# Get EPSS score for a specific CVE
stellaops epss get CVE-2024-12345
# Batch query
stellaops epss batch --file cves.txt --output epss-scores.json
```
### Programmatic Access
```csharp
// Using IEpssSource
var epssData = await epssSource.GetEpssAsync("CVE-2024-12345", cancellationToken);
if (epssData is not null)
{
Console.WriteLine($"Score: {epssData.Score:P2}");
Console.WriteLine($"Percentile: {epssData.Percentile:F1}th");
}
```
## Troubleshooting
### EPSS Data Not Available
```bash
# Check EPSS source status
stellaops epss status
# Force refresh from FIRST API
stellaops epss refresh --force
# Check for specific CVE
stellaops epss get CVE-2024-12345 --verbose
```
### Stale EPSS Data
If EPSS data is older than 7 days:
```bash
# Check staleness
stellaops epss check-staleness
# Import fresh bundle
stellaops offline import --kit latest-bundle.tar.zst
```
## References
- [FIRST EPSS Model](https://www.first.org/epss/)
- [EPSS API Documentation](https://www.first.org/epss/api)
- [EPSS FAQ](https://www.first.org/epss/faq)
- [StellaOps Risk Engine Architecture](../modules/risk-engine/architecture.md)