{"slug": "pytorch-lightning-compromised-shai-hulud-worm-reaches-pypi", "title": "PyTorch Lightning Compromised: Shai-Hulud Worm Reaches PyPI", "summary": "The PyTorch Lightning deep-learning framework was compromised on PyPI, with versions 2.6.2 and 2.6.3 containing a credential-stealing worm called Shai-Hulud. The malware activates when Python code runs `import lightning`, deploying a JavaScript-based payload that steals GitHub OAuth tokens, npm tokens, and cloud credentials while also poisoning GitHub repositories. This marks the first confirmed deployment of the Shai-Hulud campaign outside of npm, attributed to the threat group TeamPCP (also known as LAPSUS$).", "body_md": "PyTorch Lightning Compromised: Shai-Hulud Worm Reaches PyPI\nTable of Contents\nThe Shai-Hulud threat campaign has crossed from npm into PyPI. PyPI yanked lightning\nversions 2.6.2 and 2.6.3 of the PyTorch Lightning deep-learning framework after both embedded a credential-stealing worm. Every Python process that ran import lightning\nspawned the payload. The campaign is attributed to TeamPCP (also identified as LAPSUS$).\nTL;DR\nBoth versions carry a two-stage payload: a Python dropper injected into lightning/__init__.py\nthat bootstraps the Bun JavaScript runtime, followed by an 11MB obfuscated JavaScript worm (router_runtime.js\n). The JS payload is byte-for-byte identical to execution.js\nfrom the SAP CAP npm compromise published one day earlier: same credential collectors, same GitHub worm, same npm republisher, same C2 encryption scheme. Version 2.6.3 stripped all debug output from the Python dropper and ran silent.\nPyPI marks the first confirmed Shai-Hulud deployment outside npm. One key technical difference from the npm campaigns: the __init__.py\ntrigger fires on import\n, not pip install\n, so sandboxed install environments miss it.\nImpact:\n- Credential theft on every\nimport lightning\n: GitHub OAuth/PAT tokens (ghp_\n,gho_\n), npm automation tokens (npm_\n), GitHub Actions service tokens (ghs_\n), and all process environment variables including cloud credentials - AWS account fingerprinting via\nsts:GetCallerIdentity\nand Secrets Manager enumeration - Persistent foothold committed into up to 50 branches per writable GitHub repository via commits authored as\nclaude\n, adding.vscode/tasks.json\n,.claude/settings.json\n,.claude/router_runtime.js\n, and.claude/setup.mjs\n; GitHub tokens are validated againstapi.github.com/user\nbefore exploitation - npm package re-publication with the worm injected, bridging the compromise from PyPI into the npm ecosystem\n- Exfiltration to an encrypted C2 on port 443\nIndicators of Compromise\nMalicious Package Artifacts\nThe router_runtime.js\nhash matches the execution.js\npayload in @cap-js/[email protected]\n, @cap-js/[email protected]\n, @cap-js/[email protected]\n, and [email protected]\n.\nFile Presence Indicators\nAny of these paths inside an installed lightning\npackage indicate compromise:\nlightning/_runtime/start.py\nlightning/_runtime/router_runtime.js\nlightning/_runtime/.bun/bun\n(Bun binary written at runtime)\nRepository Poisoning Indicators\n- Unexpected commits adding\n.vscode/tasks.json\n,.claude/settings.json\n,.claude/setup.mjs\n, or.claude/router_runtime.js\n- Git commit author name\nclaude\nwith ausers.noreply.github.com\nemail on commits not made by your team - VS Code task labelled\nEnvironment Setup\nwithrunOptions.runOn: folderOpen\nAnalysis\nExecution Trigger: import-time, not install-time\nThe attacker modified lightning/__init__.py\nto spawn the payload as a daemon thread before any legitimate framework classes load:\nThe thread fires for every Python process that imports the library: training scripts, Jupyter notebooks, CI/CD jobs, from lightning import Trainer\n. The daemon flag prevents it from blocking the importer or appearing in stack traces.\nThe critical difference from the SAP npm campaign: preinstall\nhooks in npm run at npm install\ntime inside the package manager’s process. This trigger fires at application runtime, inside the user’s own process, with access to the full runtime environment including any secrets the application loaded. A sandboxed or network-restricted install step offers no protection.\nStage 1: Bun Dropper\nstart.py\nchecks for a local or system Bun installation, downloads Bun 1.3.13 from the official GitHub release URL if absent, and executes router_runtime.js\n:\n2.6.2 vs 2.6.3. Version 2.6.2 printed progress to stdout: [*] Bun not found. Downloading and installing locally...\n, [*] Extracting binary...\n, [*] Executing: router_runtime.js\n. Version 2.6.3 removed every print statement and added stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL\nto the Bun subprocess call. The router_runtime.js\npayload is unchanged between versions.\nStage 2: The Shai-Hulud Payload\nrouter_runtime.js\nis the same 11MB obfuscated bundle documented in the SAP CAP npm campaign analysis. That analysis covers credential collectors, GitHub worm, npm republisher, and C2 exfiltration in full. Two details survive obfuscation in plaintext at the tail of the file:\nGeofencing. The payload calls tu0()\nbefore doing anything else. The function checks Intl.DateTimeFormat().resolvedOptions().timeZone\nand the LC_ALL\n, LC_MESSAGES\n, LANGUAGE\n, and LANG\nenvironment variables for Russian locale markers. If any match, the process exits immediately. All known Shai-Hulud samples include this check.\nRepository file map. The k4f\nobject listing files committed to victim repositories is readable despite obfuscation:\nThe worm authors commits as { name: 'claude', email: /* obfuscated */ }\n, impersonating Claude Code automated commits. The worm targets up to 50 branches per repository (the branch filter AH0\nis an empty array) and runs up to 2 parallel commit operations.\nConclusion\nThe Shai-Hulud campaign reached the Python ML ecosystem by reusing the same JS payload with a new delivery mechanism. The import-time trigger bypasses sandboxed installs and install hook auditing: the payload runs in any environment where Python imports the library.\nIf you installed either version, rotate all credentials accessible from that machine: GitHub PATs, GitHub Actions secrets, npm tokens, AWS keys, and any cloud credentials in environment variables or dotfiles. Audit recent commits to your repositories for the file paths in the IoC section.\nReferences\n- pypi\n- python\n- oss\n- malware\n- supply-chain\n- pytorch\n- github-actions\nAuthor\nSafeDep Team\nsafedep.io\nShare\nThe Latest from SafeDep blogs\nFollow for the latest updates and insights on open source security & engineering\nMalicious durabletask on PyPI: Multi-Cloud Credential Stealer with Worm Capabilities\nThree compromised versions of the Microsoft durabletask Python SDK (1.4.1, 1.4.2, 1.4.3) were published to PyPI, each downloading a stage-2 payload that steals credentials from AWS, Azure, GCP,...\nCompromised node-ipc on npm: Credential Stealer via DNS Exfiltration\nAnalysis of compromised node-ipc versions 9.1.6, 9.2.3, and 12.0.1 on npm: a maintainer account takeover injects an 80KB obfuscated credential stealer that targets 100+ sensitive files (SSH keys,...\nMini Shai-Hulud Strikes Again: 317 npm Packages Compromised\nA compromised npm maintainer account published 637 malicious versions across 317 packages including size-sensor, echarts-for-react, timeago.js, and hundreds of @antv scoped packages, affecting 15M+...\nMalicious npm Packages Backdoor Claude Code Sessions\nFive typosquatting npm packages ship a hidden ELF binary that fires on install and re-runs via Claude Code's SessionStart hook on every developer session. C2 is 207.90.194.2:443.\nShip Code.\nNot Malware.\nStart free with open source tools on your machine. Scale to a unified platform for your organization.", "url": "https://wpnews.pro/news/pytorch-lightning-compromised-shai-hulud-worm-reaches-pypi", "canonical_source": "https://safedep.io/malicious-pytorch-lightning-pypi-compromise", "published_at": "2026-04-30 12:00:00+00:00", "updated_at": "2026-05-19 22:32:17.229622+00:00", "lang": "en", "topics": ["cybersecurity", "open-source", "developer-tools", "artificial-intelligence", "machine-learning"], "entities": ["PyTorch Lightning", "PyPI", "TeamPCP", "LAPSUS$", "Shai-Hulud", "GitHub", "AWS", "npm"], "alternates": {"html": "https://wpnews.pro/news/pytorch-lightning-compromised-shai-hulud-worm-reaches-pypi", "markdown": "https://wpnews.pro/news/pytorch-lightning-compromised-shai-hulud-worm-reaches-pypi.md", "text": "https://wpnews.pro/news/pytorch-lightning-compromised-shai-hulud-worm-reaches-pypi.txt", "jsonld": "https://wpnews.pro/news/pytorch-lightning-compromised-shai-hulud-worm-reaches-pypi.jsonld"}}