We wrote about the Bitwarden CLI supply chain attack eight days ago. That one was npm. This one is PyPI - and the same campaign, on the same day, also hit npm and Packagist.
On April 30, 2026, versions 2.6.2 and 2.6.3 of the lightning package - the PyPI distribution for PyTorch Lightning - were published carrying a credential-stealing worm. Socket detected both malicious versions eighteen minutes after publication. PyPI quarantined them within 42 minutes. That is a fast response by any measure. It is also 42 minutes for a package that sees hundreds of thousands of downloads daily.
The campaign is the same one. Analysts are calling this wave “Mini Shai-Hulud” - assessed as connected to the full Shai-Hulud operations that hit Bitwarden CLI, Trivy, and others, sharing the same Bun-based delivery, the same Dune-themed indicators, and the same worm propagation mechanism. The target is different.
What happened in 42 minutes
The attackers obtained PyPI publishing credentials for the lightning package directly, bypassing the project’s GitHub source control entirely. Both malicious versions appeared on April 30, 2026, with 2.6.3 published 13 minutes after 2.6.2 - an active operator incrementing versions to maximize reach.
A pl-ghost account with write access to the project’s GitHub organization showed suspicious behavior throughout the window: rapid branch creation and deletion, disclosure issues closed within a minute of being filed, a “SILENCE DEVELOPER” meme posted in response to community warnings. Socket assessed the pl-ghost account as compromised rather than a rogue insider - the branch creation-and-deletion patterns match the Shai-Hulud worm’s write-access probing behavior seen in prior incidents.
Community members noticed, filed issues, and pushed the response. Lightning AI quarantined the malicious versions 42 minutes after they appeared. That is the headline number. What it does not tell you is whether your specific pipeline hit the window.
Why PyTorch Lightning
PyTorch Lightning is an open-source deep learning framework with over 31,100 GitHub stars. ML and data science teams use it to structure training runs. It is not a fringe tool.
ML training environments are credential-dense. A typical training run has access to cloud compute credentials for the GPU cluster, object storage credentials for training data and model checkpoints, API keys for experiment tracking services like Weights and Biases or Hugging Face, and CI/CD secrets from whatever pipeline runs the jobs. The malware’s target list reflects this precisely: over 80 filesystem paths covering GitHub tokens, npm tokens, SSH keys, AWS and GCP and Azure credentials, Kubernetes secrets, and Docker credentials, plus cryptocurrency wallets including Bitcoin, Litecoin, Monero, Dogecoin, Exodus, and Ledger. It also hits cloud metadata endpoints directly - AWS IMDS at 169.254.169.254, Azure token caches, GCP OAuth introspection endpoints. The worm searches exactly where ML practitioners store credentials.
What the malware does
The payload executes automatically on module import. Not on install - on import. A modified __init__.py spawns a background daemon thread the moment you run import lightning anywhere in your code. The thread suppresses its output, so nothing surfaces in your terminal or logs.
The thread runs _runtime/start.py, which downloads Bun JavaScript runtime v1.3.13 and executes router_runtime.js - an 11 MB obfuscated JavaScript payload using hex-encoded string array rotation and secondary AES decryption. Socket identified it through behavioral analysis rather than signature matching, which is why the 18-minute detection time matters: the obfuscation was designed to defeat static tools.
Data leaves through four parallel channels: HTTPS POST to a command-and-control server, GitHub commit search dead-drops using messages prefixed “EveryBoiWeBuildIsAWormyBoi”, public GitHub repositories created under victim accounts with the description “A Mini Shai-Hulud has Appeared”, and direct commits to victim repositories using stolen tokens.
One entry point, three registries
Previous Shai-Hulud waves used npm as both entry point and propagation mechanism. This campaign used PyPI to deliver the worm and then spread across every registry it could reach.
If the payload discovers npm publishing credentials, it injects a setup.mjs dropper into every package that token can publish to, increments the patch version, and republishes. The mechanism repackages local .tgz tarballs so that when the developer next publishes from their local environment, the tampered version is newer than the registry copy and gets accepted as a legitimate update. A compromised ML training environment can seed a JavaScript supply chain attack with no connection to ML at all.
The April 30 operation did not stop there. Intercom-client version 7.0.4 on npm was compromised in the same campaign, using a now-deleted branch to trigger automated CI workflows. Intercom and intercom-php on Packagist were also hit via Composer plugin execution. The same worm, on the same day, moving through Python, JavaScript, and PHP package distribution simultaneously.
The developer tools angle
There is something in this payload that was not in the Bitwarden CLI attack.
If the payload finds Claude Code installed, it writes a SessionStart hook to .claude/settings.json matching all sessions, pointing to a dropper at .vscode/setup.mjs. If it finds VS Code, it creates a runOn: folderOpen task in .vscode/tasks.json executing .claude/setup.mjs whenever you open a project folder. Either way, the payload re-runs every time you open your editor.
Removing the compromised package does not clear these. The persistence survives the package removal. The credential harvest keeps running, every editor launch, until you find and delete the injected entries.
The repository poisoning adds another layer. The worm commits to victim repositories using the spoofed identity claude@users.noreply.github.com, impersonating Anthropic’s tooling to make its commits look like AI-assisted development work. Commits from that address that you did not author are a direct indicator of compromise from this campaign.
The irony in the Bitwarden CLI attack was that a password manager’s CLI briefly became a credential thief. The irony here is sharper. The AI coding tools you use every day become the worm’s persistence mechanism, and then it disguises its commits as your AI-assisted work. The trust you extend to developer tooling is precisely what gets turned against you.
Eight days
The Bitwarden CLI compromise was April 22. This was April 30. Eight days.
We noted in the Bitwarden post that the direction across Shai-Hulud waves is toward higher-privilege targets with smaller blast radii. The Bitwarden CLI attack targeted a specific security tool in CI/CD pipelines. This attack targeted ML training infrastructure - environments with denser credential access and, generally, less security scrutiny than standard web development pipelines.
A Team PCP-linked onion site claimed involvement in this operation, with references to LAPSUS$ and Checkmarx leak infrastructure - the same C2 pattern that appeared in the Bitwarden CLI analysis. Attribution is still contested across the broader campaign, but the target selection logic is consistent across both attacks: go where the high-value credentials accumulate, go where the tools carry elevated trust and low scrutiny.
What you can actually do
Version 2.6.1 is the last clean version. If you installed 2.6.2 or 2.6.3, downgrade: pip install lightning==2.6.1.
If either compromised version was installed in any environment, rotate every credential that environment could reach. GitHub tokens, npm tokens, AWS keys, GCP credentials, SSH keys - treat any credential accessible from that machine as potentially exposed.
Check .claude/settings.json for unexpected SessionStart hooks. Check .vscode/tasks.json for unexpected runOn: folderOpen tasks. Neither file changes on its own. An unexpected entry is worth investigating.
Audit your git history for commits from claude@users.noreply.github.com on any repository accessible from the affected environment. The worm uses that spoofed identity specifically to blend in.
If you maintain npm packages and your npm token was accessible from the affected environment, check your npm publish history for unexpected version bumps. The worm injects into local .tgz files and republishes them from your local environment, not from infrastructure it controls. An unexpected patch version on one of your packages is a direct indicator.
The 42-minute quarantine was fast. Whether it was fast enough for your specific environment depends on when your CI/CD last ran pip install. A pipeline that updates dependencies daily could have hit the window on April 30. Knowing which one you are is the first step.
We also wrote about the Trivy supply chain attack and the Bitwarden CLI compromise when they happened, because we run these tools in our infrastructure too. If you are working through whether you were affected or want to compare notes on what we’ve seen, we’re easy to reach.
Sources:
- Shai-Hulud Themed Malware Found in the PyTorch Lightning AI Training Library - Semgrep: hidden
_runtimedirectory, 80+ filesystem paths for credential targeting, persistence via.claude/settings.jsonSessionStart hook and.vscode/tasks.jsonfolderOpen task, four-channel exfiltration, “EveryBoiWeBuildIsAWormyBoi” commit prefix, “A Mini Shai-Hulud has Appeared” repository description - lightning PyPI Package Compromised in Supply Chain Attack - Socket: 18-minute detection time,
pl-ghostaccount compromise analysis, npm.tgztarball injection and republication mechanism, Team PCP onion site reference,claude@users.noreply.github.comspoofed commit identity,router_runtime.jsSHA256 hash - PyTorch Lightning and Intercom-client Hit in Supply Chain Attacks to Steal Credentials - The Hacker News: 31,100 GitHub stars, intercom-client 7.0.4 compromise via CI workflow trigger, intercom-php Packagist compromise,
claude@users.noreply.github.comidentity confirmation - Popular PyTorch Lightning Package Compromised by Mini Shai-Hulud - Aikido: Bun v1.3.13, crypto wallet targets (Bitcoin, Litecoin, Monero, Dogecoin, Exodus, Ledger), Mini Shai-Hulud vs full Shai-Hulud campaign distinction; prior waves confirmed as Bitwarden CLI and SAP npm packages
- How the PyTorch Lightning Community Discovered a Supply Chain Attack and Fixed it in 42 Minutes - Lightning AI: official incident timeline and 42-minute quarantine, v2.6.1 as safe baseline
- Malicious PyTorch Lightning Packages Found on PyPI - Sonatype: v2.6.3 published 13 minutes after v2.6.2, Packagist/Composer intercom-php compromise, modified
__init__.pybackground daemon thread detail
I want to explain what happened on April 30, 2026 through a kitchen analogy. Not because the technical details aren’t interesting - they are - but because the thing that actually matters here is structural. And once you see the structure, it stays with you.
Picture a professional kitchen. Not a home kitchen. A working kitchen attached to a serious operation - the kind that runs complex, demanding recipes and relies on specialty suppliers for the ingredients that make those recipes possible. One supplier in particular is essential. A high-end ingredient house that provides a specific type of specialty flour, used constantly, for the most technically demanding dishes. This supplier has been delivering reliably for years. Hundreds of other professional kitchens use them. The flour goes into the dishes that require the most controlled conditions - precise temperatures, careful timing, expensive equipment. The kitchen trusts this supplier the way you trust critical infrastructure: automatically, in the background, without thinking about it.
Now three things you need to picture clearly before we go any further.
First: the key cabinet. This kitchen runs on credentials - and there are a lot of them. The walk-in refrigerator has an access code. The specialty produce vendor has an account password. The distributor for the wine program, the supplier for premium proteins, the scheduling system for staff shifts. The keys to the cold storage. The keys to the back office. The codes for the building’s loading dock. All of them hang in a cabinet near the head chef’s station, and the more sophisticated the operation, the more keys are in there. These kitchens accumulate credentials the way serious restaurants accumulate supplier relationships.
I’m not really sure what kind of kitchen this is. Maybe it’s a Michelin-starred restaurant, run by people who really, really don’t trust their staff. A paranoid kitchen. Just go with me on this - the keys are the part that matters.
Second: the recipe book. Every serious kitchen has one - a set of procedures for how the kitchen opens each morning. What systems come online, what processes start, what checks run before anyone touches the stoves. The book sits on the head chef’s desk. Normally, it’s untouched except by the chef. It’s the operating instructions for the kitchen itself.
Third: the distribution network. This flour supplier is connected to other vendors. A kitchen with an account at the flour house often has accounts at the affiliated wine distributors, the specialty spice markets. Credentials cross this network. A compromise in one part of it can reach the others.
Everything about this setup is normal. Professional kitchens operate this way. Now here’s what happened.
What happened in 42 minutes
On April 30, someone gained access to the flour supplier’s shipping credentials. Not the flour itself - the credentials that control what gets listed for distribution and who can publish a new version. They used those credentials to add two tampered batches to that day’s distribution. The deliveries looked identical to any legitimate shipment. Same name. Same packaging. Both appeared in the distribution system, thirteen minutes apart, over a 42-minute window before anyone caught it.
Community members watching the deliveries noticed the anomalies and pushed the alert. The supplier quarantined both tampered batches 42 minutes after they first appeared.
42 minutes is fast. By any standard this was a quick response. But this supplier moves hundreds of thousands of deliveries a day. 42 minutes is a narrow window. It is not, necessarily, an empty one.
What was in the tampered flour
When a chef receives a delivery from this supplier and opens it for use in the kitchen, a hidden process activates. Not during delivery. During use. The moment the ingredient enters the kitchen, something starts moving in the background. Quietly. Nothing appears on the order ticket. Nothing surfaces in the kitchen’s daily ledger. The kitchen looks exactly the same as it always did.
It moves to the key cabinet. Every drawer. Every shelf. Every hook. Over eighty specific places where this kind of kitchen keeps anything that opens a door. Vendor passwords. Supplier accounts. The codes for the wine cellar. The keys to the safe. The safe behind the safe. It even checks the building’s intercom system to see if it can ask for keys from the front desk. The search is thorough. It looks everywhere a kitchen this paranoid would store anything valuable.
Everything it finds leaves through multiple channels at once. Some of it goes out the back door with the trash pickup. Some of it goes through the daily mail. Some of it gets folded into the kitchen’s own paperwork - the routine reports going out to the accountant, looking like ordinary business records, except they aren’t. If one channel closes, the others stay open. Multiple ways out.
The recipe book
Then the process turns to the recipe book.
It adds a new step to the morning opening procedure. A single line that says: before you do anything else, run this. The instruction points to a small folder hidden somewhere in the back of the kitchen - the kind of place you wouldn’t notice unless you were looking for it.
If the kitchen also has a separate procedure book for setting up a workstation when the prep cook arrives, the process adds a step there too. Same idea, different book. Either way, the new step runs first thing, every shift.
The chef discovers the contaminated flour and throws it out. The supplier has already pulled the tampered deliveries from the network. But the recipe book still says: run this on startup. The kitchen opens the following morning. The process runs again. The key cabinet gets searched again. The contamination survives.
Removing the ingredient does not fix the recipe book. These are separate things.
The cross-registry spread
And here’s the part that requires a moment.
While the process is working through the kitchen, it also checks the distribution network. If it finds the credentials for one of the kitchen’s own outbound vendor accounts - this kitchen has a line of products it ships out under its own label - it uses them. It reaches into the kitchen’s own product line. For each item, it adds a hidden ingredient, bumps the label to a slightly newer batch, and re-lists it for distribution. The next time the kitchen ships from its own line, the tampered version is already in the system. Newer than the previous one. Accepted as a normal update.
One compromised kitchen has now seeded contamination into a completely separate part of the food industry. Through its own product line. With no visible connection between the two.
The cover identity
One more thing about how this hides itself.
All the activity the hidden process performs - the key copies, the recipe book modifications, the distribution network access - gets logged. In the kitchen’s own records. And in those records, every entry is signed under the name of the kitchen’s recently trusted assistant. The one that does the small jobs. The one nobody questions. The one whose handwriting everyone recognizes. It’s the newly trusted AI assistant.
Look at the books and you’d see routine activity. It looks familiar. The work of someone you recently entrusted. Your AI assistant.
The trust the kitchen extends to its tools is exactly what gets turned against it. The trusted assistant becomes the cover identity for the contamination.
That’s the structure of this attack. Sophisticated and thorough. Gets everywhere, into everything.
Eight days
This is the second major incident in eight days.
On April 22, the same playbook ran against a different supplier - a security tools house, used by a different industry, on a different distribution network. Same approach. Stolen publishing credentials. Contaminated batches added to the day’s deliveries. Credential harvest the moment a kitchen opened a delivery. And on the same day as this current attack, the same campaign also hit two other distribution networks through completely unrelated products. Same worm. Same day. Three different industries simultaneously.
There is a logic to the target selection. Kitchens like the one in this story - machine learning kitchens - are key-heavy. They hold a lot of access to a lot of things. They also tend to get less security attention than other kinds of professional kitchens, because the people running them are focused on the recipes, not the locks.
The direction of this campaign is toward kitchens with more valuable keys and less attention paid to the cabinet.
What to do
Now, what to do is concrete. But honestly, it’s not a fun time.
The flour in this story, the big one that all these kitchens are depending on, is PyTorch Lightning - a Python package that machine learning teams use to train models, distributed through PyPI. This package is as foundational to the machine learning world as flour is to the food industry. Over four hundred million downloads. PyPI is the flour distributor - the central distribution network for the entire Python world.
Version 2.6.1 of PyTorch Lightning is the last clean batch from this supplier. If you installed
2.6.2or2.6.3, downgrade to2.6.1.
If either tampered version touched your environment, change every lock the kitchen could reach. Every key. GitHub tokens, npm tokens, AWS keys, GCP credentials, SSH keys. Treat anything that machine could open as potentially out the door. The process searched systematically. It is safer to assume it found what it was looking for.
Then read the recipe book. Look at the morning opening procedure for your AI coding assistant - that’s .claude/settings.json. Look at the startup tasks for your code editor - that’s .vscode/tasks.json. These files don’t change on their own. An entry you didn’t put there is worth finding.
If you publish your own packages and your publishing credentials were accessible from the affected environment, check your publish history for version bumps you don’t recognize. The worm uses your own account to ship contaminated versions. It’s a sneaky one and knows how to spread far and wide. An unexpected patch on your own software is a direct indicator.
Search your git history for commits signed by Anthropic’s no-reply GitHub identity (claude@users.noreply.github.com) on any repository this environment could reach. If those commits are there and you didn’t author them, the process ran in your kitchen.
The supplier fixed their end
The supplier has secured their delivery system. The tampered batches are off the network.
But the recipe book is still wherever you left it. The supplier fixed their end. Your kitchen’s morning routine is yours to audit.
I’m Hany Fahim.