Skip to content

YARA Rules Management

BlackSnufkin edited this page May 3, 2026 · 4 revisions

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.


Configuration

# 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: 120

Same rules_path for both lanes — keep one rule pack synchronized across modes.


Directory layout

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.


Severity tagging — required for scoring

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.


Adding rules

Option A — drop a new file:

  1. Create Scanners/Yara/Rules/Your_New_File.yar.
  2. 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.


Regenerating LitterBox.yar from the Rules folder

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.

Command flags

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.


Static vs dynamic — what they catch differently

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.


See also

📌 LitterBox · self-hosted payload analysis sandbox

Release


🚀 Getting Started

📊 Pipelines & Pages

🔬 Scanners · 4 modules

🛰️ EDR Integration
🔌 API & Clients
⚙️ Configuration & Dev

Clone this wiki locally