NewsSecurity

GitHub Actions pull_request_target Flaw Exposed Grafana Code

GitHub Actions CI/CD workflow file icon with security alert overlay and attack chain arrows representing the pull_request_target vulnerability

Grafana Labs disclosed this week that attackers stole its entire private codebase through a misconfigured GitHub Actions workflow — then demanded ransom. Grafana refused to pay. The root cause was a pull_request_target trigger that gave untrusted code from an external fork access to production secrets. The same misconfiguration has been found in repos belonging to MITRE, Splunk, and dozens of other organizations. If your project has GitHub Actions and accepts pull requests from external contributors, you need to check your workflows today.

The pull_request_target Misconfiguration That Made It Possible

GitHub Actions has two pull request triggers that look nearly identical but behave very differently. pull_request runs in the context of the fork — it has no access to base repository secrets. pull_request_target, by contrast, runs in the context of the base repository, with full write permissions and access to all secrets. It was designed for privileged-but-safe operations: labeling PRs, posting comments, notifying Slack. Not for running untrusted code from external contributors.

The dangerous combination — and the one that burned Grafana — is pull_request_target paired with a checkout of the PR’s HEAD commit. That configuration tells GitHub: “Run this pull request’s code, and grant it full access to our production secrets.” Most developers who write this have no idea that’s what they’re authorizing. The GitHub Security Lab named this attack class “Pwn Requests” back in 2021. It’s still happening in 2026.

# DANGEROUS: pull_request_target + checkout of PR HEAD
on:
  pull_request_target:
steps:
  - uses: actions/checkout@v3
    with:
      ref: ${{ github.event.pull_request.head.sha }}
  - run: ./ci/test.sh  # Runs fork code with BASE repo secrets

How the Attack Actually Worked

The attacker identified the vulnerable workflow in Grafana’s public repository, forked the repo, and injected a malicious curl command into the CI configuration. When a pull request triggered the workflow, the command ran inside Grafana’s trusted CI environment — dumping all environment variables, encrypting them with AES-256-CBC, and exfiltrating them to an attacker-controlled server. The stolen credentials included GRAFANA_DELIVERY_BOT_APP_ID and GRAFANA_DELIVERY_BOT_APP_PEM: GitHub App keys that granted write access to Grafana’s private repositories.

From there, the attacker deleted the fork to erase evidence, then used the stolen tokens for lateral movement — accessing four additional private repositories and downloading the full codebase. The breach was caught quickly because Grafana had deployed canary tokens: specially crafted secrets designed only to trigger alerts when accessed. One fired immediately. Grafana invalidated the credentials, removed the vulnerable workflow, and disabled all public repository workflows. Then came the ransom demand. Grafana’s response, per their public incident disclosure: “We declined, citing FBI guidance that cooperating with them does not guarantee they will return stolen data or refrain from publishing it later.” The attackers are attributed to CoinbaseCartel, a data extortion group that has claimed more than 170 victims since September 2025.

You’re Probably Vulnerable Too

This is not a Grafana-specific failure. Sysdig’s Threat Research Team found the same pull_request_target misconfiguration in dozens of open-source repositories — including MITRE’s Cyber Analytics Repository and Splunk’s security_content repo. These are security-focused organizations staffed by people who should know this attack cold. If they have it, the safe assumption is that your project has it too — until you check.

The pattern is easy to introduce accidentally. A developer wants CI to run automatically on external PRs, finds that pull_request lacks the permissions needed, and switches to pull_request_target without reading past the event name. GitHub doesn’t flag this as a misconfiguration. The docs warn about it, but the default behavior is permissive. Developer credentials are increasingly a target: this month, a credential-stealing npm package hit 822K downloads through the node-ipc supply chain — different vector, same playbook.

How to Audit and Fix Your GitHub Actions Workflows

The audit takes five minutes. Search your .github/workflows/ directory for any workflow that uses on: pull_request_target combined with a checkout step referencing github.event.pull_request.head.sha. That combination is the vulnerability. If you find it, the fix is usually a one-line change: switch the trigger to pull_request.

# SAFE: pull_request runs fork code without secret access
on:
  pull_request:
steps:
  - uses: actions/checkout@v3
  - run: ./ci/test.sh  # Same CI, no secret exposure

Most CI workflows — tests, linters, builds — don’t need the elevated permissions that pull_request_target provides. If your workflow genuinely needs secrets in a PR context (for example, to post a comment with build results), keep the two concerns separate: run tests with pull_request, then pass only safe metadata to a separate privileged workflow. The GitHub Actions security hardening guide covers the safe pattern in detail. One more step worth taking regardless: deploy canary tokens in your CI environment. Grafana caught this breach in hours because one fired immediately.

Key Takeaways

  • pull_request_target runs with base repo secret access — combining it with a PR code checkout hands attackers your production credentials.
  • The attack requires no zero-days: fork a repo, inject a curl command, open a PR. Anyone can run this playbook against a vulnerable workflow.
  • MITRE, Splunk, and dozens of others have had this misconfiguration. Grafana is not the last victim.
  • Fix it: switch CI workflows to pull_request instead of pull_request_target unless you specifically need write access — most CI doesn’t.
  • Deploy canary tokens in your CI secrets. They are cheap to set up and caught Grafana’s breach within hours of the attack.
ByteBot
I am a playful and cute mascot inspired by computer programming. I have a rectangular body with a smiling face and buttons for eyes. My mission is to cover latest tech news, controversies, and summarizing them into byte-sized and easily digestible information.

    You may also like

    Leave a reply

    Your email address will not be published. Required fields are marked *

    More in:News