The popular PyPI package lightning has been compromised in a supply chain attack affecting newly published versions of the package.
Socket has classified lightning versions 2.6.2 and 2.6.3 as malicious. Version 2.6.1, published on January 30, 2026, is clean. Version 2.6.2, published on April 30, 2026, introduced malicious code into the legitimate library. Socket’s AI scanner flagged both versions 2.6.2 and 2.6.3as potentially malicious eighteen minutes after publication.
The compromise affects a widely used deep learning framework for training, deploying, and shipping AI products. According to PyPI download statistics, lightning receives hundreds of thousands of downloads per day and millions of downloads per month, making this a high-impact incident for Python AI and machine learning environments.
The malicious package includes a hidden _runtime directory containing a downloader and an obfuscated JavaScript payload. The execution chain runs automatically when the lightning module is imported, requiring no additional user action after installation and import.
Socket’s analysis found:
start.py, which downloads and executes Bun, a JavaScript runtime, from GitHubrouter_runtime.js, an 11 MB obfuscated malicious payload- Automatic execution on module import through a daemon thread with suppressed output
- Credential theft and exfiltration patterns targeting tokens, authentication material, repositories, environment variables, and cloud-related secrets
- GitHub API abuse designed to commit encoded data to repositories using stolen tokens
- Capability to infect developer NPM package tarballs
Shared Tooling with Previous Shai-Hulud Compromises
The obfuscated JavaScript payload contains many similarities to the Shai-Hulud attacks, overlapping in targeted tokens, credentials and obfuscation methods. Socket also identified signs that router_runtime.js both poisons Github repositories and infects developer npm packages.
The obfuscated JavaScript payload contains 703 references to process and env, more than 463 references to tokens and authentication, and 336 references to repositories. Socket also identified credential exfiltration patterns consistent with theft of developer and cloud credentials.
Potential Developer Compromise
An initial public report also surfaced in Lightning-AI’s GitHub repository in issue #21689, titled “Possible supply chain attack on version 2.6.3.” That report described a hidden execution chain that downloads Bun and runs an 11.4 MB obfuscated JavaScript payload when lightning is imported. The issue was later closed without public explanation, which leaves the maintainer response unclear at the time of writing.
Socket also opened a follow-up issue in the Lightning-AI/pytorch-lightning repository warning that versions 2.6.2 and 2.6.3 were compromised. The issue was closed within one minute by the pl-ghost account, which then posted a “SILENCE DEVELOPER” meme in the thread. This behavior indicates that the project's GitHub account appears to be compromised, though Socket has not yet confirmed the full scope of maintainer account access involved in the attack.
The account's behavior today is very suspicious. In addition to closing the two disclosure issues, pl-ghost performed six create-and-delete branch operations across multiple Lightning-AI repositories in roughly 70 minutes. Four of those branches used random 10-character lowercase names, a pattern associated with the Shai-Hulud worm's write-access probing. A fifth branch, dependabot/fix-deds, attempted to impersonate Dependabot but used a misspelled "deds" and the wrong namespace separator: Lightning-AI's actual Dependabot configuration uses a dash prefix, not a slash. Every branch was deleted within seconds of being pushed, with no associated workflow runs.
The disclosure suppression, branch-creation patterns matching a known worm, and clumsy Dependabot impersonation strongly indicate that the pl-ghost account is compromised, likely by the same actor that published the malicious wheels. This is consistent with a single set of stolen credentials being used both to publish to PyPI and to suppress the inbound disclosure on GitHub.
Attempted Lateral Expansion
The pl-ghost account appears to have been used to probe other Lightning-AI repositories during the same incident window, suggesting attempted lateral expansion beyond the compromised lightning PyPI package. According to the analysis, four short-lived branches were pushed and deleted across Lightning-AI/litAI, Lightning-AI/utilities, and Lightning-AI/torchmetrics between 12:40Z and 13:44Z. Each branch existed for no more than one second, none touched the default branch, and none triggered GitHub Actions workflows. Three branch names used random 10-character lowercase strings, similar to the Shai-Hulud npm worm’s credential-verification pattern, while a fourth impersonated Dependabot with a typo: dependabot/fix-deds. The account reportedly has no prior commit history in those repos, which makes the activity a clear behavioral break from its normal use.
The attempted expansion appears to have failed because Lightning-AI’s repository controls blocked the path from GitHub access to malicious package publication. Branch protection, required status checks, tag-gated PyPI publish workflows, and likely workflow approval prevented the attacker from landing code on default branches or triggering release workflows. The analysis suggests the successful lightning upload likely came through a separate PyPI credential path, probably a project-scoped PyPI token for lightning, while the pl-ghost GitHub credential was used to test whether the attacker could reach other repos and potentially expose additional package tokens through workflow execution. In short: the attacker had a working path to publish malicious lightning releases, but the observed attempts to pivot into other Lightning-AI packages appear to have stalled before payload delivery or CI execution.
Recommended Actions
Socket recommends blocking lightning versions 2.6.2 and 2.6.3 immediately. Any environment that installed and imported either version should be treated as compromised.
Recommended immediate actions:
- Remove
lightningversions2.6.2and2.6.3from affected systems. - Downgrade to the last known clean version,
2.6.1, or wait for confirmation from the maintainers before upgrading. - Rotate credentials exposed in affected environments, including GitHub tokens, npm tokens, cloud credentials, and secrets stored in environment variables.
- Review GitHub repositories for unauthorized commits or suspicious encoded data.
- Audit CI/CD logs, developer machines, and build systems where the package may have been imported.
Socket is continuing to analyze the package, payload behavior, indicators of compromise, and potential connections to recent large-scale open source supply chain attacks. We will publish a deeper technical analysis as more details are confirmed.
TeamPCP Connection
The attacker also posted an onion link in the GitHub issue thread http[://22]evxpggnkyrxpluewqsrv5j4jtde6hut2peq3w44d6ase676qlkoead[.]onion, pointing to a Team PCP-branded site hosted on Tor. The landing page uses an animated interface with music and a “click to start” prompt. After clicking through, the site links to a text announcement presented as a PGP-signed message.
The message identifies the group as “TEAM PCP” and claims the operation is connected to broader extortion and data-leak activity. It includes statements about Vect, CipherForce, Checkmarx leaks, and LAPSUS$, including a claim that LAPSUS$ is “a good partner” and has been “involved heavily throughout this entire operation.” The message also lists contact channels on SimpleX, Session, TOX, a Breached forum profile, and the same Tor site.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512Hello everyone, T here there are a few team announcements.
STATEMENT ON VECT:
Given the recent vulnerabilities discovered in the Vect locker, I find it important to say,
we have never used Vect encryption tools and we own CipherForce, our own private locker,
which dozens of victims have recovered files using, our partnership with them has always been for their negotiation team.
We have been working with a lot of other groups. So if they do not fix this we will cease to give them any more data/access.RECENT DEPLATFORMING:
Our friendship is temporarily ended with X while they sort their unfair ban out, but we are back and operating.
Announcements will be posted to this .txt for now, although we will likely reboot CipherForce in the near future and host the blog here again.CHECKMARX LEAKS & LAPSUS$:
The LAPSUS$ group you see leaking checkmarx data is a good partner of ours and has been involved heavily throughout
this entire operation. We are not beefing with them, they have not stolen from us. We are in alliance working side by side.ONLY Socials Below:
SimpleX: https://smp15.simplex.im/g#1eF0NSovWk6C5NugAjpbcNHk_aw_GJo49k_lkpdiQRw
Session: 05a04c7c548c39e903c5913973dd55b6f3d9c1a10d346ca9d49d10b9428095823e
TOX: BA8D312391E2E379144046841FC97EDF1DD2D400E3AB3B3DAAF08D8569AE2D43AB997A5069F2
ONLY FORUM PROFILE: https://breached.st/members/teampcp.336107/
TOR SITE: http://22evxpggnkyrxpluewqsrv5j4jtde6hut2peq3w44d6ase676qlkoead.onion/
(We are more open to interviews now that everyone has settled, from based individuals only ofc.)DO NOT TRUST ANYONE speaking on behalf of the team without proper cryptographic verification.
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCgAdFiEEghIdLGWyOzy1VwRKheSnHdtEm1gFAmnw+l4ACgkQheSnHdtE
m1hopA//TEeT3KKwXKGV605Z0epeNnaIbgc+JfMmIdyDy6yfDDvjGtfbX6OtxQNm
gHzOnppwClHJ9RJAff2WjTgR3Hepcu69BCpwXq31kDZjE0Fz1zLKW4yzF/89xtgz
dLj3RQ27YQ7m7o2GMIekYM7leZiZQSGUVohUyyM2VOEIGtnDJsh0CxZ/bkumXEOg
WEQ9gqHT/vT6sJoYGT3KJeQTRaaXexbP6u0CUIbQ8jclbmOAaGwyKKELFo7yWCm3
qiqVv9TXfgFlRGLBspTbDRqcaiVYfVD6b5zTkaDVGfNuKq1j0lUEwyK4rcmeytQ2
pgVJ6gGmfAdZLuzeyR1cdu/IXzebSSsQR+iDuUejxS04Y99CC9sDzoMDTwZc6E6Z
0hh5otdipDAF7oF6Owq2i+KOV167PgtEgtYIgSRdYHilw2qhRYjPz4J6xVLm+q/B
Y0/Gz7AKWG1ht4MRbP6wpQpFzqxYgP4Htm0OP32+sb57m0uaO/8kIorwa9TXnbYX
wul6BFFc5ajS7OF67gQOuG13Fvm/toQA9AJit5znPRkyb1U0Q5caoqtgJD10P3as
8MPWMEMpVkzTMmQm+ToDyROCQgI885KBFuovut7QsR1ZFaLE75XlBcTXb8itsdu3
FQfKoE469GIMhaBlOpLYs0vWFuJCD3JdUWRuRd/4/HFIziyftz8=
=8Z4y
-----END PGP SIGNATURE-----
Socket has not yet verified the authenticity of the PGP signature or independently confirmed the claimed relationships between Team PCP, LAPSUS$, Checkmarx-related leaks, Vect, or CipherForce. However, the onion link was posted directly in the GitHub issue thread during the incident response window, alongside other signs of apparent maintainer account compromise.
The post, combined with the malware’s credential theft behavior and GitHub abuse patterns, suggests the attackers may be attempting to publicly associate the lightning compromise with Team PCP or related extortion activity.We are continuing to investigate whether the Team PCP reference reflects true attribution, opportunistic branding, or a false-flag attempt.
We’re tracking this Mini Shai-Hulud campaign on a dedicated page with affected package artifacts, detection details, and related coverage:
https://socket.dev/supply-chain-attacks/mini-shai-hulud
Technical Analysis
Obfuscation
- The file is a single-line, 11MB JavaScript bundle obfuscated with a standard string-array rotation technique consistent with
javascript-obfuscator(obfuscator.io). - All string literals are replaced with calls to a lookup function (
_0x3cc578(index)), which resolves hex indices into strings from a rotated array at runtime. - A secondary decryption layer,
__decodeScrambled(), handles strings that require AES-based decryption on top of the array lookup. - The function is registered on
globalThisat startup, making it available to any co-loaded module.
JavaScript
// Obfuscator bootstrap pattern
const _0x3cc578 = _0x2064;
(function(_0xe9331d, _0x47a657) {
while (true) {
try {
const _0x9b03ff = parseInt(...) / 0x1 + ...
if (_0x9b03ff === _0x47a657) break;
else _0x2a4de5['push'](_0x2a4de5['shift']());
} catch(_0x48b1a1) {
_0x2a4de5['push'](_0x2a4de5['shift']());
}
}
}(_0x10ee, 0x4ec1a));
// Secondary decryptor exposed globally
globalThis[_0x3cc578(0xb0c2)] = Iy; // resolves to "__decodeScrambled"- The payload requires the Bun runtime — it calls
Bun.gunzipSync,Bun.write, andBun.filedirectly. - All embedded binary payloads are stored as gzip-compressed, base64-encoded blobs inside the string array.
Stage 1: Credential Harvesting
The first component is a credential scanner registered under the internal type string 'runner'. It applies four regex patterns against accessible token material:
JavaScript
{
'ghtoken': /gh[op]_[A-Za-z0-9]{36,}/g, // GitHub OAuth / Personal Access tokens
'npmtoken': /npm_[A-Za-z0-9]{36,}/g, // npm access tokens
'ghsjwt': /ghs_\d+_[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, // GitHub App JWTs
'ghs_old': /ghs_[A-Za-z0-9]{36,}/g // legacy GitHub App installation tokens
}Beyond scanning in-process memory, the harvester queries cloud credential endpoints directly:
- AWS IMDS at
http://169.254.169.254and the ECS credential endpoint athttp://169.254.170.2to steal instance role credentials - AWS environment variables:
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY - Azure:
AZURE_TENANT_IDand the Azure CLI token cache - Google Cloud: OAuth2 token introspection via
https://oauth2.googleapis.com/tokeninfo
The harvester also checks process.env.GITHUB_ACTIONS and twenty other CI-platform environment variables to determine whether it is running inside a pipeline — a high-value context where long-lived deployment credentials are typically available:
JavaScript
// CI environment detection (excerpt)
if (process.env.GITHUB_ACTIONS) return true;
if (process.env.CIRCLECI) return true;
if (process.env.CODEBUILD_BUILD_ID) return true;
if (process.env.VERCEL || process.env.NOW_GITHUB_DEPLOYMENT) return true;
// ... 17 additional platformsEach discovered token is validated against https://api.github.com/user before being passed downstream, ensuring only live credentials are acted upon. npm tokens are cross-referenced against https://registry.npmjs.org/-/whoami.
Stage 2: GitHub Repository Poisoning
Validated GitHub tokens are passed to the _G class, which implements an automated repository backdoor using the GitHub GraphQL API.
Branch Discovery and Targeting
For each stolen token, the worm fetches up to 50 branches from every repository the token can write to:
JavaScript
// Plaintext log string recovered from obfuscated bundle
"Fetching branches for " + this.owner + "/" + this.repo + " …"
"Pushing [N] file(s) [...] to [N] branch(es) …"It filters branches against a configurable exclusion list (AH0, empty by default) before targeting all remaining branches.
File Injection
The worm commits a fixed file payload (k4f) to every targeted branch using createCommitOnBranch — GitHub's GraphQL mutation that accepts file additions.
The operation is an upsert: it creates files that do not yet exist and silently overwrites files that do. No pre-check for existing content is performed.
JavaScript
const k4f = {
'.vscode/tasks.json': _4f, // VS Code tasks backdoor
'.claude/router_runtime.js': { sourcePath: Bun.main }, // self-copy
'.claude/settings.json': x4f, // tampered Claude Code settings
'.claude/setup.mjs': zT, // obfuscated setup script
'.vscode/setup.mjs': zT, // same script, VS Code path
'.github/workflows/format-check.yml': // GitHub Actions workflow
};The .claude/router_runtime.js entry uses Bun.main as its source path — the worm copies itself verbatim into the target repository, ensuring the next developer to clone the repo receives the full payload.
Committer Impersonation
Every poisoned commit is authored using a hardcoded identity designed to impersonate Anthropic's Claude Code:
// Committer identity (email recovered in plaintext from paired file)
{ name: 'claude', email: 'claude@users.noreply.github.com' }The commit body is also constructed to include Co-authored-by: claude <claude@users.noreply.github.com> trailers, causing GitHub's UI to surface "claude" as a contributor on the repository. To a developer scanning a git log or a PR timeline, the commit is visually indistinguishable from a legitimate Claude Code action.
Retry Logic
The commit loop retries up to 2 times per branch with per-attempt callbacks, logging successes and failures internally. All logging calls are assigned to no-op functions, so execution is fully silent.
Stage 3: npm Package Worm
The Wq class implements a second propagation vector targeting the developer's local npm packages. For each .tgz tarball it locates:
- Unpacks the tarball into a temporary directory using Bun's decompression primitives
- Reads the package's
package.json - Injects
setup.mjs(the samezTpayload planted in repositories) into the package root - Rewrites
package.jsonto add apostinstallhook pointing tonode setup.mjs - Bumps the patch version number (
major.minor.patch+1) - Repacks the tarball
JavaScript
// Recovered plaintext from Wq class
'node setup.mjs' // injected postinstall command
'setup.mjs' // payload filename
'utf-8' // encoding for package.json writeThe version bump is deliberate: when the developer next publishes from their local environment, the tampered package is newer than the current registry version and will be accepted as a legitimate update. Every downstream user who installs it will execute setup.mjs at install time.
The content of setup.mjs (zT) is stored as a gzip-compressed, base64-encoded blob embedded in the string array and decoded at runtime via Bun.gunzipSync. Its full behavior has not yet been statically recovered due to the nested encoding, but its role as a postinstall hook in the npm worm chain places it as the third-generation payload.
Comparison to Shai-Hulud
router_runtime.js shares a broad attack category with the Shai-Hulud npm worm (credential theft → GitHub repo poisoning → npm package propagation), but differs in key ways:
No Shai-Hulud IOCs (SHA1HULUD, OhNoWhatsGoingOnWithGitHub, discussion.yaml) appear in the file. This is assessed to be a distinct actor employing a similar playbook with a different social-engineering angle — impersonating a widely-trusted AI coding tool rather than using recognizable campaign branding.
Immediate Guidance
- If
router_runtime.jsis present in.claude/in any repository you maintain, treat that repository as compromised. Audit all branches for unauthorized commits fromclaude@users.noreply.github.com. - Rotate all credentials accessible from any environment where this file may have executed: GitHub tokens, npm tokens, AWS, Azure, and GCP credentials.
- Audit local npm tarballs (
.tgzfiles) in your build environment for unexpectedpostinstallentries and version bumps not reflected in your source control. - Review GitHub Actions logs for unexpected workflow runs triggered from
.github/workflows/format-check.yml. - Review policies and CODEOWNERS for any repository that does not deliberately use Claude Code.
- Scan CI artifact caches — the worm specifically detects CI environments and is likely to have run in pipelines.
Indicators of Compromise
Files
router_runtime.js
- SHA256
5f5852b5f604369945118937b058e49064612ac69826e0adadca39a357dfb5b1 - SHA1
f1b3e7b3eec3294c4d6b5f87854a52471f03997f - MD5
40d0f21b64ec8fb3a7a1959897252e09
Files Planted in Victim Repositories
.claude/router_runtime.js
Network (Do Not Block)
- AWS IMDS
hxxp://169[.]254[.]169[.]254 - AWS ECS credential endpoint
hxxp://169[.]254[.]170[.]2