SkillAgentSearch skills...

yara-rule-authoring

Guides authoring of high-quality YARA-X detection rules for malware identification

Install / Use

npx skills add trailofbits/skills --skill yara-rule-authoring

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

84/100

Supported Platforms

Universal

Our assessment of yara-rule-authoring

yara-rule-authoring scores 84/100 on our quality scale, 300th of 504 Content & Media skills we index.

Its SKILL.md is 23 KB long, well organised into 30 sections with 10 code examples: a thorough specification that gives an agent plenty to work with.

With 7,225 GitHub stars, it is one of the more widely adopted skills in the catalogue.

Substance
30/30
Structure
20/20
Description
12/15
Adoption
16/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 3 days ago, so yara-rule-authoring is actively maintained.
  • It is released under the CC-BY-SA-4.0 license; check its terms before commercial use.
  • Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

yara-rule-authoring compared with similar skills

All 4 of these similar skills score higher than yara-rule-authoring; compare them before choosing.

SkillScoreStarsUpdatedFormat
yara-rule-authoring (this skill)by trailofbits847.2k3d agoSKILL.md
siyuanby siyuan-note10046.5ktodayMCP Server
algorithmic-artby anthropics100177.9k4d agoSKILL.md
pptxby anthropics100177.9k4d agoSKILL.md
designby nextlevelbuilder100130.2k5d agoSKILL.md

Frequently asked questions

How do I install yara-rule-authoring?
Run npx skills add trailofbits/skills --skill yara-rule-authoring. The install tabs above show the steps for each supported agent.
Which AI agents does yara-rule-authoring work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is yara-rule-authoring safe to use?
It is CC-BY-SA-4.0-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is yara-rule-authoring still maintained?
The repository was last updated 3 days ago, so yara-rule-authoring is actively maintained.

name: yara-rule-authoring description: > Guides authoring of high-quality YARA-X detection rules for malware identification. Use when writing, reviewing, or optimizing YARA rules. Covers naming conventions, string selection, performance optimization, migration from legacy YARA, and false positive reduction. Triggers on: YARA, YARA-X, malware detection, threat hunting, IOC, signature, crx module, dex module.

YARA-X Rule Authoring

Write detection rules that catch malware without drowning in false positives.

This skill targets YARA-X, the Rust-based successor to legacy YARA — 5-10x faster regex, better errors, built-in formatter, stricter validation, new modules (crx, dex), 99% rule compatibility. It powers VirusTotal's production systems. Install with brew install yara-x or cargo install yara-x; the CLI is yr. See Migrating from Legacy YARA for existing rules.

Core Principles

  1. Strings must generate good atoms — YARA extracts 4-byte subsequences for fast matching. Strings with repeated bytes, common sequences, or under 4 bytes force slow bytecode verification on too many files.

  2. Target specific families, not categories — "Detects ransomware" catches everything and nothing. "Detects LockBit 3.0 configuration extraction routine" catches what you want.

  3. Test against goodware before deployment — A rule that fires on Windows system files is useless. Validate against VirusTotal's goodware corpus or your own clean file set.

  4. Short-circuit with cheap checks first — filesize (instant), then magic bytes (nearly instant), then strings (cheap), then modules (expensive).

  5. Metadata is documentation — Future you (and your team) need to know what this catches, why, and where the sample came from.

When to Use

  • Writing new YARA-X rules for malware detection
  • Reviewing existing rules for quality or performance issues
  • Optimizing slow-running rulesets
  • Converting IOCs or threat intel into detection signatures
  • Debugging false positive issues
  • Preparing rules for production deployment
  • Migrating legacy YARA rules to YARA-X
  • Analyzing Chrome extensions (crx module) or Android apps (dex module)

When NOT to Use

  • Static analysis requiring disassembly → use Ghidra/IDA skills
  • Dynamic malware analysis → use sandbox analysis skills
  • Network-based detection → use Suricata/Snort skills
  • Memory forensics with Volatility → use memory forensics skills
  • Simple hash-based detection → just use hash lists

Platform Considerations

YARA works on any file type. Adapt patterns to your target:

| Platform | Magic Bytes | Bad Strings | Good Strings | |----------|-------------|-------------|--------------| | Windows PE | uint16(0) == 0x5A4D | API names, Windows paths | Mutex names, PDB paths | | macOS Mach-O | uint32(0) == 0xFEEDFACE (32-bit), 0xFEEDFACF (64-bit), uint32be(0) == 0xCAFEBABE (universal) | Common Obj-C methods | Keylogger strings, persistence paths | | JavaScript/Node | (none needed) | require, fetch, axios | Obfuscator signatures, eval+decode chains | | npm/pip packages | (none needed) | postinstall, dependencies | Suspicious package names, exfil URLs | | Office docs | uint32(0) == 0x04034B50 | VBA keywords | Macro auto-exec, encoded payloads | | VS Code extensions | (none needed) | vscode.workspace | Uncommon activationEvents, hidden file access | | Chrome extensions | Use crx module | Common Chrome APIs | Permission abuse, manifest anomalies | | Android apps | Use dex module | Standard DEX structure | Obfuscated classes, suspicious permissions |

uintNN() reads little-endian. Write the constant as the bytes reversed, or use uintNNbe() and write them in file order. A ZIP/OOXML file starts with bytes 50 4B 03 04, so it is uint32(0) == 0x04034B50 — uint32(0) == 0x504B0304 compiles cleanly and never matches anything. The same trap catches Mach-O universal binaries: on disk they are CA FE BA BE, so uint32(0) == 0xCAFEBABE is a dead branch; write uint32be(0) == 0xCAFEBABE or uint32(0) == 0xBEBAFECA. Verify with yr scan against one known-good sample before trusting any magic-byte check.

macOS Malware Detection

No dedicated Mach-O module exists yet — use magic bytes plus string patterns. Good indicators:

  • Keylogger artifacts: CGEventTapCreate, kCGEventKeyDown
  • SSH tunnel strings: ssh -D, tunnel, socks
  • Persistence paths: ~/Library/LaunchAgents, /Library/LaunchDaemons
  • Credential theft: security find-generic-password, keychain
// Pattern from Airbnb BinaryAlert
rule SUSP_Mac_ProtonRAT
{
    strings:
        $lib1 = "SRWebSocket" ascii          // Library indicators
        $lib2 = "SocketRocket" ascii
        $behav1 = "SSH tunnel not launched" ascii   // Behavioral indicators
        $behav2 = "Keylogger" ascii
    condition:
        (uint32(0) == 0xFEEDFACF or uint32be(0) == 0xCAFEBABE) and
        any of ($lib*) and any of ($behav*)
}

JavaScript Detection

| Target | Approach | |---|---| | npm package | package.json patterns, postinstall/preinstall hooks, exfil combination: fetch + env access + credential paths | | Chrome extension | crx module | | Other extension | Manifest patterns, background script behaviors | | Standalone JS | Obfuscation markers (eval+atob, fromCharCode chains), unique function/variable names, packed payloads | | Minified/webpack bundle | Unique strings that survive bundling (URLs, magic values); avoid function names — they get mangled |

Good JS strings: Ethereum function selectors — { a9 05 9c bb } (transfer(address,uint256)), { 70 a0 82 31 } (balanceOf(address)); zero-width characters for steganography — { E2 80 8B E2 80 8C }; obfuscator signatures — _0x, var _0x; specific C2 domains and webhook URLs.

Bad JS strings: require, fetch, axios (too common); Buffer, crypto (legitimate uses everywhere); process.env alone (need specific env var names).

String Selection

Value ranking: mutex names are gold, C2 paths silver, error messages bronze. Stack strings are almost always unique. If you need more than 6 strings, you're over-fitting.

Reject a candidate string when any of these holds:

| Test | Why it fails | Do instead | |---|---|---| | Under 4 bytes | No atom | Find a longer string | | Repeated bytes (0000, 9090) | Weak atom | Add surrounding context | | API name (VirtualAlloc, CreateRemoteThread) | Every packer and installer calls it | Hex pattern of the call site plus a unique marker | | Appears in Windows system files | Guaranteed FPs | Find something family-specific | | Common path (C:\Windows\, cmd.exe) | Ubiquitous | Find malware-specific paths | | Appears in other malware families | Not identifying this family | Combine with a family-specific marker |

Everything left — unique to this family — is what the rule should rest on.

Choosing a String Type

| Need | Use | |---|---| | Exact ASCII/Unicode text | $s = "MutexName" ascii wide | | Specific byte sequence | $h = { 4D 5A 90 00 } | | Byte sequence with variation | Hex wildcards: { 4D 5A ?? ?? 50 45 } | | Pattern with structure (URLs, paths) | Bounded regex: /https:\/\/[a-z]{5,20}\.onion/ | | Unknown encoding (XOR, base64) | Modifier: $s = "config" xor(0x00-0xFF) |

Modifier discipline: never use nocase or wide speculatively — only with confirmed evidence that case or encoding varies across samples. nocase doubles atom generation; wide doubles string matching. "If you don't have a clear reason for using those modifiers, don't do it" — Kaspersky Applied YARA.

Condition Design

Order for short-circuit: filesize <, magic bytes, strings, modules. If the condition runs past 5 lines, split into multiple rules.

all of vs any of

| Situation | Use | |---|---| | Strings are individually unique to the malware | any of them — each alone is suspicious | | Strings are common but the combination is suspicious | all of them — require the full pattern | | Strings have different confidence levels | Group: all of ($core_*) and any of ($variant_*) | | Seeing false positives | Tighten: any → all, add more required strings |

Lesson from production: rules using any of ($network_*) where the strings included fetch, axios, and http matched virtually all web applications. Switching to require a credential path AND a network call AND an exfil destination eliminated the FPs.

Grouping by Confidence

Different indicator types carry different weight — a C2 domain might be definitive while library imports need corroboration. Grouping by prefix lets you express graduated requirements:

strings:
    $a1 = "SRWebSocket" ascii            // Category A: library indicators
    $a2 = "SocketRocket" ascii
    $b1 = "SSH tunnel" ascii             // Category B: behavioral
    $b2 = "keylogger" ascii nocase
    $c1 = /https:\/\/[a-z0-9]{8,16}\.onion/   // Category C: C2

condition:
    filesize < 10MB and
    any of ($a*) and any of ($b*)        // Evidence from BOTH categories

Modules vs Byte Checks

| Need | Use | |---|---| | imphash, rich header, authenticode | PE module — too complex to replicate | | Magic bytes or simple offsets | uint16/uint32 — faster, no module overhead | | Section names/sizes | PE module, but put the magic-byte filter FIRST | | Chrome extension permissions | crx module — string parsing is fragile | | LNK target paths | lnk module — the format is complex |

"Avoid the magic module — use explicit hex checks instead" — Neo23x0. Generalize it: if uint32() can do the job, don't load a module.

Performance

  • Regex must be anchored to a 4+ byte literal. Without one it evaluates at every file offset — catastrophic. Write /mshta\.exe http:\/\/.../, not /http:\/\/.../. If you can't anchor, use a hex pattern with wildcards.
  • Bound every regex quantifier — .{0,30}, never .*. Unbounded regex is both a performance disaster and a memory explosion.
  • Bound loops with filesize — filesize < 100KB and for all i in (1..#a) : .... Unbounded #a can reach thousands in large files.
  • Prefer hex over regex where the bytes are fixed.

Before Writing: Is the Sample Packed?

| Signal | What to do | |---|---| | Entropy > 7.0 | Likely packed — find the unpacked layer first | | Few or no readable strings | Likely packed — use entropy, PE structure, or packer signatures | | UPX/MPRESS/custom packer detected | Target the unpacked payload OR detect the packer itself | | Readable strings available | Proceed with string-based detection |

Don't write rules against packed layers. The packing changes; the payload doesn't.

When Strings Fail, Pivot to Structure

If extraction returns only API names and generic paths:

| Available signal | Use | |---|---| | High entropy sections | math.entropy() on specific sections | | Unusual import pattern | pe.imphash() for import-hash clustering | | PE structure anomalies | Section names, sizes, characteristics | | Metadata present | Version info, timestamps, resources | | Nothing unique | This sample may not be detectable with YARA alone |

"One can try to use other file properties, such as metadata, entropy, import hashes or other data which stays constant." — Kaspersky Applied YARA Training

Debugging False Positives

  1. Which string matched? — yr scan -s rule.yar false_positive.exe
  2. In a legitimate library? — add a not $fp_vendor_string exclusion
  3. A common development pattern? — replace the string with something more specific
  4. Multiple generic strings matching together? — tighten to require all, plus a unique marker
  5. Malware using a common technique? — target its specific implementation details, not the technique

When to Abandon the Approach

  • Extraction returns only API names and paths → [pivot to structure](#when-strings-fail-p

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars7.2k
CategoryContent
Updated3d ago
Forks615

Languages

Python

Trust signals

100/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

No cautions