Threat reportSupply ChainTL-2026-0318

prt-scan: AI-Powered GitHub Actions Supply Chain Campaign Exploiting pull_request_target

highMONITORING

prt-scan: AI-Powered GitHub Actions Supply Chain Campaign (TL-2026-0318), also tracked as prt-scan campaign, is a high-severity supply-chain compromise scored CVSS 8.6, first published 2026-04-04. It has no confirmed attribution, affects GitHub GitHub Actions (pull_request_target trigger), maps to 18 MITRE ATT&CK techniques (T1027, T1036, T1057), and is covered by 9 detection rules and 21 indicators of compromise.

CVSS
8.6/10High
CVEs
0None referenced
Techniques
18MITRE ATT&CK
Actors
0Not attributed
Detection rules
9SPL · KQL · Sigma
IOCs
21Indicators of compromise

Key facts for TL-2026-0318

Threat ID
TL-2026-0318
Also known as
prt-scan campaign, PRT_EXFIL campaign
Severity
HIGH
CVSS
8.6 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N)
Status
MONITORING
Category
SUPPLY_CHAIN
First published
Last reviewed
Attribution confidence
HIGH
Motivation
FINANCIAL
Target sectors
technology, open-source, cloud-infrastructure, cybersecurity, enterprise-software, financial, iot, embedded-systems, developer-tools, blockchain
Target regions
Global
Detection rules
9
Indicators of compromise
21

How prt-scan: AI-Powered GitHub Actions Supply Chain Campaign works

Coordinated supply chain campaign by a single threat actor using 6 GitHub accounts across 6 waves (March 11 - April 3, 2026). Exploits pull_request_target workflow trigger to steal CI/CD secrets, cloud credentials, and API tokens from 500+ repositories using AI-generated, language-aware payloads with a 5-phase exploitation chain.

The prt-scan campaign is a sustained, multi-wave supply chain attack targeting open-source repositories that use GitHub Actions with the pull_request_target workflow trigger. A single threat actor operated 6 disposable GitHub accounts — all registered with Proton Mail addresses using the plus-alias pattern — across 3 weeks from March 11 to April 3, 2026.

The attack methodology is systematic: the actor forks target repositories, creates branches matching the pattern prt-scan-{12-hex-chars}, injects language-aware malicious payloads into CI configuration files (conftest.py for Python, package.json for Node.js, build.rs for Rust, Makefile for Make-based projects, action.yml for GitHub Actions), and opens pull requests with the generic title "ci: update build configuration" and body "Automated build configuration update." When target repositories have pull_request_target workflows that checkout the PR head SHA, the malicious code executes in the privileged workflow context with access to repository secrets.

The payload implements a sophisticated 5-phase exploitation chain:

Phase 1 (EXFIL): Immediately dumps all environment variables and extracts the GITHUB_TOKEN from git HTTP extra headers, encoding output as gzip+base64 between ==PRT_EXFIL_START== and ==PRT_EXFIL_END== markers in CI logs.

Phase 2 (RECON): Enumerates repository and organization secrets via GitHub API endpoints (/actions/secrets, /actions/organization-secrets, /environments), parses workflow YAML files for secrets.XXX references, checks token permissions, downloads recent artifacts, requests OIDC tokens, and probes cloud instance metadata services (AWS IMDS at 169.254.169.254, Azure IMDS, GCP metadata.google.internal).

Phase 3 (DISPATCH): If the stolen token has contents:write and actions:write permissions, creates a temporary workflow file (.github/workflows/.prt_tmp_<nonce>.yml) on the default branch that maps all discovered secrets into environment variables and dispatches it, escalating from reading secret names to exfiltrating actual secret values.

Phase 4 (LABEL_BYPASS): Runs a base64-encoded Python script that parses all workflow YAMLs for pull_request_target label gates, extracts the required label name (defaulting to "safe to test"), creates the label via GitHub API, and applies it to the attacker's own PR — defeating label-based approval gates.

Phase 5 (DELAYED): Launches a background Python daemon that scans /proc/*/environ every 2 seconds for 10 minutes, searching for 22 high-value environment variable keywords including NETLIFY, AWS_SECRET, NPM_TOKEN, CLOUDFLARE, STRIPE, OPENAI, ANTHROPIC, DATABASE_URL, SSH_PRIVATE, and others. Findings are exfiltrated via PR comments wrapped in ==PRT_DELAYED_START== markers, catching secrets injected by later CI steps.

The campaign uses no external command-and-control infrastructure — all exfiltration occurs through GitHub API calls, CI build logs, and PR comments. The actor's User-Agent is consistently python-requests/2.32.5. Payloads are AI-generated and repository-aware, adapting injection vectors to each target's technology stack (pytest hooks for Python repos, npm preinstall scripts for Node.js, cargo build scripts for Rust, .PHONY targets for Make). Some variants use double base64 encoding for obfuscation.

Confirmed compromises include the npm packages @codfish/eslint-config and @codfish/actions (106 malicious versions each published with pattern 0.0.0-PR-{PR#}--{commit}), and darlal/obsidian-switcher-plus where GITHUB_TOKEN and DIFF_ACTION_TOKEN were exposed. High-profile targets include Svelte, Zephyr RTOS, AWS SageMaker Core, Palo Alto Networks pan.dev, SAP open-ux-tools, Red Hat Developer Hub, Sentry, OpenSearch, IPFS, NixOS, Jina AI, recharts, Capstone, and JHipster.

Bot review results were mixed: CodeRabbit flagged all variants as critical, Sourcery AI caught exfiltration and label bypass, Qodo identified 3 critical blocking bugs — but gstraccini auto-approved PRs creating false legitimacy, and Codacy reported 0 issues. The campaign sustained approximately 7 PRs per hour over 22+ hours with an overall ~10% success rate across 500+ malicious PRs.

The fundamental architectural weakness exploited is pull_request_target workflows that checkout PR head code (ref: github.event.pull_request.head.sha) without restrictions, granting fork PRs access to repository secrets. Secondary weaknesses include insufficient label-based gating (attackers can create/apply their own labels), lack of first-time contributor approval requirements, and reliance on automated bot reviews.

MITRE ATT&CK techniques used in TL-2026-0318

defense-evasion

T1027 Obfuscated Files or Information; T1036 Masquerading; T1684.001 Impersonation

discovery

T1057 Process Discovery; T1087 Account Discovery; T1518 Software Discovery; T1526 Cloud Service Discovery

execution

T1059 Command and Scripting Interpreter

persistence

T1098 Account Manipulation; T1543 Create or Modify System Process

collection

T1119 Automated Collection; T1213 Data from Information Repositories

initial-access

T1195 Supply Chain Compromise; T1199 Trusted Relationship

credential-access

T1528 Steal Application Access Token; T1552 Unsecured Credentials

exfiltration

T1567 Exfiltration Over Web Service

resource-development

T1585 Establish Accounts

Affected products and versions in prt-scan: AI-Powered GitHub Actions Supply Chain Campaign

  • GitHub — GitHub Actions (pull_request_target trigger)
    Vulnerable versions: All repositories using pull_request_target with head checkout
    Fixed in: N/A — architectural misconfiguration, not a software bug
  • codfish — @codfish/eslint-config (npm)
    Vulnerable versions: 106 malicious versions: 0.0.0-PR-*
    Fixed in: Unpublished malicious versions
  • codfish — @codfish/actions (npm)
    Vulnerable versions: 106 malicious versions: 0.0.0-PR-*
    Fixed in: Unpublished malicious versions
  • darlal — obsidian-switcher-plus
    Vulnerable versions: PR #236 — GITHUB_TOKEN and DIFF_ACTION_TOKEN exposed
    Fixed in: PR closed by maintainer

Remediation for prt-scan: AI-Powered GitHub Actions Supply Chain Campaign

Immediate actions

  • Close all open PRs from accounts: ezmtebo, elzotebo, 69tf420, 420tb, beforetested-boop, testedbefore without running CI
  • Search CI workflow logs for PRT_EXFIL_START, PRT_RECON_START, PRT_DELAYED_START markers — assume full secret compromise if found
  • Search default branches for .github/workflows/.prt_tmp_*.yml residual files from Phase 3 injection
  • Rotate ALL CI/CD secrets immediately if exfiltration markers detected in logs
  • Report ezmtebo and related accounts to GitHub Trust & Safety

Workarounds

  • Replace pull_request_target with pull_request trigger where possible (fork PRs will not have secret access)
  • Add explicit permissions blocks to workflows limiting token scope (contents: read, pull-requests: read)
  • Require manual approval labels from maintainers only (not PR authors) before CI runs on fork PRs

Longer-term hardening

  • Audit all pull_request_target workflows — ensure they NEVER checkout PR head code (github.event.pull_request.head.sha)
  • Implement first-time contributor approval gates for all CI workflows
  • Use path-based trigger conditions to limit pull_request_target scope
  • Isolate fork PR workflows in restricted contexts with zero secret access
  • Gate privileged operations behind maintainer-only approval
  • Deploy AI-powered code review bots (CodeRabbit, Sourcery AI) that detect exfiltration patterns
  • Monitor for prt-scan-* branch patterns and generic CI update PR signatures across organization repos

Weaknesses (CWE) in prt-scan: AI-Powered GitHub Actions Supply Chain Campaign

CWE-829, CWE-494, CWE-352, CWE-269, CWE-200

Timeline of prt-scan: AI-Powered GitHub Actions Supply Chain Campaign

  • First wave begins. Account 'testedbefore' (testedbefore@proton.me) starts submitting malicious PRs to open-source repositories targeting pull_request_target workflows.
  • Account 'testedbefore' ceases activity. Account 'beforetested-boop' (testedbefore+89@proton.me) takes over, continuing the campaign with identical prt-scan branch pattern.
  • Account 'beforetested-boop' ceases activity. Campaign enters 12-day dormancy period.
  • Campaign resumes with accounts '420tb' (testedbefore+55@proton.me) and '69tf420' (testedbefore+99@proton.me, GitHub ID 271847720) submitting PRs in rapid succession.
  • Account '69tf420' ceases activity. Campaign enters second dormancy.
  • darlal/obsidian-switcher-plus PR #236 triggers npm hook variant. GITHUB_TOKEN and DIFF_ACTION_TOKEN exposed through exfiltration chain. Community member flags PR as suspicious; maintainer closes.
  • npm packages @codfish/eslint-config and @codfish/actions compromised — 106 malicious versions published per package with version pattern 0.0.0-PR-{PR#}--{commit}.
  • Final escalation wave. Accounts 'elzotebo' (elzotebo@proton.me) and 'ezmtebo' (elzotebo+88@proton.me, GitHub ID 273211198) activate with highest velocity — approximately 7 PRs per hour over 22+ hours.
  • Account 'ezmtebo' ceases activity following public disclosure. Earlier accounts already deleted/ghosted by GitHub.
  • Wiz Research and SafeDep publish independent analyses of the prt-scan campaign, identifying all 6 accounts and the 5-phase payload structure. Campaign reaches 500+ malicious PRs with ~10% success rate.
  • As of 2026-05-29, the specific prt-scan operator went quiet after public disclosure on April 3 (all 6 disposable accounts deleted/ghosted by GitHub, no resumption since), but the campaign stays a live concern: the pull_request_target root cause is an unpatched architectural flaw and GitHub's mitigations remain unshipped previews. The same technique is being actively exploited in May 2026 by successor/copycat actors (e.g. Mini Shai-Hulud TanStack, AI-driven CI/CD scanners), so the TTP can readily

Sources cited for prt-scan: AI-Powered GitHub Actions Supply Chain Campaign

Detection coverage for TL-2026-0318

As of 2026-04-04, Threadlinqs Intelligence publishes 9 detection rule(s) for TL-2026-0318 across Splunk SPL, Microsoft KQL and Sigma, covering 21 indicator(s) of compromise. The whole corpus is readable without an account; a free account unlocks full detection query text in Splunk SPL, Microsoft KQL and Sigma; paid tiers add raw indicator values, correlation and the MCP server. Threadlinqs MCP server · View plans.

9 detection rules (Splunk SPL, Microsoft KQL, Sigma) · Blue and above. Compare plans
21 indicators of compromise · Red and above. Compare plans

Further reading

Threadlinqs Intelligence — Real-Time Threat Detection Platform

[ 0 threats ] [ 0 det ] [ CRIT: 0 ] [ HIGH: 0 ]
// threat_feed
$ sort --newest
Showing all threats

Live intelligence console

Threat level
Fig. 01 · Threat weatherIndexing the archive…
1 square = 1 threat · click to open

Latest Threats