-
Notifications
You must be signed in to change notification settings - Fork 166
YARA Rules Management
LitterBox runs YARA in two complementary lanes against the same rules file:
- Static — scans the uploaded file on disk.
- Dynamic — scans the running process memory of the executed payload (or any live PID).
Both lanes contribute to the Detection Score Explained number. Both use Scanners/Yara/LitterBox.yar.
# Config/config.yaml
analysis:
static:
yara:
enabled: true
tool_path: ".\\Scanners\\Yara\\yara64.exe"
command: "{tool_path} -s -m {rules_path} {file_path}"
rules_path: ".\\Scanners\\Yara\\LitterBox.yar"
timeout: 120
dynamic:
yara:
enabled: true
tool_path: ".\\Scanners\\Yara\\yara64.exe"
command: "{tool_path} -s -m {rules_path} {pid}"
rules_path: ".\\Scanners\\Yara\\LitterBox.yar"
timeout: 120Same rules_path for both lanes — keep one rule pack synchronized across modes.
Scanners/Yara/
├── yara64.exe # YARA scanner binary
├── LitterBox.yar # Master include file (one entry per Rules/ file)
└── Rules/
├── Linux_Backdoor_Bash.yar
├── Windows_Trojan_Cobalt.yar
├── Technique_ProcessHollowing.yar
├── Tool_Mimikatz.yar
└── ... (one .yar per rule family)
LitterBox.yar is purely an aggregator — it includes every individual .yar under Rules/:
include ".\Rules\Linux_Backdoor_Bash.yar"
include ".\Rules\Windows_Trojan_Cobalt.yar"
include ".\Rules\Technique_ProcessHollowing.yar"
include ".\Rules\Tool_Mimikatz.yar"
// ...Splitting per-family means you can disable a noisy rule pack by commenting out one include line without touching the underlying ruleset.
The detection score uses YARA rule metadata to weight matches. Every rule that should contribute to scoring must declare a severity in its meta block:
rule My_Detection {
meta:
author = "..."
severity = "HIGH" // CRITICAL | HIGH | MEDIUM | LOW | INFO
description = "Catches X"
strings:
$s1 = "..."
condition:
any of them
}Severity weights (risk_analyzer.py):
| Severity | Weight | Approx. meaning |
|---|---|---|
CRITICAL |
100 | Definitive, family-specific signature (Mimikatz strings, Cobalt Strike beacon stub, etc.) |
HIGH |
80 | Strong technique match (process hollowing, RWX shellcode, syscall stubs) |
MEDIUM |
50 | Generic but suspicious patterns (default — assigned when severity is missing or unknown) |
LOW |
20 | Weak indicators, possibly noisy |
INFO |
5 | Surface match for visibility, not detection |
Numeric severity (severity = 100) also works and maps to the same buckets via NUMERIC_SEVERITY_MAP.
Multiple matches at the same severity get diminishing returns — first match full weight, subsequent at 0.5^N. So 5 LOW matches don't sum to a CRITICAL: scoring is anti-spam by design.
Option A — drop a new file:
- Create
Scanners/Yara/Rules/Your_New_File.yar. - Add one line to
Scanners/Yara/LitterBox.yar:
include ".\Rules\Your_New_File.yar"Option B — edit an existing family file: add the rule to the appropriate Rules/<family>.yar. No LitterBox.yar change needed.
Restart isn't necessary — YARA is invoked fresh per analysis run, so updates take effect on the next scan.
Drop-in helper for when you've batch-added rule files and don't want to hand-edit the master:
# generate_rules.ps1
param (
[string]$RulesDirectory,
[string]$OutputFile = "LitterBox.yar"
)
if (-Not (Test-Path -Path $RulesDirectory -PathType Container)) {
Write-Error "Directory not found: $RulesDirectory"
exit 1
}
$yarFiles = Get-ChildItem -Path $RulesDirectory -Filter "*.yar"
if ($yarFiles.Count -eq 0) {
Write-Error "No .yar files in $RulesDirectory"
exit 1
}
$outputPath = Join-Path -Path $RulesDirectory -ChildPath $OutputFile
if (Test-Path $outputPath) { Remove-Item -Path $outputPath -Force }
foreach ($file in $yarFiles) {
Add-Content -Path $outputPath -Value "include `".\$($file.Name)`""
}
Write-Host "Wrote $outputPath".\generate_rules.ps1 -RulesDirectory ".\Scanners\Yara\Rules" -OutputFile "LitterBox.yar"
# Then move/rename the output up one level if needed.YARA is invoked with:
| Flag | Effect |
|---|---|
-s |
Print the actual matched strings (not just rule names) |
-m |
Print metadata (severity, description, etc.) — required: the parser reads severity from this output |
Without -m the static analyzer can't read severity → every match defaults to MEDIUM, which throws off scoring.
| Lane | Sees | Misses |
|---|---|---|
| Static (file on disk) | Strings in unpacked code, configuration blobs, hard-coded URLs, embedded shellcode at rest | Anything decrypted or generated at runtime |
| Dynamic (process memory) | Decrypted payload, decoded strings, in-memory shellcode, runtime-patched APIs | Strings in resources never loaded |
Run both. Identical rule, different visibility — many rules fire on one lane but not the other for a given sample, and that's a useful signal in itself.
- Detection Score Explained — exact YARA scoring formula and match-multiplier behavior
-
Configuration Reference —
analysis.static.yaraandanalysis.dynamic.yarablocks - New Scanner — pattern for adding any new analyzer (YARA is one example)
- YARA documentation — rule syntax + meta block reference