SC-GHA-SCRIPT-INJECTION-2020
CI/CD · GitHub Actions · GitHub Actions workflows
Résumé
GitHub Security Lab documented (initial guidance August 20, 2020, updated since) a widespread GitHub Actions vulnerability class in which attacker-controlled event fields interpolated into run shell steps cause command execution. Because expressions in double-brace syntax are evaluated and substituted into the temporary shell script before the runner executes it, embedding an untrusted field such as github.event.issue.title or github.event.pull_request.head_ref directly in a run block lets the attacker break out of the intended command. An attacker who simply opens an issue or PR with a title containing a quote and a shell payload (for example a backtick command-substitution or a semicolon-curl sequence) executes arbitrary commands on the runner, reading environment variables and exfiltrating GITHUB_TOKEN and repository secrets to an external host. This is expression/script injection via untrusted github.event input, found across a large number of public-repo workflows. The fix is to never interpolate untrusted context into a shell; pass it through an intermediate quoted environment variable so it is treated as data, not script.
Comment l’éviter dans votre code
- Never interpolate github.event.* directly into run shells; assign it to an env variable and reference it quoted.
- Treat all github.event fields ending in title, body, ref, head_ref, name, label, message, or email as untrusted input.
- Set GITHUB_TOKEN permissions to read-only by default and grant write scopes only on the specific jobs that need them.
- Run static analysis (CodeQL, zizmor) on workflow files to catch expression-injection sinks before merge.
- Move logic that handles untrusted input into a pinned action or script that takes arguments, not inline templated shell.
Références
- https://securitylab.github.com/resources/github-actions-untrusted-input/
- https://github.blog/security/supply-chain-security/four-tips-to-keep-your-github-actions-workflows-secure/
- https://docs.github.com/en/actions/concepts/security/script-injections
- https://semgrep.dev/docs/learn/vulnerabilities/command-injection/github-actions-injection
Vulnérabilités liées
Tout Supply chain →- HIGHGHSA-7QW2-W5RC-37X2
PraisonAI recipe workflow policy can be bypassed by declaring and YAML-approving dangerous tools outside TEMPLATE.yaml
- CRITICALSC-PPE-CICDSEC4-2022
Poisoned Pipeline Execution is the class of attack in which an actor with write access to source control, but no direct access to the build environment, injects attacker-controlled commands that the CI pipeline then executes with its own privileges, secrets, and tokens. Direct PPE (D-PPE) modifies the CI configuration file itself (for example .github/workflows, .gitlab-ci.yml, or a Jenkinsfile) by pushing to an unprotected branch or opening a pull request, so the new pipeline steps run on trigger. Indirect PPE (I-PPE) instead poisons files the pipeline already references, such as a Makefile, test harness, build script, or linter config, when the config is protected but the referenced code is not. Public PPE (3PE) abuses public and open-source repositories that run unreviewed code from anonymous fork pull requests, frequently via the dangerous pull_request_target trigger that grants the fork workflow access to repository secrets. The pattern is catalogued as CICD-SEC-4 in the OWASP Top 10 CI/CD Security Risks (published September 2022) and in Cider/Legit Security research, with real cases including public-repo PPE in popular projects and GitHub Actions workflows abused for cryptocurrency mining.
- CRITICALGHSA-4H5R-5JM8-JXJM
gemini-mcp-tool vulnerable to OS command injection and @file exfiltration via prompt quoting (CVE-2026-0755)
- HIGHGHSA-5CJ2-3JR2-5H77
OpenClaw: Shell positional parameters could weaken strict inline-eval checks
- LOWGHSA-CWPP-5962-Q4F6
OpenClaw: Exec allowlist could miss side effects from transparent command wrappers
- CRITICALGHSA-R253-R9JW-QG44
Crawl4AI: Unauthenticated RCE via Chromium launch-argument injection in browser_config.extra_args