diff --git a/CHANGELOG.md b/CHANGELOG.md index 99ee866..f42d45b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,29 @@ GitHub Release; `sync-fanout.yml` then opens the Kali sync PR. ## [Unreleased] +### Added + +- **GCP parity — 2 new red↔blue pairs (+4 entries) and a recon entry (+1).** Brings + GCP up from a single pair to rough parity with the other big-three clouds. + **Persistence** (`T1098`): IAM policy backdoor — `setIamPolicy` binding a rogue + principal — detected on the `SetIamPolicy` `ADD` binding delta in Cloud Audit + Logs. **Defense Evasion** (`T1562.008`): Cloud Audit log tamper — `DeleteSink` / + `auditConfigs` strip — detected via the self-witnessing Admin Activity events + (plus a Data Access gap monitor). Also adds an unpaired **Discovery** + (`T1580`/`T1526`/`T1069.003`) `gcp-enum-recon` entry (projects / Asset Inventory / + IAM blast-radius mapping), mirroring the unpaired on-prem `smb-enum-nxc`. + +### Changed + +- **`asrep-probing-4771` retargeted to the real AS-REP roast artifact.** The + detection now keys primarily on a *successful* `4768` with pre-authentication + type 0 (the AS-REP etype is negotiated — often RC4 `0x17`, AES where RC4 is + disabled — so the clause keys on the type-0 invariant, not the cipher) — the + roastable AS-REP its red mate actually emits — and keeps + the `4771 0x18` one-source-many-accounts burst as a secondary Kerbrute + enumeration/spray tell. Previously it only saw the collateral `4771` probing, not + the roast itself. + ## [v2.3.0] - 2026-07-10 ### Added diff --git a/entries/blue/asrep-probing-4771.md b/entries/blue/asrep-probing-4771.md index 429985c..4924898 100644 --- a/entries/blue/asrep-probing-4771.md +++ b/entries/blue/asrep-probing-4771.md @@ -1,8 +1,8 @@ --- id: asrep-probing-4771 -title: Detect AS-REP / Kerbrute probing (4771 0x18) +title: Detect AS-REP roast (4768 no-preauth) + Kerbrute probing (4771) detection: splunk-spl -event_ids: [4771] +event_ids: [4768, 4771] attack: tactic: TA0006 techniques: [T1558.004] @@ -10,10 +10,29 @@ source: TrustedSec "Actionable Purple Teaming" (BH USA 2023) pair: asreproast-getnpusers --- -One client address generating Kerberos pre-auth failures (`4771`, failure code -`0x18`) across many distinct accounts is the spray/roast tell — a real user -fat-fingers their own name, not five-plus others. Tune the threshold to the -environment. +Detect on the invariant, not the IOC. The roastable AS-REP is a *successful* +`4768` TGT request with **pre-authentication type 0** (no pre-auth) for a +non-machine account — that is the account `GetNPUsers`/`--asreproast` actually +harvests. The AS-REP is issued under the account's long-term key — RC4 (`0x17`) +where it's enabled, AES (`0x11`/`0x12`) in RC4-disabled domains — and cracked +offline (`hashcat -m 18200` for the RC4 case). Unlike Kerberoasting the attacker +doesn't force the etype, so the *invariant* is the type-0 pre-auth on a user, not +the negotiated cipher — key on it directly and don't constrain the encryption +type, or AES-only domains slip through. A normal account pre-auths with type 2 +(encrypted timestamp), so type 0 on a user is the tell. The +one-source-to-many-accounts `4771 0x18` burst is a *secondary* enumeration tell: +it fires on wrong-password pre-auth failures against pre-auth-**required** +accounts (Kerbrute enum / spraying), never on the roast itself — so keep it, but +alert on the `4768` first. + +```spl +index=main EventCode=4768 Pre_Authentication_Type=0 Account_Name!="*$" +| stats count values(Account_Name) AS Accounts by Client_Address +| sort -count +``` + +Secondary tell — Kerbrute enumeration / spray burst (one source, many accounts, +wrong-password pre-auth failures). Tune the threshold to the environment: ```spl index=main EventCode=4771 Failure_Code="0x18" diff --git a/entries/blue/gcp-audit-log-tamper-audit.md b/entries/blue/gcp-audit-log-tamper-audit.md new file mode 100644 index 0000000..a786d29 --- /dev/null +++ b/entries/blue/gcp-audit-log-tamper-audit.md @@ -0,0 +1,30 @@ +--- +id: gcp-audit-log-tamper-audit +title: Detect audit-log tamper (GCP, DeleteSink / auditConfig strip) +detection: gcp-logging +event_ids: [] +attack: + tactic: TA0005 + techniques: [T1562.008] +source: GCP logging abuse (blind Data Access telemetry) +pair: gcp-audit-log-disable +--- + +The tamper is self-witnessing: sink and auditConfig changes are Admin Activity +events, which are always-on and immutable, so the act of going dark is itself +logged. Alert on `DeleteSink`/`UpdateSink`, and on any `SetIamPolicy` that removes +or narrows `auditConfigs` (a `REMOVE` delta on the audit config). Pair it with a +gap monitor — an unexpected drop-to-zero in a project's Data Access log volume is +the corroborating signal when the config change slips through. + +GCP Cloud Audit Logs telemetry (native Cloud Logging filter below; also queryable +as Sentinel `GCPAuditLogs`), companion-only — `PURPLE-TEAM.md` is on-prem Windows. +Triage by `protoPayload.authenticationInfo.principalEmail` (the actor) and +`protoPayload.resourceName` (the sink or project touched). + +```text +logName=~"cloudaudit.googleapis.com%2Factivity" +(protoPayload.methodName=~"DeleteSink$" OR protoPayload.methodName=~"UpdateSink$" + OR (protoPayload.methodName=~"SetIamPolicy$" + AND protoPayload.serviceData.policyDelta.auditConfigDeltas.action="REMOVE")) +``` diff --git a/entries/blue/gcp-iam-policy-audit.md b/entries/blue/gcp-iam-policy-audit.md new file mode 100644 index 0000000..a2b6a6e --- /dev/null +++ b/entries/blue/gcp-iam-policy-audit.md @@ -0,0 +1,30 @@ +--- +id: gcp-iam-policy-audit +title: Detect IAM policy backdoor (GCP audit, SetIamPolicy binding ADD) +detection: gcp-logging +event_ids: [] +attack: + tactic: TA0003 + techniques: [T1098] +source: GCP IAM abuse (resource IAM policy binding persistence) +pair: gcp-iam-policy-backdoor +--- + +Every IAM grant lands in Cloud Audit Logs (Admin Activity) as a `SetIamPolicy` +call whose `serviceData.policyDelta.bindingDeltas` carries the `ADD` action, the +role, and the member. Alert on additions of sensitive roles +(`roles/owner`, `roles/editor`, `*Admin`) or grants to unexpected/external +principals — and treat a binding whose member is `allUsers`/`allAuthenticatedUsers` +as an immediate, standalone finding. + +GCP Cloud Audit Logs telemetry (native Cloud Logging filter below; also queryable +as Sentinel `GCPAuditLogs`), companion-only — `PURPLE-TEAM.md` is on-prem Windows. +Triage each hit by `protoPayload.authenticationInfo.principalEmail` (the actor) +and the added `member`/`role`. + +```text +logName=~"cloudaudit.googleapis.com%2Factivity" +protoPayload.methodName=~"SetIamPolicy$" +protoPayload.serviceData.policyDelta.bindingDeltas.action="ADD" +protoPayload.serviceData.policyDelta.bindingDeltas.role=("roles/owner" OR "roles/editor" OR "roles/resourcemanager.projectIamAdmin") +``` diff --git a/entries/red/gcp-audit-log-disable.md b/entries/red/gcp-audit-log-disable.md new file mode 100644 index 0000000..6deb090 --- /dev/null +++ b/entries/red/gcp-audit-log-disable.md @@ -0,0 +1,24 @@ +--- +id: gcp-audit-log-disable +title: GCP Cloud Audit log tamper (delete sink / strip auditConfigs) +section: GCP / cloud IAM +phase: Defense Evasion +attack: + tactic: TA0005 + techniques: [T1562.008] +platform: [cloud] +source: GCP logging abuse (blind Data Access telemetry) +pair: gcp-audit-log-tamper-audit +--- + +Blind the defenders before the noisy work. Delete or redirect the log sink that +exports to the SIEM, or strip the project's `auditConfigs` so Data Access logs +stop flowing — either way the analytics that key on those events go dark. Note +the ceiling: Admin Activity logging cannot be disabled and records the tamper +itself, so this buys time, not silence. +(Cloud — no slots.) + +```sh +gcloud logging sinks delete --project= +gcloud projects set-iam-policy policy-no-auditconfigs.json +``` diff --git a/entries/red/gcp-enum-recon.md b/entries/red/gcp-enum-recon.md new file mode 100644 index 0000000..faed60b --- /dev/null +++ b/entries/red/gcp-enum-recon.md @@ -0,0 +1,26 @@ +--- +id: gcp-enum-recon +title: GCP enumeration (projects, assets, IAM — recon) +section: GCP / cloud IAM +phase: Discovery +attack: + tactic: TA0007 + techniques: [T1580, T1526, T1069.003] +platform: [cloud] +source: GCP post-access recon (blast-radius mapping) +pair: null +--- + +Once a token or service-account key lands, map the blast radius before touching +anything. List every reachable project, sweep all resources with the Asset +Inventory API, then read the IAM policy to learn who holds what — the shortest +path from "a credential" to "the accounts worth backdooring." Read-only and +low-signal on its own (Data Access telemetry, often unmonitored), which is why it +ships unpaired. +(Pure recon — no paired blue detection.) + +```sh +gcloud projects list +gcloud asset search-all-resources --scope=projects/ +gcloud projects get-iam-policy --format=json +``` diff --git a/entries/red/gcp-iam-policy-backdoor.md b/entries/red/gcp-iam-policy-backdoor.md new file mode 100644 index 0000000..0318bb5 --- /dev/null +++ b/entries/red/gcp-iam-policy-backdoor.md @@ -0,0 +1,26 @@ +--- +id: gcp-iam-policy-backdoor +title: GCP IAM policy backdoor (setIamPolicy → rogue principal) +section: GCP / cloud IAM +phase: Persistence +attack: + tactic: TA0003 + techniques: [T1098] +platform: [cloud] +source: GCP IAM abuse (resource IAM policy binding persistence) +pair: gcp-iam-policy-audit +--- + +With `resourcemanager.projects.setIamPolicy` (any Owner/`*.admin` role), bind an +attacker-controlled principal straight into the project's IAM policy — a rogue +service account, a personal Google identity, or `allUsers`. Granting +`roles/owner` (or the quieter `roles/resourcemanager.projectIamAdmin`) is durable, +survives SA-key rotation, and blends into normal IAM churn. Prefer an existing +low-noise SA as the member so the grant looks routine. +(Cloud — no slots.) + +```sh +gcloud projects add-iam-policy-binding \ + --member='serviceAccount:@.iam.gserviceaccount.com' \ + --role='roles/owner' +```