Writing YARA rules for endpoint detection and Snort/Suricata rules for network detection — rule structure, string modifiers, condition logic, and how to test and validate both rule types against real samples.
Sign in to mark this article as read and track your progress.
After analysing a malware sample, the analyst produces defensive signatures that security tools can deploy immediately. Two signature types cover the two detection domains:
Together they provide defence in depth: YARA stops the file if it reaches a host, Snort/Suricata blocks the C2 communication at the network perimeter.
YARA is a pattern-matching tool for files and memory. A YARA rule has three sections: metadata, strings, and condition.
rule Detect_Dropper_C2 {
meta:
description = "Detects dropper with hardcoded C2"
author = "Ashish Revar, SITAICS"
date = "2026-05-12"
hash = "abc123..."
strings:
$mz = { 4D 5A } // PE magic bytes
$cmd = "cmd.exe /c" ascii nocase
$url = "http://evil.com/gate.php" wide ascii
$ua = "Mozilla/5.0 (compatible)" ascii
$mutex = "Global\\MicrosoftUpdateService3"
condition:
$mz at 0 and 2 of ($cmd, $url, $ua, $mutex)
}
| Type | Syntax | What It Matches |
|---|---|---|
| Plain string | $s = "cmd.exe" | Exact byte sequence |
ascii | $s = "cmd.exe" ascii | ASCII encoding (default) |
wide | $s = "cmd.exe" wide | UTF-16LE encoding (Windows Unicode) |
nocase | $s = "cmd.exe" nocase | Case-insensitive |
fullword | $s = "cmd" fullword | Only standalone word — not "cmdline" |
| Hex bytes | $s = { 4D 5A ?? 00 } | Byte pattern with ?? wildcards |
| Hex with jumps | $s = { 4D 5A [2-4] 50 45 } | Variable-length gap between bytes |
| Regex | $s = /https?:\/\/[0-9.]{7,15}\/gate/ nocase | Regular expression |
condition:
$mz at 0 // PE file (MZ at offset 0)
and filesize < 500KB // reasonable size
and 2 of ($cmd, $url, $ua) // at least 2 of 3 IOC strings
and not $mutex // exclude known false positive
Operators: and, or, not, all of, any of, N of, at, in
Good rule: specific enough to avoid false positives, broad enough to catch variants
// Too specific — fails if one byte changes
$url = "http://185.220.101.47/gate.php"
// Better — matches the gate endpoint pattern across IP variants
$url = /https?:\/\/[0-9.]{7,15}\/gate\.php/
// Too broad — matches almost any Windows binary
$cmd = "cmd.exe"
// Better — matches the specific abuse pattern
$cmd = "cmd.exe /c vssadmin delete shadows" nocase
# Test against a single file
yara rule.yar suspicious.exe
# Test recursively against a directory
yara -r rule.yar malware_samples/
# Verify no false positives against clean files
yara rule.yar C:\Windows\System32\notepad.exe
# Should produce NO output
# Scan memory of a running process
yara -p 3412 rule.yar
Snort and Suricata inspect network traffic in real time, alerting when C2 patterns cross the sensor.
action proto src_ip src_port direction dst_ip dst_port (options)
alert tcp $HOME_NET any -> $EXTERNAL_NET $HTTP_PORTS (
msg:"Malware C2 beacon — custom User-Agent";
flow:established,to_server;
content:"User-Agent: Mozilla/5.0 (compatible)";
content:"POST /gate.php";
sid:1000042;
rev:1;
)
| Option | Purpose | Example |
|---|---|---|
flow | Connection direction and state | flow:established,to_server |
content | Exact byte match | content:"POST /gate.php" |
pcre | Perl-compatible regex | pcre:"/gate\.php\?id=[A-F0-9]{16}/i" |
nocase | Case-insensitive match | content:"user-agent"; nocase |
distance | Next content N bytes after previous | distance:0 |
within | Next content within N bytes of previous | within:50 |
dsize | Match on packet data size | dsize:>100 |
threshold | Alert rate limiting | threshold:type limit, track by_src, count 1, seconds 60 |
alert udp $HOME_NET any -> any 53 (
msg:"Possible DGA domain query — high entropy hostname";
content:"|01 00 00 01 00 00 00 00 00 00|";
pcre:"/[a-z]{12,20}\.(com|net|org|info)\x00/i";
sid:1000043;
rev:1;
)
established — TCP three-way handshake completedto_server — traffic flowing from client to server (outbound from $HOME_NET)This combination matches only genuine C2 POST requests — not SYN packets, not server responses.
Network perimeter:
Snort/Suricata rule → blocks C2 communication
→ Catches malware that reached the host but fires its beacon
Endpoint:
YARA rule → detects the file on disk or in memory
→ Catches malware before or during execution
Together:
If YARA misses a new variant → Snort catches the beacon
If Snort misses encrypted C2 → YARA catches the file
| Source | URL | Content |
|---|---|---|
| YARA-Rules project | github.com/Yara-Rules/rules | Community rules for major families |
| Mandiant/FireEye | github.com/mandiant/red_team_tool_countermeasures | Rules for APT tools |
| Neo23x0 (Florian Roth) | github.com/Neo23x0/signature-base | High-quality community rules |
| CAPE output | Local CAPE instance | Auto-generated rules from extracted configs |
Always test community rules against a clean file corpus before deployment — false positive rates vary significantly.