Cyber News / Article / Six Accounts, One Actor: Inside the prt-scan Supply Chain Campaign

Six Accounts, One Actor: Inside the prt-scan Supply Chain Campaign
After hackerbot-claw, another AI-powered campaign exploiting pull_request_target confirms the threat is here to stay. We trace the attacker back to three weeks before anyone noticed.
On April 2, 2026, security researcher Charlie Eriksenpublicly identifiedan automated campaign exploiting GitHub'spull_request_targetworkflow trigger. The attacker, operating under the accountezmtebo, openedover 475 malicious PRs in 26 hourstargeting repositories belonging to both prominent organizations and hobbyists. This attacker is reminiscent ofhackerbot-claw, the AI powered CI/CD attacker that used five different exploitation methods across seven successful high profile attacks.
But public reports onprt-scanmissed the beginning of the story. Wiz Research has identified six total waves of activity from the same threat actor, starting on March 11, 2026 -three weeks before public disclosure. Across all six waves, the attacker opened well over 500 malicious PRs, successfully compromised at least two npm packages, and evolved from crude bash scripts toAI-generated, language-aware payloads. However, despite elaborate multi-phase payloads, the attack revealsfundamental misunderstandings of GitHub’s threat model, significantly limiting their success.
The campaign exploits a well-documented but still widespread misconfiguration: GitHub'spull_request_targettrigger. Unlikepull_request, this trigger runs in the context of the base repository, granting access to repository secrets even when the PR originates from a fork. The attacker's playbook:
Search for repositories usingpull_request_targetwithin GitHub Actions
Fork the target repository
Create a branch namedprt-scan-{12-hex-chars}
Inject a malicious payload into a file that executes during CI (conftest.py,package.json, Makefile,build.rs, etc.)
Open a PR with the innocuous title "ci: update build configuration"
If the malicious workflow triggers: stealGITHUB_TOKEN, enumerate secrets, probe cloud metadata (AWS/Azure/GCP), and exfiltrate credentials via base64-encoded workflow log markers and PR comments
IfNPM_TOKENfound: publish a malicious package version
Wave 1a:testedbefore(March 11–13)Email:[email protected]
The earliest campaign activity dates to March 11, 2026 around 08:00 UTC, when a now-deleted GitHub account began opening malicious PRs. Across 10 PRs we see small repositories targeted. All PRs present consistent TTPs: theprt-scan-branch prefix, the same title and body text, andpull_request_targetexploitation.
The attacker was already testing multiple injection vectors. These includesetup.pyusing Python'sos.system()and Makefile targets with.PHONY: __prt_exfil. As early as this first wave, we can see the attacker building complex payloads featuring encoded stages across the RECON/DISPATCH patterns that will be traced throughout the campaign.
Wave 1b:beforetested-boop(March 13–16)Email:[email protected]
Eleven hours aftertestedbefore’s last PR, we see this new account continue the raw shell approach, targeting larger projects. This account managed 47 PRs, as the attacker began to scale up their efforts. Payloads continued the prior pattern with package.json and Makefile injections.
Wave 2a:420tb(March 28)Email:[email protected]
After a two week gap in observed activity, a new account picks up the action. Across 11 PRs in 37 minutes, we see the attacker begin to polish their approach.
Wave 2b:69tf420(March 28-29)Email:[email protected]
Two hours later,69tf420(GitHub ID271847720) continued with identical attacks. Consistent TTPs were present: branch naming convention, PR title, and python-requests/2.32.5 user agent.The payload in this wave was a consistent raw 351-line bash script (.github/prt_exfil.sh) dropped directly into the workflow directory with no obfuscation.
Wave 3a:elzotebo(April 2)Email:[email protected]
This account began experimenting with "smart" file placement, and targeted high-profile repositories. It managed 13 PRs before activity ceased. This included strange hallucinations likepip.py, as well as frequently falling back to theprt_exfil.shbash payload.
Wave 3b:ezmtebo(April 2-3)Email:[email protected]
That brings us to the publicly reported wave. The account was created April 2 at 20:59 UTC, with the first malicious PR opened just two minutes later. Over the next 26 hours,the attacker opened over 475 PRs.
By this point, the attacker had evolved significantly: AI-generated, repository-aware wrappers that adapt to each target's tech stack. Go test files for Go repos, npm scripts for JavaScript projects,conftest.pyfor Python. Better crafted payloads, with more obfuscation, and heavy stages (RECON, DISPATCH, DELAYED) are base64-encoded.
Despite the campaign's AI-assisted targeting, the payloads reveal a gap between ambition and execution. The attacker attempted a sophisticated multi-phase payload, but filled it with techniques that feel illogical to an expert and would rarely work in practice.
For a full breakdown of the payload, see the Appendix.
Every payload follows the same five-phase structure, regardless of injection point:
The attacker also generates language-appropriate wrappers:
Despite the elaborate payload, several design choices reveal the attacker's misunderstanding of GitHub's permission model:
Label bypass is dead code: attempting to bypass label-gated workflows by creating and applying labels via API is circular, and unlikely givenGITHUB_TOKENdefaults inpull_request_targetworkflows.
Permissions-blind exploitation: the attempts in the DISPATCH phase to create a new workflow using stolen tokens is ignorant to the permissions context being exploited.
Mismatched injection files: despite the targeting, the attack frequently pairs the wrong payload and repository, like injectingbuild.rsin a Python SDK repo.
Blind metadata-probing: attempting to access IMDS on self-hosted runners is doomed to fail and likely to drive detection.
The attack shows automation, not understanding. The attacker built multi-language wrappers, modular base64 stages, and unique nonces per target. But they failed to validate whether techniques would work against GitHub's permission model.
Across over 450 analyzed exploit attempts,we have observed a <10% success rate. In most cases, successful attacks were against small hobbyist projects, and only exposed ephemeral GitHub credentials for the workflow. For the most part,this campaign did not grant the attacker access to production infrastructure, cloud credentials, or persistent API keys, barring minor exceptions. High-value targets including Sentry, OpenSearch, IPFS, NixOS, Jina AI, and recharts all successfully blocked the attack through a combination of first-time contributor approval gates, actor-restricted workflows, and path-based trigger conditions. The campaign demonstrates that whilepull_request_targetvulnerabilities remain exploitable at scale, modern CI/CD security practices, particularly contributor approval requirements, are effective at protecting high-profile repositories.
However, the attacker successfully compromised at least two npm packages with a shared maintainer, across 106 versions: across@codfish/eslint-configand@codfish/actions, versions like0.0.0-PR-{PR#}--{commit}
Several characteristics suggest this campaign leverages AI/LLM capabilities:
Velocity: ~7 PRs per hour sustained over 22+ hours
Adaptive targeting: Dynamically identifies language, framework, test runner, and CI configuration
Idiomatic payloads: Generated code matches repository conventions (Go test file structure, pytest conftest patterns, npm script hooks)
This represents an evolution in attacker capability: agentic tooling that can fork, analyze, inject, and submit at machine speed.
Review your GitHub organization for Indicators of Compromise (listed below). Wiz customers shouldrefer to our Threat Center Advisory. For preventative guidance, you can refer to ourblog poston GitHub hardening.
Theprt-scancampaign demonstrates how AI-assisted automation is lowering the barrier to large-scale supply chain attacks. Identifying vulnerable workflows, crafting plausible injection points, and adapting to different tech stacks previously required expertise and manual orchestration. It can now be executed at machine speed across hundreds of targets by low-sophistication attackers.
The campaign's 10% success rate may seem low, but at ~500 attempts, that adds up to dozens of successful compromises.Verified credential theft was observed impacting AWS keys, Cloudflare API tokens, and Netlify auth tokens.Further, the attacker is learning: each wave showed improved evasion, more sophisticated payloads, and better operational security.
This is supply chain security's new adversary: automated, adaptive, and operating faster than manual review can catch.
GitHub Accounts
testedbefore <[email protected]>
beforetested-boop <[email protected]>
420tb <[email protected]>
69tf420 <[email protected]>
elzotebo <[email protected]>
ezmtebo <[email protected]>
Malicious Branch Pattern:prt-scan-[12-character-hex]
PR Signatures
Title:'ci: update build configuration'
Body:'Automated build configuration update.'
User-Agent:python-requests/2.32.5
Payload Markers in Workflow Logs
Phase 1 - EXFIL: Immediately dump environment variables and extract the GitHub token from git config, exfiltrate by printing gzipped and base64-encoded to workflow logs where the attacker can retrieve it.
Phase 2 - RECON: Using the stolen token, enumerate secret names via GitHub API, list deployment environments and workflow files, and probe cloud metadata endpoints for SSRF opportunities.
Prereqs: Token needs actions:read or metadata:read (default in most workflows).
Phase 3 - DISPATCH: If the token has write access, create a temporary workflow file on the default branch that references all discovered secret names, then trigger it via workflow_dispatch to capture actual secret values in logs.
Prereqs: Token needs contents:write + actions:write.
Phase 4 - LABEL_BYPASS: Parse repository workflow files for label-gated conditions (e.g., if: github.event.label.name == 'safe to test'), then create and apply the required label to the malicious PR to trigger additional privileged workflows.
Prereqs: Token needs issues:write or pull_requests:write to apply labels.
Phase 5 - DELAYED: Launch a background Python daemon that scans /proc/*/environ every 2 seconds for 10 minutes, catching secrets loaded by later job steps, then exfiltrate via PR comments which persist even if workflow logs are deleted. For example, in one case we observed the initial EXFIL captured only the GitHub token, but the DELAYED daemon caught AWS_SECRET_ACCESS_KEY when it was loaded minutes later by an e2e test step, posting it to PR comments. However, other secrets like the associated AWS_ACCESS_KEY_ID were not captured.
Prereqs: Token needs pull_requests:write or issues:write to post comments; Linux /proc filesystem required.
A compromised axios maintainer account led to malicious npm releases that propagated across environments. Learn how to assess impact, detect compromise, and secure your development workflows.
How TeamPCP are leveraging stolen secrets from the recent supply chain attacks to compromise cloud environments
Accelerate your SecOps team with the Blue Agent for threat investigation, now Generally Available
Get a personalized demo
©2026Wiz, Inc.
StatusPrivacy PolicyTerms of UseModern Slavery StatementCookie Settings
Related articles
Attackers Chain JFrog Artifactory Flaws to Gain Admin Control and Plant Backdoors
about 7 hours ago
Chrome V8 Zero-Day Exploited in the Wild Enables Code Execution Inside Sandbox
2 days ago
FreeIPA Flaw Chain Lets Anonymous Clients Create Reusable Administrator Credentials
3 days ago
You might Also like

Anthropic Says Russian Hackers Used Claude AI to Automate Malware Evasion

GitLab Patches Critical Flaws Enabling Arbitrary File Read, Credential Theft and Remote Code Execution

