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
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ GitHub Release; `sync-fanout.yml` then opens the Kali sync PR.

### Added

- **Jenkins CI/CD** platform (3 companion-only red↔blue pairs) — the self-hosted
counterpart to the GitHub/GitLab SaaS rounds, detected via the Jenkins Audit Trail
plugin log (`product: jenkins`, keyword/URI matches):
- `jenkins-script-console` ↔ `jenkins-script-console-audit` — Groovy Script Console
RCE + in-memory credential dump; detect `/script` / `/scriptText` (T1059).
- `jenkins-api-token` ↔ `jenkins-api-token-audit` — mint a user API token for durable
non-interactive access; detect `generateNewToken` (T1098).
- `jenkins-job-backdoor` ↔ `jenkins-job-backdoor-audit` — create/reconfigure a job to
run attacker code on the controller + agents; detect `/createItem` / `/job/<name>/configSubmit`
(T1072).

- **Terraform Cloud / IaC** platform (3 companion-only red↔blue pairs) — detections
are Terraform Cloud audit-trail SPL (`product: terraform`, nested `resource.type` /
`resource.action`):
Expand Down Expand Up @@ -77,7 +88,7 @@ GitHub Release; `sync-fanout.yml` then opens the Kali sync PR.
- `gh-deploy-key-backdoor` ↔ `gh-cred-audit` — writable deploy key / fine-grained
PAT for durable access; detect `repo.create_deploy_key` /
`personal_access_token.access_granted` (T1098).
- Corpus is now 50 paired concepts + 1 unpaired recon entry.
- Corpus is now 53 paired concepts + 1 unpaired recon entry.

## [v1.4.0] - 2026-06-30

Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ No mainstream tool ships attacks paired with the telemetry they trip.

## Corpus

50 paired concepts + 1 unpaired recon entry (SMB enum), spanning Credential
53 paired concepts + 1 unpaired recon entry (SMB enum), spanning Credential
Access, Privilege Escalation, Lateral Movement, Persistence, Execution, Defense
Evasion, and Discovery — on-prem AD, a multi-cloud slice (Entra/M365, AWS, GCP),
Kubernetes, Okta, GitHub Actions + GitLab CI/CD, the Harbor container registry,
HashiCorp Vault, and Terraform Cloud:
Kubernetes, Okta, CI/CD (GitHub Actions, GitLab, Jenkins), the Harbor container
registry, HashiCorp Vault, and Terraform Cloud:

| Attack (red) | Detection (blue) | ATT&CK |
| --------------------------------- | ----------------------------------------------------- | --------- |
Expand Down Expand Up @@ -142,6 +142,9 @@ HashiCorp Vault, and Terraform Cloud:
| Rogue agent pool (Terraform) | audit `agent_pool` `create` _(IaC)_ | T1543 |
| Org/team token backdoor (Terraform) | audit `authentication_token` `create` _(IaC)_ | T1098 |
| Variable injection (Terraform) | audit `variable` `create`/`update` _(IaC)_ | T1072 |
| Script Console RCE (Jenkins) | audit `/script`/`/scriptText` request _(CI)_ | T1059 |
| User API token backdoor (Jenkins) | audit `generateNewToken` request _(CI)_ | T1098 |
| Job/pipeline backdoor (Jenkins) | audit `/createItem`/`/job/<name>/configSubmit` request _(CI)_ | T1072 |

Growth is mechanical now that the drift gate exists: author the red+blue entry
pair, mark the matching flat blocks, then `gen-views.sh`. For **on-prem** pairs the
Expand Down
25 changes: 25 additions & 0 deletions entries/blue/jenkins-api-token-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
id: jenkins-api-token-audit
title: Detect API token creation (Jenkins audit log)
detection: jenkins-audit-log
event_ids: []
attack:
tactic: TA0003
techniques: [T1098]
source: Jenkins persistence (user API token)
pair: jenkins-api-token
---

A request to `ApiTokenProperty/generateNewToken` is the invariant. API tokens are minted
rarely, so one generated by an unexpected actor — or against a privileged/service account,
or during an incident — is the persistence tell. Reconcile new tokens against known CI
integrations, prefer short-lived tokens, and alert on generation for accounts that don't
normally hold API tokens.

Jenkins Audit Trail plugin telemetry, companion-only — `PURPLE-TEAM.md` is on-prem
Windows.

```spl
index=jenkins sourcetype=jenkins:audit "ApiTokenProperty/generateNewToken"
| table _time, user, uri
```
25 changes: 25 additions & 0 deletions entries/blue/jenkins-job-backdoor-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
id: jenkins-job-backdoor-audit
title: Detect job create/reconfigure (Jenkins audit log)
detection: jenkins-audit-log
event_ids: []
attack:
tactic: TA0002
techniques: [T1072]
source: Jenkins abuse (malicious job / pipeline)
pair: jenkins-job-backdoor
---

`/createItem` (new job) and `/job/<name>/configSubmit` (reconfigure) are the invariants.
Job changes are routine in active shops, so the signal is one by an unexpected actor, on
a sensitive/privileged job, outside config-as-code (JCasC/pipeline-in-SCM), or immediately
followed by a build. Prefer pipelines defined in version-controlled `Jenkinsfile`s so
config drift is reviewable, and alert on UI-side job edits that bypass that path.

Jenkins Audit Trail plugin telemetry, companion-only — `PURPLE-TEAM.md` is on-prem
Windows.

```spl
index=jenkins sourcetype=jenkins:audit ("/createItem" OR uri="*/job/*/configSubmit")
| table _time, user, uri
```
26 changes: 26 additions & 0 deletions entries/blue/jenkins-script-console-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
id: jenkins-script-console-audit
title: Detect Script Console use (Jenkins audit log)
detection: jenkins-audit-log
event_ids: []
attack:
tactic: TA0002
techniques: [T1059]
source: Jenkins abuse (Groovy Script Console)
pair: jenkins-script-console
---

A request to `/script` or `/scriptText` is the invariant. The Script Console is
admin-only and rarely used legitimately (mostly break-glass), so *any* hit — especially
outside a change window, from a service account, or right before a credential-store read
— is high-signal for controller RCE / credential theft. Alert in real time, scope
`RunScripts`/`Administer` tightly, and pair with process-exec telemetry on the controller
host.

Jenkins Audit Trail plugin telemetry, companion-only — `PURPLE-TEAM.md` is on-prem
Windows.

```spl
index=jenkins sourcetype=jenkins:audit ("/scriptText" OR "/script")
| table _time, user, uri, node
```
26 changes: 26 additions & 0 deletions entries/red/jenkins-api-token.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
id: jenkins-api-token
title: Jenkins user API token (durable non-interactive access)
section: Jenkins / CI/CD
phase: Persistence
attack:
tactic: TA0003
techniques: [T1098]
platform: [jenkins]
source: Jenkins persistence (user API token)
pair: jenkins-api-token-audit
---

Mint an **API token** for a user (your own, or a higher-privileged one you've
compromised): a long-lived, non-interactive credential that drives the Jenkins REST API
and CLI, keeps working after the account's password/session is reset, and blends in among
CI automation. Generating one hits
`.../descriptorByName/jenkins.security.ApiTokenProperty/generateNewToken`, which the
Audit Trail plugin records. (CI controller — no slots.)

```sh
# mint an API token for durable API + CLI access
curl -s -u <user>:<pass> -X POST \
"https://<jenkins>/user/<user>/descriptorByName/jenkins.security.ApiTokenProperty/generateNewToken" \
--data "newTokenName=ci-cache"
```
25 changes: 25 additions & 0 deletions entries/red/jenkins-job-backdoor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
id: jenkins-job-backdoor
title: Jenkins job/pipeline backdoor (run code on controller + agents)
section: Jenkins / CI/CD
phase: Execution
attack:
tactic: TA0002
techniques: [T1072]
platform: [jenkins]
source: Jenkins abuse (malicious job / pipeline)
pair: jenkins-job-backdoor-audit
---

Jenkins is a deployment tool — abuse it to run code across the estate. Create a job (or
reconfigure one) whose build step runs your payload, then let it execute on the
controller or fan out to build **agents** (harvesting their credentials and reaching the
networks they can). A cron/SCM trigger makes it recurring. Creating a job hits
`/createItem`; reconfiguring one hits `/job/<name>/configSubmit` — both logged by the Audit
Trail plugin. (CI controller — no slots.)

```sh
# create a job whose build step runs attacker code (config.xml carries the payload)
curl -s -u <user>:<api-token> -H "Content-Type: application/xml" --data-binary @config.xml \
"https://<jenkins>/createItem?name=ci-cache"
```
25 changes: 25 additions & 0 deletions entries/red/jenkins-script-console.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
id: jenkins-script-console
title: Jenkins Script Console RCE (controller code exec + cred dump)
section: Jenkins / CI/CD
phase: Execution
attack:
tactic: TA0002
techniques: [T1059]
platform: [jenkins]
source: Jenkins abuse (Groovy Script Console)
pair: jenkins-script-console-audit
---

The iconic Jenkins move: with **Overall/Administer** (or `RunScripts`), POST Groovy to
the controller's Script Console and get instant code execution *as the Jenkins process* —
run OS commands, and decrypt every stored credential in memory
(`com.cloudbees.plugins.credentials.SystemCredentialsProvider`) in one request. `/script`
is the form, `/scriptText` the API. Both are logged by the Audit Trail plugin. (CI
controller — no on-host target slot.)

```sh
# run Groovy on the controller (RCE; swap in the credentials-dump one-liner to loot creds)
curl -s -u <user>:<api-token> --data-urlencode 'script=println "id".execute().text' \
"https://<jenkins>/scriptText"
```
Loading