Skip to content

kodeverksted/ws-setup

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitHub Actions Security Workshop — Setup Guide

A self-hostable workshop environment inspired by OWASP Juice Shop, but focused on GitHub Actions attack vectors. Participants exploit intentionally vulnerable workflows to extract tokens and simulate lateral movement across a GitHub organization.


How It Works

Each challenge is a GitHub repository with a deliberately vulnerable Actions workflow. Hidden inside the workflow (via a Docker image pulled at runtime) is a short-lived GitHub App installation token. The token is scoped to a single "flag" repository — participants must extract the token and use it to open an issue there, simulating lateral movement. A scoreboard tracks progress across all challenges.

Tokens are automatically rotated on schedule (and on manual trigger), so a burned token never permanently stops the workshop.


Challenges

See docs/CHALLENGES.md for the full challenge summary.


Repository Structure

ws-setup/
├── README.md                        # This file
├── copilot-instructions.md          # GitHub Copilot context for this project
├── setup.sh                         # Creates all repos, pushes files, sets secrets
├── docs/
│   ├── FACILITATOR.md               # Workshop running order and facilitation notes
│   ├── CHALLENGES.md                # Challenge summary table
│   └── challenges/
│       ├── challenge-01.md          # Vulnerability, exploit, fix, facilitator nudges
│       ├── challenge-02.md
│       ├── challenge-03.md
│       ├── challenge-04.md
│       ├── challenge-05.md
│       ├── challenge-06.md
│       └── challenge-07.md
└── repos/                           # Mirrors target org repo structure
    ├── token-dispenser/             # Token rotation workflows and Dockerfile
    ├── challenge-1/                 # Vulnerable repo files
    ├── challenge-2/
    ├── challenge-3/
    ├── challenge-4/
    ├── challenge-5/
    ├── challenge-6/
    ├── challenge-7/
    ├── flag/                        # Template pushed to all flag-N repos
    └── scoreboard/                  # GitHub Pages scoreboard

Prerequisites


Step 1 — Create the GitHub App

This must be done manually via the GitHub UI. The app is what generates short-lived, scoped tokens for each challenge.

1.1 Create the app

Go to your organization's settings:

https://github.com/organizations/YOUR_ORG/settings/apps/new

Fill in:

  • GitHub App name: anything, e.g. ws-token-distributor
  • Homepage URL: your org URL, e.g. https://github.com/YOUR_ORG
  • Webhooks: uncheck "Active" — not needed
  • Permissions (Repository, not Organization):
    • Issues: Read & Write
    • Contents: Read-only
  • Where can this app be installed: Only on this account

Click Create GitHub App.

1.2 Generate a private key

On the app's settings page, scroll to Private keys and click Generate a private key. A .pem file will be downloaded — keep it safe.

1.3 Note your App ID

At the top of the app settings page you'll see App ID — note this down.

1.4 Install the app on your organization

Go to:

https://github.com/organizations/YOUR_ORG/settings/apps

Click Edit next to your app, then Install App in the left sidebar. Install it on your org and select Only select repositories. You'll come back to add repositories here after creating them.


Step 2 — Run the Setup Script

Clone this repo and run the setup script. It will:

  1. Create all repositories in your org (token-dispenser, challenge-1 through challenge-7, flag-1 through flag-7, scoreboard)
  2. Push the vulnerable workflows and READMEs to each challenge repo
  3. Set the required secrets on token-dispenser
  4. Set required permissions for GitHub packages. (challenge-1-env should only be accessible from challenge-1 etc.)
git clone https://github.com/YOUR_ORG/ws-setup
cd ws-setup

export ORG="your-org-name"
export APP_ID="123456"
export APP_PRIVATE_KEY="$(cat /path/to/your-app.pem)"

bash setup.sh

Note: The script uses gh CLI, so make sure you're authenticated with an account that has org admin access.


Step 3 — Grant the App Repository Access

After the setup script creates all repos, go back to the app installation settings:

https://github.com/organizations/YOUR_ORG/settings/installations/INSTALLATION_ID

Click Configure and under Repository access, add all flag-N repositories and the scoreboard repository:

  • flag-1 through flag-7
  • scoreboard

The flag repos are internal, so the scoreboard workflow must use the GitHub App to generate a read token for them — a plain GITHUB_TOKEN from the scoreboard repo cannot access internal repos in the org. The scoreboard repo is included in the installation so that actions/create-github-app-token can run there.


Step 4 — Trigger the First Token Rotation

Go to token-dispenserActionsRotate Workshop TokensRun workflow.

This generates fresh installation tokens (scoped to their respective flag repos), bakes them into Docker images, and pushes them to GHCR. The challenge workflows pull these images at runtime — participants extract the token from there.

Verify it worked by checking that the packages challenge-env-1 through challenge-env-7 appear under your org's packages tab on GitHub.


Step 5 — Grant Challenge Repos Access to Their Packages

The challenge images are private GHCR packages. Each challenge repo's GITHUB_TOKEN needs explicit read access to pull its image. This must be done manually after the first rotation (the packages don't exist before that).

For each challenge, navigate to the package settings and add the corresponding repo under Manage Actions access:

Package settings URL Repo to add
https://github.com/orgs/YOUR_ORG/packages/container/challenge-env-1/settings challenge-1
https://github.com/orgs/YOUR_ORG/packages/container/challenge-env-2/settings challenge-2
https://github.com/orgs/YOUR_ORG/packages/container/challenge-env-3/settings challenge-3
https://github.com/orgs/YOUR_ORG/packages/container/challenge-env-4/settings challenge-4
https://github.com/orgs/YOUR_ORG/packages/container/challenge-env-5/settings challenge-5
https://github.com/orgs/YOUR_ORG/packages/container/challenge-env-6/settings challenge-6
https://github.com/orgs/YOUR_ORG/packages/container/challenge-env-7/settings challenge-7

This only needs to be done once. The access persists across token rotations since the package name stays the same.


Step 6 — Set the Workshop Cron Schedule

In token-dispenser, edit .github/workflows/rotate-token.yml and update the cron to match your workshop window. Tokens expire after 1 hour, so rotate at least every 45 minutes within your session window.

Note that for a free github organization, the schedule might not run as expected. In that case, you can trigger manual rotations from the Actions tab in token-dispenser & scoreboard as needed..

Example for a workshop running 10:00–13:00 UTC on March 4th:

on:
  schedule:
    - cron: "*/45 10-12 4 3 *"
  workflow_dispatch:

Commit and push this change before the workshop starts.


Secrets Reference

The following secrets must be set on token-dispenser (the setup script handles this):

Secret Description
APP_ID Numeric App ID from the GitHub App settings page
APP_PRIVATE_KEY Full PEM content including -----BEGIN RSA PRIVATE KEY----- headers

Facilitator Runbook

See docs/FACILITATOR.md for the full running order and facilitation notes.


Customizing

Adding a new challenge

  1. Add an entry to repos/token-dispenser/challenges.json
  2. Create docs/challenges/challenge-0N.md with vulnerability, exploit, fix, and facilitator nudges
  3. Add the challenge row to docs/CHALLENGES.md
  4. Create the challenge repo files under repos/challenge-N/
  5. Run setup.sh to create the repo and push the files
  6. Grant the GitHub App access to flag-N in the org installation settings
  7. Trigger a manual rotation in token-dispenser

Changing the token expiry

GitHub App installation tokens have a maximum lifetime of 1 hour — this cannot be changed. Adjust the rotation cron to be shorter than 1 hour to ensure tokens never expire mid-workshop.

Using an existing organization

The setup script is safe to run against an existing org — it only creates new repos and will not touch any existing ones. Repo names are hardcoded as challenge-N and flag-N to avoid collisions.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors