Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 25 additions & 6 deletions entries/blue/asrep-probing-4771.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
---
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]
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"
Expand Down
30 changes: 30 additions & 0 deletions entries/blue/gcp-audit-log-tamper-audit.md
Original file line number Diff line number Diff line change
@@ -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"))
```
30 changes: 30 additions & 0 deletions entries/blue/gcp-iam-policy-audit.md
Original file line number Diff line number Diff line change
@@ -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")
```
24 changes: 24 additions & 0 deletions entries/red/gcp-audit-log-disable.md
Original file line number Diff line number Diff line change
@@ -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 <sink> --project=<project>
gcloud projects set-iam-policy <project> policy-no-auditconfigs.json
```
26 changes: 26 additions & 0 deletions entries/red/gcp-enum-recon.md
Original file line number Diff line number Diff line change
@@ -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/<project>
gcloud projects get-iam-policy <project> --format=json
```
26 changes: 26 additions & 0 deletions entries/red/gcp-iam-policy-backdoor.md
Original file line number Diff line number Diff line change
@@ -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 <project> \
--member='serviceAccount:<attacker-sa>@<project>.iam.gserviceaccount.com' \
--role='roles/owner'
```