Skip to content

Detection Score Explained

BlackSnufkin edited this page May 3, 2026 · 1 revision

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 high-level shape

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.


Risk levels

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.


File-type contributions

PE info (10% weight)

From file_info.json's pe_info block β€” written at upload time, no analyzer involvement.

  • High-entropy sections β€” > 7.0 adds 10 per section, > 7.5 adds 20 per section. Capped at 40.
  • Suspicious imports β€” CreateRemoteThread, VirtualAllocEx, WriteProcessMemory, NtMapViewOfSection, ZwMapViewOfSection count as critical (+15 each); LoadLibraryA/W, GetProcAddress, OpenProcess, VirtualAllocExNuma count 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 go or rust (those legitimately ship with mismatched checksums).

Static analysis (50% weight)

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_threat triggered β†’ +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.

Dynamic analysis (40% weight)

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.

EDR contribution (additive, capped at +50)

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.


Why EDR doesn't appear on the file_info hero buttons

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.


Process-type scoring

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.

Driver-type scoring (BYOVD)

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].


Risk factors (the indicators list)

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"

See also

πŸ“Œ LitterBox Β· self-hosted payload analysis sandbox

Release


πŸš€ Getting Started

πŸ“Š Pipelines & Pages

πŸ”¬ Scanners Β· 4 modules

πŸ›°οΈ EDR Integration
πŸ”Œ API & Clients
βš™οΈ Configuration & Dev

Releases Β· CHANGELOG Β· Issues Β· README

Clone this wiki locally