GitHub Actions workflows can access various sources of untrusted data through the GitHub context, such as:
- Pull request titles, descriptions, and commit messages
- Issue titles and comments
- Branch names and tag names
- User-provided inputs
When this untrusted data is directly interpolated into shell commands using GitHub’s expression syntax (${{ … }}), it can lead to
script injection vulnerabilities. An attacker who can control the content of these data sources (for example, by creating a pull request with a
malicious title) can potentially execute arbitrary commands on the runner.
Understanding expression expansion
It’s important to understand that GitHub Actions expressions (${{ … }}) are expanded before the shell script is
executed. This expansion happens at the workflow processing stage, not within the shell itself. This means:
- GitHub Actions first evaluates the expression and replaces it with the actual value
- The resulting string (which may contain shell metacharacters) is then passed to the shell
- The shell receives and executes the already-expanded string as part of the script
Even seemingly safe assignments like pr_title="${{ github.event.pull_request.title }}" are vulnerable because the expansion happens
first. For example, if an attacker creates a pull request with a title containing "; rm -rf / #, the workflow will expand the expression
to:
pr_title=""; rm -rf / #"
The shell will then interpret this as:
- An assignment:
pr_title="" (assigns empty string)
- A command separator:
;
- An arbitrary command:
rm -rf /
- A comment:
# (hides the remaining quote)
This demonstrates that any use of untrusted data in GitHub expressions within a run block is dangerous, regardless of
whether it appears to be a simple assignment or a direct command.
What is the potential impact?
The consequences of successful script injection attacks in GitHub Actions can be severe and far-reaching:
Information disclosure
Attackers can extract sensitive information from the runner environment, including:
- Repository secrets and environment variables
- Authentication tokens and API keys
- Build artifacts and deployment credentials
System compromise
Successful command injection allows attackers to:
- Execute arbitrary commands on the runner
- Install malware or backdoors
- Access other resources available to the runner
Supply chain attacks
Compromised workflows can be used to:
- Inject malicious code into build artifacts
- Modify dependencies or packages being published
- Compromise downstream systems that consume the artifacts
Repository manipulation
Attackers may be able to:
- Modify repository contents or history
- Create unauthorized releases or tags
- Access or modify other repositories the workflow has access to
The impact is particularly severe because GitHub Actions runners often have elevated privileges and access to sensitive resources, making them
attractive targets for attackers.