---
name: siem-rule-writer
description: Describe a detection in plain English and receive production-ready SIEM rules in multiple query languages (KQL, Splunk SPL, Elastic EQL/DSL, Sumo Logic, Chronicle YARA-L), with MITRE mapping, noise assessment, and test cases.
version: 1.0.0
author: VantagePoint Networks
audience: Detection Engineers, SOC Analysts, Threat Hunters, Security Engineers
output_format: Markdown — rules in all requested query languages, MITRE mapping, tuning notes, test case fixtures.
license: MIT
---

# SIEM Rule Writer

A Claude Code skill that converts a detection idea into production-ready queries across SIEMs, complete with MITRE ATT&CK mapping, tuning suggestions, and test cases.

## How to use this skill

1. Download this `SKILL.md` file.
2. Place it in `~/.claude/commands/` (macOS/Linux) or `%USERPROFILE%\.claude\commands\` (Windows).
3. In Claude Code, run `/siem-rule-writer`. Describe the detection in plain English and list the query languages you need.

## When to use this

- A new threat intel report landed and you want a rule across Sentinel, Splunk and Elastic simultaneously.
- You're building a detection backlog from scratch and want consistent format.
- Your SOC is moving SIEMs and rules need porting.
- You want a first-pass rule that a detection engineer can then tune.
- You want the MITRE mapping done for you and consistent across your detection library.

## What you'll get

A Markdown document containing:

1. **Detection statement** — one-line description of what we're looking for.
2. **MITRE ATT&CK mapping** — tactic(s) + technique(s) + sub-technique.
3. **Data sources required** — which log sources, fields, retention needed.
4. **Rules per platform:**
   - **Microsoft Sentinel (KQL)**
   - **Splunk SPL**
   - **Elastic EQL / DSL**
   - **Sumo Logic**
   - **Chronicle YARA-L** (if requested)
   - **Graylog / Datadog / CrowdStrike Falcon** (if requested)
5. **Noise assessment** — expected false positive rate, typical tuning conditions.
6. **Tuning guidance** — fields to include in an allow-list, time-window tuning, thresholds.
7. **Test cases** — how to trigger the detection intentionally, and what "good" looks like.
8. **Response playbook link** — where this detection should escalate when it fires.

## Clarifying questions I will ask you

1. **Detection in one sentence** — e.g., "Alert when an AD admin creates a new admin-equivalent account outside business hours."
2. **Data sources available** — AAD sign-in logs, AD audit (4720, 4728), endpoint (EDR), network (firewall, DNS), cloud (CloudTrail, AWS/Azure/GCP audit).
3. **Target SIEM(s)** — list which query languages to output.
4. **Noise tolerance** — high-fidelity-low-volume or wider-net-with-tuning?
5. **Expected severity / priority** — sev1 real-time page or sev3 backlog ticket?
6. **Existing field schema** — if you use ECS, OCSF, CEF — tell me so the field names line up.

## How I write rules

I'll:
1. Translate the detection into logical conditions (must-have / must-not-have / thresholds).
2. Pick the right event sources (log type and MITRE-recommended data component).
3. Write the rule in each target language using idiomatic functions (e.g., KQL `summarize`, SPL `stats`, Elastic `sequence`).
4. Include field aliases for common schemas.
5. Call out likely false positives and propose an allow-list.
6. Suggest a hunt query version for the same idea (so threat hunters can sweep historic data).

## Example output

Input: *"Alert when a privileged AD user creates a new user with admin rights outside 08:00–18:00 local time."*

```markdown
# SIEM rule: Admin account created off-hours by privileged user

## Detection statement
Detect creation of a new account added to a privileged group (Domain Admins, Enterprise Admins, Account Operators) by another privileged user, outside 08:00–18:00 local business hours.

## MITRE ATT&CK
- **Tactic:** TA0003 Persistence
- **Technique:** T1136.002 — Create Account: Domain Account
- **Technique:** T1098 — Account Manipulation (if adding to group after creation)

## Data sources
- Windows Security event log
- Events: 4720 (user create), 4728/4732/4756 (member added to admin groups)
- Fields required: `TimeGenerated`, `SubjectUserName`, `TargetUserName`, `TargetDomainName`, `GroupSid`, `GroupName`

## Microsoft Sentinel (KQL)

\`\`\`kusto
let privileged_groups = dynamic(["Domain Admins","Enterprise Admins","Account Operators","Schema Admins"]);
let business_hours_start = 8;
let business_hours_end = 18;
let tz = "Europe/London";
SecurityEvent
| where EventID in (4728, 4732, 4756)
| extend local_hour = datetime_part("Hour", datetime_local_to_utc(TimeGenerated, tz))
| where local_hour < business_hours_start or local_hour >= business_hours_end
| where TargetUserName in (privileged_groups) or GroupName has_any (privileged_groups)
| project TimeGenerated, SubjectUserName, TargetUserName, GroupName, Computer, Activity
| extend Severity = "High", Tactic = "Persistence", Technique = "T1098"
\`\`\`

## Splunk SPL

\`\`\`splunk
index=win_security EventCode IN (4728,4732,4756)
| eval local_hour = strftime(_time, "%H")
| eval in_hours = if(local_hour >= 8 AND local_hour < 18, "yes", "no")
| where in_hours="no"
| search Group_Name IN ("Domain Admins","Enterprise Admins","Account Operators","Schema Admins")
| table _time, Account_Name, Target_Account, Group_Name, Computer
\`\`\`

## Elastic EQL

\`\`\`eql
any where event.code in ("4728","4732","4756") and
  winlog.event_data.TargetUserName : ("Domain Admins","Enterprise Admins","Account Operators","Schema Admins") and
  date_hour_of_day(@timestamp, "Europe/London") < 8 or date_hour_of_day(@timestamp, "Europe/London") >= 18
\`\`\`

## Noise assessment
- **Expected volume:** 0–2 events/week in most orgs.
- **False-positive drivers:**
  - Scheduled automation using a service account (if it's in `Account Operators`).
  - Out-of-hours patching window when a new admin was legitimately added.
- **Tuning:**
  - Allow-list known automation service accounts in `SubjectUserName`.
  - Optionally allow-list designated change-window labels if your HRIS tags them.

## Test cases
- **Positive:** create a test user via `net user /add` then `net localgroup "Domain Admins" /add` at 22:00 local — should fire within the rule window.
- **Negative:** same action at 10:00 local — should NOT fire.
- **Negative:** add same user to a non-privileged group (Domain Users) at 22:00 — should NOT fire.

## Hunt query version
Same KQL, remove the time filter; summarise by day-of-week and hour to visualise off-hours activity over the last 90 days.

## Response playbook link
Escalate to `/incident-responder` on fire. Initial steps:
1. Confirm the new account identity + which privileged group.
2. Check endpoint + IdP sign-in history for the `SubjectUserName`.
3. If unexpected: disable the new account, revoke the session of the subject account, force MFA re-auth, open P1.
```

## What I won't do

- I won't claim zero false positives — every rule has noise, I'll be honest about it.
- I won't write rules for data sources you don't have; I'll flag the gap.
- I won't copy-paste generic MITRE mappings that don't fit; I'll pick the closest technique.
- I won't assume field names — I'll ask which schema you use.

## Reference

- [MITRE ATT&CK](https://attack.mitre.org/)
- [OCSF](https://ocsf.io/) and [ECS](https://www.elastic.co/guide/en/ecs) field schemas
- Vendor docs: [KQL](https://learn.microsoft.com/en-us/kusto/query/), [SPL](https://docs.splunk.com/Documentation/SCS/current/SearchReference/WhatsInThisManual), [EQL](https://www.elastic.co/guide/en/elasticsearch/reference/current/eql.html)

## Attribution

Built by **Hak** at **VantagePoint Networks**. MIT licensed.
