-
Notifications
You must be signed in to change notification settings - Fork 166
Detection Score Explained
LitterBox computes a 0β100 detection score per sample, plus a categorical level (Low / Medium / High / Critical) and a list of human-readable triggering indicators. This page explains exactly what feeds the number and how the weights work.
The implementation is in app/utils/risk_analyzer.py.
The score is computed differently for the three analysis types:
| Type | What contributes | Notes |
|---|---|---|
file (the default for uploaded payloads) |
pe_info Γ 0.10 + static Γ 0.50 + dynamic Γ 0.40 + EDR (additive, capped) |
Most common case |
process (live PID analysis) |
dynamic Γ 1.0 |
No file analysis to weigh in |
driver (BYOVD) |
BYOVD-specific scoring only | Static / dynamic / EDR not applicable |
For file, the three weighted contributions sum to 0β100, then the EDR contribution is added on top (capped) and the total is clipped to 100.
| Score | Level |
|---|---|
| 75β100 | Critical |
| 50β74 | High |
| 25β49 | Medium |
| 0β24 | Low |
Critical-band scores get a +15% multiplier (capped at 100) β when multiple strong signals all fire, the score should saturate hard rather than averaging out.
From file_info.json's pe_info block β written at upload time, no analyzer involvement.
-
High-entropy sections β
> 7.0adds 10 per section,> 7.5adds 20 per section. Capped at 40. -
Suspicious imports β
CreateRemoteThread,VirtualAllocEx,WriteProcessMemory,NtMapViewOfSection,ZwMapViewOfSectioncount as critical (+15 each);LoadLibraryA/W,GetProcAddress,OpenProcess,VirtualAllocExNumacount as high-risk (+8 each). Capped at 30. -
PE checksum mismatch β the stored PE checksum doesn't match the calculated one. Adds 25 β but only if the build toolchain isn't
goorrust(those legitimately ship with mismatched checksums).
From static_analysis_results.json. Three contributors:
YARA matches. Per-rule severity from rule metadata:
| Severity | Per-match weight |
|---|---|
CRITICAL |
100 |
HIGH |
80 |
MEDIUM |
50 |
LOW |
20 |
INFO |
5 |
Multiple matches at the same severity get diminishing returns: full weight for the first, then 0.5 ^ N for each additional. The total is normalized by halving (so the scale plays well with other signals) and then a "match multiplier" 1.0 + min(matches Γ 0.15, 0.5) is applied β many matches at lower severity still bump the score.
CheckPlz indicators. AV signature matches:
-
initial_threattriggered β +50 (a single AV signature firing is a strong signal) - Each additional indicator β +15, capped at +40.
File entropy. Whole-file entropy > 7.0 adds 20; > 7.5 adds 30.
From dynamic_analysis_results.json. Six contributors:
YARA-mem matches. Same severity scoring as static YARA, but no extra match-multiplier.
PE-Sieve. total_suspicious Γ 20 Γ severity_multiplier (1.5Γ if severity == 'critical'). Capped at 45 for files / 30 for processes.
Moneta memory anomalies. Per-anomaly weights:
| Indicator | Weight (file) | Weight (process) |
|---|---|---|
total_private_rwx |
15 | 10 |
total_modified_code |
12 | 10 |
total_heap_executable |
10 | 10 |
total_modified_pe_header |
10 | 10 |
total_private_rx |
8 | 8 |
total_inconsistent_x |
8 | 8 |
total_missing_peb |
5 | 5 |
total_mismatching_peb |
5 | 5 |
Per-indicator score capped at 2Γ weight; total Moneta contribution capped at 40 (file) / 30 (process).
Patriot behavior indicators. Per-finding severity scores:
| Severity | File | Process |
|---|---|---|
critical |
25 | 20 |
high |
15 | 15 |
medium |
10 | 10 |
low |
5 | 5 |
Total Patriot contribution capped at 35.
HSB (Hunt Sleeping Beacon). count Γ severity_score per detection. For files: severity multiplier 1 + (severity Γ 0.5); max 40 per detection. Detection count totaled, no global cap.
RedEdr β Defender at runtime. Only Defender's defender_events with category == 'threat' (a real ThreatFound verdict) bump the score. Each such event adds nothing individually β the presence of any threat verdict adds +50 as a flat contribution. Other RedEdr signals (network, audit-API, file-ops, child processes) are descriptive only β they show on the page and in the report but don't affect the score.
When edr_results exists for the sample, the strongest signal across all profiles becomes the contribution:
| Condition | Contribution |
|---|---|
| Any profile has high/critical alerts | +50 |
Any profile blocked the binary at write/spawn (status: blocked_by_av) |
+35 |
| Any profile has only low/medium alerts | +15 |
| No EDR alerts at all | 0 |
The EDR contribution is additive β it stacks on top of the weighted file/static/dynamic score. The justification: EDR alerts are real-world detection on an EDR-instrumented host, the strongest ground-truth signal LitterBox can collect. They should bump the score regardless of what local heuristics found.
The EDR contribution is part of the file's overall score, but each EDR run also has its own results page. The score on the file_info page reflects EDR alerts when present; the per-profile EDR pages render their own scoped detection assessment.
The total_alerts > 0 count drives the DETECTED badge on the EDR-row of the file_info page. The badge is gated on isTerminal && totalAlerts > 0 β it never claims detection while Phase-2 is still in flight, and never claims detection on a profile that returned zero alerts even after a successful run.
Live PID analysis (dynamic_analysis_results.json only β no file backing). Same dynamic contributors as the file path. Two normalizations apply:
- If there are zero YARA matches AND β€1 PE-Sieve hit, the score is capped at 65 (rule of thumb: a process with no yara matches and minimal memory anomalies is at most "high", never "critical").
- If no risk factor mentions "high", the score is capped at 75.
Computed by _calculate_byovd_risk in risk_analyzer.py.
Starting from 0, then:
| Signal | Score change |
|---|---|
Has any of: dangerous_imports, critical_imports, terminate_process, communication capability |
+55 |
| Not blocked on Windows 11 | +25 |
| Blocked on Windows 11 | -50 |
| Not blocked on Windows 10 | +20 |
| Blocked on Windows 10 | -20 |
| Not in LOLDrivers DB | +10 |
| Listed in LOLDrivers DB | -5 |
Blocked on both Windows 10 and 11 short-circuits to 0 (no exploitation potential).
Final score is clipped to [0, 100].
Every contributor that fires also adds a one-line human-readable factor to a risk_factors array. These appear in the report's "Triggering Indicators" section and on the file-info hero. Examples:
- "Critical: CheckPLZ AV signature triggered"
- "PE-Sieve observed 3 memory modifications"
- "5 weighted memory anomalies observed"
- "2 weighted runtime indicators observed"
- "Critical: Microsoft Defender flagged the binary at runtime (1 threat verdict)"
- "Critical: Elastic Defend raised 4 high/critical detection alerts"
- Application Architecture β where the risk calculator sits in the request flow
-
app/utils/risk_analyzer.pyβ the source -
EDR Integration β how EDR runs feed
edr_results
- π Home
- π§ Application Architecture
- π Dashboard
- π All in One Pipeline
- π― Detection Score Explained
- 𧬠Blender Scanner
- π FuzzyHash Scanner
- π‘οΈ HolyGrail BYOVD Scanner
- π YARA Rules Management