
Proof-of-concept lab demonstrating command injection in GitHub Actions workflow dispatch (CVE-2026-39866). Runs vulnerable and patched versions side-by-side for educational analysis.
Proof-of-concept lab for command injection in GitHub Actions workflow dispatch
CVE-2026-39866 is a command injection bug in Lawnchair's GitHub Actions release workflow. The release_update.yml file passes user input directly to bash without quoting, so an attacker can inject commands. This repo runs both the vulnerable and patched versions side by side.
| Field | Value |
|---|---|
| CVE ID | CVE-2026-39866 |
| GHSA | GHSA-9prc-pp2c-3427 |
| CWE | CWE-78 (Improper Neutralization of Special Elements used in an OS Command) |
| Product | LawnchairLauncher/lawnchair |
| Affected | All versions below fcba413 |
| Patched | fcba413 |
| Discovery | Abhay Kumar (@abhayclasher) |
The vulnerability exists in the release_update.yml workflow file. The action takes an artifactName parameter and passes it directly into a bash command without quoting.
- name: Rename artifact
run: mv ${{ inputs.artifactName }}.zip lawnchair.zip
GitHub Actions substitutes the variable value into the string before the shell runs it. Without quotes around ${{ inputs.artifactName }}, the shell interprets spaces and semicolons as command separators.
If an attacker supplies file.zip; touch pwned.txt # as the artifactName:
How GitHub Actions parses it:
${{ inputs.artifactName }} → file.zip; touch pwned.txt #
How the shell executes it:
mv file.zip; touch pwned.txt #.zip lawnchair.zip
The shell executes mv file.zip, then touch pwned.txt, then ignores the rest as a comment.
GITHUB_TOKEN or release signing keys).git clone https://github.com/abhayclasher/CVE-2026-39866.git
cd CVE-2026-39866/app
npm install
node server.js
git clone https://github.com/abhayclasher/CVE-2026-39866.git
cd CVE-2026-39866
docker compose up -d
After the server starts, visit these URLs:
| URL | Description |
|---|---|
http://localhost:3000/workflow/vulnerable | Vulnerable workflow — command injection executes on submit |
http://localhost:3001/workflow/patched | Same workflow with fcba413 patch applied |
The vulnerable version will execute your injected command on the runner. The patched version treats the input as a literal string.
You can also run the standalone PoC:
cd poc
node exploit.js
This spins up a minimal server on port 3000 that demonstrates the same bug without the full application context.
1. Attacker controls the artifactName input parameter
2. GitHub Actions expands ${{ inputs.artifactName }} into the YAML
3. The run step sends this to bash
4. Bash sees unquoted special characters and treats them as command syntax
5. Commands execute with runner environment privileges
┌────────────────────────────────────────────────────┐
│ 1. Malicious input: │
│ │
│ artifactName = "file.zip; id #" │
│ │
│ 2. GitHub Actions substitutes into YAML: │
│ │
│ run: mv file.zip; id #.zip lawnchair.zip │
│ │
│ 3. Bash sees: │
│ - mv file.zip │
│ - id <-- Arbitrary command │
│ - #.zip lawnchair.zip (ignored) │
│ │
│ 4. Shell executes arbitrary command │
└────────────────────────────────────────────────────┘
Version fcba413 introduces quoting around the interpolated variable:
- name: Rename artifact
run: mv "${{ inputs.artifactName }}.zip" lawnchair.zip
Quotes prevent the shell from parsing special characters. The payload stays treated as a literal filename string.
Best practice is to use environment variables to completely isolate user input:
- name: Rename artifact
env:
ARTIFACT_NAME: ${{ inputs.artifactName }}
run: mv "$ARTIFACT_NAME.zip" lawnchair.zip
View the full commit: fcba413
CVE-2026-39866/
├── README.md # This file
├── docker-compose.yml # Docker lab configuration
├── app/
│ ├── Dockerfile # Container build configuration
│ ├── package.json # Node.js dependencies
│ └── server.js # Vulnerable + patched workflow runner simulation
└── poc/
└── exploit.js # Standalone minimal proof-of-concept
Reported by Abhay Kumar. This repository is intended for local educational use only. Do not deploy the vulnerable server on a public network.
Educational purposes only — use in isolated environments