Zscaler ThreatLabz describes how the Shai-Hulud campaign evolved from npm-focused supply-chain compromise into PyPI, CI/CD abuse, IDE configuration attacks, and prompt injection aimed at LLM-based security scanners. The blog links early activity to TeamPCP/UNC6780 and highlights tactics such as OIDC token scraping, .pth persistence, and provenance-bypassing malicious publishing under valid Sigstore attestations. #ShaiHulud #TeamPCP #UNC6780 #PyPI #Sigstore #OIDC
Keypoints
- The campaign expanded beyond npm into PyPI, showing a broader software supply-chain threat pattern.
- Early waves are attributed with high confidence to TeamPCP, tracked by Mandiant as UNC6780.
- Attackers shifted from maintainer compromise to abusing CI/CD workflows, especially GitHub Actions and OIDC-based publishing.
- March 2026 activity used GitHub Actions cache poisoning to compromise Trivy and deploy a malicious Python persistence file,
litellm_init.pth. - May 2026 attacks scraped OIDC tokens from runner memory and used valid provenance to publish malicious packages from trusted infrastructure.
- The campaign later extended into IDE configuration files targeting Claude Code, Cursor, VS Code, and Gemini Code Assist environments.
- June 2026 PyPI malware added prompt injection to mislead LLM-based security scanners and reduce detection confidence.
MITRE Techniques
- [T1195.002 ] Compromise Software Supply Chain – The campaign repeatedly compromised package ecosystems and build pipelines to deliver malicious artifacts (‘the activity expanded beyond npm into the Python Package Index (PyPI)’ and ‘compromised Aqua Security’s Trivy vulnerability scanner through GitHub Actions cache poisoning’).
- [T1059.006 ] Command and Scripting Interpreter: Python – Malicious
.pthfiles executed automatically whenever Python started (‘Python processes all .pth files in site-packages during interpreter startup’). - [T1195.001 ] Compromise Software Dependencies and Development Tools – The attackers poisoned downstream tools and dependencies such as Trivy and package artifacts (‘any pipeline that installed Trivy without a pinned version downloaded and executed the attacker’s binary’).
- [T1606 ] Forge Web Credentials – The attacker scraped an OIDC token and used it to mint a valid publishing token (‘scraped an OIDC token from the Runner.Worker process memory’ and ‘presented to GitHub’s OIDC federation endpoint to generate a valid npm publish token’).
- [T1552.001 ] Unsecured Credentials: Credentials in Memory – The attack extracted tokens from runner memory (‘scraped an OIDC token from the Runner.Worker process memory’).
- [T1204.002 ] User Execution: Malicious File – Opening a repository or running Python triggered the payload in multiple waves (‘Opening project in Claude Code’, ‘Opening folder in VS code’, and ‘Every invocation triggers the payload’).
- [T1027 ] Obfuscated Files or Information – The malware used obfuscated payloads and wrapped JavaScript to hinder analysis (‘multi-layer obfuscation’, ‘obfuscator-wrapped JavaScript payloads’).
- [T1036 ] Masquerading – The campaign used camouflage and misleading naming to blend in (‘staged to route traffic to api.anthropic.com/v1/api, a non-existent endpoint’ and ‘leading-hyphen naming’).
- [T1021.001 ] Remote Services: Remote Desktop/Remote Services – Not mentioned directly; omitted.
- [T1071.001 ] Application Layer Protocol: Web Protocols – The C2 and publishing flows relied on web-based services such as GitHub, OIDC, and staged HTTP endpoints (‘GitHub-based dead drops’ and ‘api.anthropic.com/v1/api’).
- [T1564.001 ] Hide Artifacts: Hidden Files and Directories – The campaign used orphan commits and hidden workflow/configuration files to conceal execution (‘Pushed orphan commits’ and files like
.github/workflows/discussion.yaml,.claude/settings.json). - [T1205 ] Traffic Signaling – The dead-drop C2 used commit markers and signaling strings to retrieve instructions (‘queried GitHub’s commit search API once per hour for the marker string firedalazer’).
- [T1090.002 ] Proxy: External Proxy – Session Protocol was used to help bypass DNS blocking and conceal infrastructure (‘Session Protocol bypasses DNS blocking’).
- [T1055 ] Process Injection – Not explicitly described; omitted.
- [T1091 ] Replication Through Removable Media – Not mentioned; omitted.
- [T1202 ] Indirect Command Execution – IDE hooks and tasks ran embedded commands like
node .github/setup.jswhen a project was opened (‘SessionStart hook’, ‘runOn: folderOpen’). - [T1566 ] Phishing – The initial waves involved AiTM phishing that harvested live TOTP codes (‘Adversary-in-the-Middle (AiTM) phishing harvested live Time-based One-Time Password (TOTP) codes’).
- [T1490 ] Inhibit System Recovery – Not mentioned; omitted.
- [T1574.001 ] Hijack Execution Flow: DLL Search Order Hijacking – Not mentioned; omitted.
- [T1556 ] Modify Authentication Process – The campaign undermined trusted publishing and identity-based workflows by abusing OIDC and CI trust paths (‘abused OIDC trusted publishing following a Red Hat engineer account compromise’).
Indicators of Compromise
- [Files ] Malicious droppers, payloads, and persistence files –
setup_bun.js,litellm_init.pth, and_index.js - [Files ] Backdoor or workflow-related files –
.github/workflows/discussion.yaml,.claude/settings.json, and.vscode/tasks.json - [File hashes ] Known SHA-256 samples from Hades PyPI –
dc48b09b2a5954f7ff79ab8a2fd80202bd3b59c08c7cdbc6025aa923cb4c0efe,e1342a80d4b5e83d2c7c22e1e0aaa95f2d88e3dbf0d853a4994b180c93a4b17d, and 1 more hash - [Domain ] Infrastructure and exfiltration-related domains –
models.litellm.cloudandapi.anthropic.com - [URL ] C2 camouflage destination –
hxxps://api.anthropic.com/v1/api - [Strings ] Download or tasking markers used in the campaign –
oven-sh/bun/releases/downloadandfiredalazer - [Package / artifact names ] Malicious package artifacts and filenames –
litellm_init.pth,-setup.pth, and_index.js