Install
$ agentstack add skill-evilfreelancer-secs-writing-yara-rules ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Writing YARA Rules
A good YARA rule matches the malware's nature — its code, its structure, its unavoidable constants — not its costume, which is a filename or a mutable string that changes on the next build. Match the nature and the rule survives recompilation and catches the whole family; match the costume and you catch one sample once. The entire craft is choosing strings and a condition that are both specific enough to avoid false positives and durable enough to generalize.
When to Use
- Writing a YARA rule to detect a malware family, tool, or capability
- Creating a signature for a file or in-memory artifact from a known sample
- Turning IOCs or a captured specimen into a portable, testable detection
- Hunting for a family across a corpus (VirusTotal Retrohunt, LOKI/THOR, on-host scan)
- Clustering related samples by shared code, constants, or structure
- Reviewing or tuning an existing rule for false positives and scan performance
When NOT to Use
- The detection belongs in log or SIEM telemetry, not on files or memory —
use writing-sigma-rules
- **Choosing what to detect and where it should live, the strategy above rule
syntax** — use engineering-detections
- You do not yet understand the sample well enough to pick durable anchors —
use analyzing-malware first
- Running the hunt rather than authoring the signature — use
hunting-threats - Packaging IOCs, attribution, and a finished report — use
producing-threat-intelligence
Rule Anatomy
Three sections: meta (documentation, never matched on), strings (the patterns), condition (the boolean that decides a hit).
import "pe"
rule Family_Loader_v1
{
meta:
author = "analyst"
date = "2026-07-26"
description = "ExampleLoader stage-1, config-decode stub + family constants"
hash = "a1b2c3..."
reference = "https://internal/case/1234"
version = "1"
tlp = "amber"
strings:
$decode = { 8A 04 0? 32 0? 88 0? 4? 3B ?? 72 }
$marker = "cfg::begin" ascii
$mutex = "Global\\ExL-%08x" ascii
condition:
uint16(0) == 0x5A4D and filesize = 3 and // the $beacon string appears 3+ times
// Bound the search — a 40-byte dropper is not a 50 MB installer
filesize " and // API import fingerprint
pe.number_of_sections == 5 and
pe.imports("wininet.dll", "InternetOpenA") and
pe.exports("PluginInit") and
for any s in pe.sections : ( s.name == ".xdata" and s.raw_data_size == 0 ) and
pe.rich_signature.length > 0 and // Rich header (compiler fingerprint)
pe.timestamp > 1577836800
}
imphash clusters samples built from the same import table; the Rich header fingerprints the build toolchain and is hard to forge; pe.imports/pe.exports anchor on capability rather than incidental bytes.
ELF mirrors PE for Linux (elf.number_of_sections, elf.type, elf.symtab). math scores regions — flag packed or encrypted blobs:
import "math"
rule Packed_HighEntropy {
condition:
uint16(0) == 0x5A4D and
math.entropy(0x1000, filesize - 0x1000) >= 7.2 and // near-random tail
math.mean(0, filesize) > 100
}
hash computes digests inside the condition — useful to pin a specific embedded resource, not the whole file (a whole-file hash needs no YARA):
import "hash"
rule EmbeddedResource {
condition:
hash.sha256(0x400, 0x200) == "e3b0c4..."
}
Targeting Position-Independent Things
Anchor on what moves with the code, not with the campaign. In rough order of durability: crypto constants (S-boxes, MD5/SHA init values, custom key schedules) → the family's own mutex/pipe/registry name templates → config markers and delimiters → C2 URI templates (/gate/, /panel/upload.php) → unique error and debug strings the developer left in → stack strings once you have deobfuscated them with floss. A filename, a compile timestamp, a campaign C2 IP, and a single common Win32 API name are not durable — they are the costume.
Killing False Positives
A rule is not done when it hits the sample; it is done when it does not hit goodware. Three levers, applied together:
- Quorum — require
N of Mstrings, never a lone common one. Any single
string can appear in benign software; three family constants together will not.
- File-type anchor — lead the condition with
uint16(0) == 0x5A4Dor the
ELF magic so the rule never even scans an unrelated file type.
- Size bound — `filesize " # scheme in the path is optional
This strips page boilerplate — roughly 78% fewer tokens on a prose page — and
returns the full text rather than a summary, so you can grep it and trust a
negative result.
Three things it is not for. Fetch JSON and API responses raw, because
readability extraction mangles structured data. Fetch authenticated or
JavaScript-rendered pages directly, because it retrieves them anonymously. And
never route **adversary infrastructure** (phishing links, C2, malware hosting),
**client-owned hosts**, or **engagement URLs** through it — the request leaves
your machine to a third party, and for live adversary infrastructure it also
tips off the operator.
Some sites block the extractor and return an error blob rather than the page —
`{"error":"Failed to fetch: 418 I'm a teapot"}` from freedesktop.org, for
instance. That is the fetch being refused, **not** the source saying the thing
does not exist. Re-fetch the URL directly before drawing any conclusion from
it.
## References
- `writing-sigma-rules` — the same craft for log and SIEM telemetry
- `engineering-detections` — choosing what to detect and where it belongs
- `analyzing-malware` — understanding the sample that a durable rule is built from
- `hunting-threats` — running the hunt these signatures drive
- `producing-threat-intelligence` — packaging IOCs, attribution, and reporting
- `reporting-security-findings` — writing up what the detection found
- YARA (VirusTotal) and YARA-X — the engines and their documentation
- yarGen — auto-generate candidate rules and strings from samples (a starting
point, never a finished rule)
- yara-validator / `yr fmt` — lint and normalize rules in CI
- LOKI and THOR (Nextron) — IOC and YARA scanners for on-host hunting
- VirusTotal Retrohunt — run a rule across VT's corpus to find related samples
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [EvilFreelancer](https://github.com/EvilFreelancer)
- **Source:** [EvilFreelancer/secs](https://github.com/EvilFreelancer/secs)
- **License:** Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.