GlassWorm Is Hiding Malware in Your Python Code Right Now Using Invisible Characters. Here Is How to Find It

18 min read 796 views

Sources: StepSecurity Threat Intelligence (original discovery, March 16, 2026), SecurityWeek, Bleeping Computer, The Hacker News, Aikido Security, Socket. Campaign confirmed active as of March 17, 2026.

Table of Contents

Hacker in dark room representing GlassWorm supply chain attack targeting Python GitHub repositories in 2026

This Attack Is Happening Right Now and You May Already Be Affected

On March 16, StepSecurity’s threat intelligence team published a report on something they had been tracking since March 8. An active malware campaign had compromised hundreds of GitHub developer accounts and injected hidden malicious code into hundreds of Python repositories. The attack is still running. New repositories are still being hit as of today, March 17.

If you have run pip install from a GitHub repository in the last ten days, cloned and executed a Python project from GitHub, or installed a VS Code extension from the Open VSX marketplace at any point in the last six months, there is a non-zero chance you need to read this carefully and check your machine. That is not hyperbole. That is the specific advice from the security researchers who discovered and are actively monitoring this campaign.

The attack is called GlassWorm, and this is not its first appearance. It has been running in waves since October 2025. What changed this month is the scale and the technique. The attackers moved from targeting VS Code extensions to taking over developer GitHub accounts directly and poisoning source code repositories that other developers then unknowingly pull into their own projects. The method they used to hide the malicious code is technically sophisticated in a way that is worth understanding, because it exploits something about how text is rendered in every major code editor, terminal, and code review interface on earth.

What GlassWorm Actually Is and How Long It Has Been Running

GlassWorm first appeared in October 2025 as a supply chain attack against the Open VSX marketplace, which is the extension registry used by VS Code forks like VSCodium, Cursor, and other editors that do not use Microsoft’s official marketplace. The malware was embedded inside legitimate-looking VS Code extensions and downloaded by developers who had no reason to suspect anything was wrong. Security researchers estimated the initial wave was downloaded over 35,000 times before it was contained, which took about three days.

A second wave hit in November 2025, infecting three VS Code extensions with a combined download count of around 10,000. Because VS Code extensions update automatically, the malware reached all users of those extensions without requiring any additional action on their part. The extension updated in the background and the malicious code was present the next time the editor launched.

A third wave came in late January 2026. Four more extensions were compromised, with over 22,000 combined downloads. Each time, the malware was designed to steal credentials, specifically GitHub tokens, NPM tokens, SSH keys, and Git credentials, and to exfiltrate cryptocurrency wallet data. The campaign was attributed to a consistent threat actor throughout based on the reuse of specific infrastructure.

Then in early March 2026, the same actor escalated. Instead of just compromising extensions and waiting for developers to install them, GlassWorm started using the GitHub credentials it had already stolen from those previous waves to directly access developer accounts and poison their repositories from the inside.

The Invisible Code Trick: Unicode Characters That Render as Nothing

The name GlassWorm comes from the technique the malware uses to hide itself inside source code. It exploits specific ranges of Unicode characters, specifically the Unicode Private Use Area ranges U+FE00 through U+FE0F and U+E0100 through U+E01EF. These characters are called variation selectors and they are defined in the Unicode standard as modifiers that adjust the appearance of the preceding character. In practice, in most modern rendering environments, they display as zero-width whitespace. They are invisible.

The malicious code is encoded using these invisible characters and embedded directly inside otherwise legitimate Python files. When a developer opens the file in VS Code, looks at it in a terminal, reviews it on GitHub’s web interface, or runs any standard code review tool, they see nothing unusual. The visible code looks exactly like what they expect. The malicious instructions are sitting right there in the file, encoded as characters that every tool treats as whitespace.

A hidden decoder embedded elsewhere in the file extracts the bytes from these invisible characters and passes them to JavaScript’s eval() function, which executes the second-stage payload. The entire mechanism is designed specifically to defeat code review. You could stare at the file for an hour and not see it. Automated static analysis tools that work at the visible character level will not find it either unless they have been specifically updated to look for variation selectors used in this way.

Active threat: This campaign is still running as of March 17, 2026. StepSecurity confirmed new repositories are being compromised daily. If you installed VS Code extensions from the Open VSX marketplace between October 2025 and now, or cloned Python repositories from GitHub in the last ten days, check your machine using the indicators in the “How to Check” section below before continuing your workday.

ForceMemo: How They Got Into GitHub and Rewrote Your Commit History

The GitHub account takeover phase of this campaign is tracked separately under the name ForceMemo, named after the specific technique used to inject the malicious code. Here is how the attack chain works from start to finish.

First, GlassWorm malware from a compromised VS Code extension steals the developer’s GitHub token and other credentials. This happened silently to thousands of developers across the previous attack waves from October 2025 through January 2026. Many of those developers do not know their tokens were stolen because the malware is designed to exfiltrate quietly without triggering any visible behavior change.

Second, the attacker uses those stolen tokens to authenticate to GitHub as the developer. GitHub sees a valid token and treats the access as legitimate. No login alert fires because no password was entered. The token is sufficient.

Third, the attacker identifies all repositories the compromised account has write access to, including not just personal repos but any organization repositories, open-source projects, and collaborative projects the developer contributes to.

Fourth, the attacker injects obfuscated malicious code into Python files in those repositories. Then, critically, they rebase the injection onto the most recent legitimate commit and force-push it to the default branch. This rewrites the repository’s Git history in a specific way: the original commit message, author name, and author date are all preserved exactly. Only the committer date, which is a less visible metadata field, is updated to the current time.

From GitHub’s web interface, the commit looks exactly like the developer’s original work. The commit message is unchanged. The author is unchanged. The timestamp shown in most views is unchanged. There is no pull request. There is no diff highlight showing new code. StepSecurity specifically noted that no other documented supply chain campaign has used this exact injection method before ForceMemo. It is a novel technique specifically designed to survive code review by looking identical to legitimate history.

Python code on a dark terminal screen representing the GlassWorm malicious code injection in GitHub repositories

Using a Blockchain as a Command Server: The Solana C2 Trick

One of the most technically interesting aspects of GlassWorm is how it receives instructions from the attacker. Most malware communicates with a command-and-control server hosted on a conventional server somewhere, which creates a vulnerability for defenders: find the server, block the IP or domain, and the malware goes silent. This attacker chose a different approach.

GlassWorm queries a Solana blockchain wallet address for its command-and-control instructions. The specific address is BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC. The Solana blockchain allows wallet transactions to include a small memo field, and the attacker uses this field to post JSON instructions. The malware running on a compromised machine queries this Solana address every five seconds via the public Solana RPC API, reads the latest memo field, decodes the base64-encoded URL inside it, and connects to that URL to fetch the current payload.

The reason this is clever from an attacker’s perspective: the blockchain itself cannot be taken down, blocked, or seized in the way a conventional server can. The Solana network is distributed across thousands of nodes worldwide. Blocking access to the Solana RPC API is theoretically possible at a network level but would affect legitimate uses of the network and is not a standard defensive operation. The attacker can update the payload URL as often as they want, and every infected machine picks up the change within five seconds. StepSecurity observed 50 separate transactions on this address between November 27, 2025 and March 13, 2026, with the attacker updating the payload URL multiple times per day.

This architecture means the actual payload servers, which are conventional hosted infrastructure that can be blocked and taken down, are effectively disposable. The attacker rotated through six different server IPs over the campaign’s lifetime. When one gets blocked, a new memo in the Solana wallet sends all infected machines to the next one. The blockchain acts as a permanent, uncensorable redirect service.

What the Malware Actually Does Once It Is on Your Machine

Once GlassWorm executes on a compromised machine, it operates on multiple simultaneous objectives. The primary credential theft targets are GitHub personal access tokens, SSH keys, NPM authentication tokens, and Git credentials stored in the developer’s environment. These are the assets that give access to more repositories, which is how the campaign self-propagates: credentials stolen from infected machines are used to compromise more repositories, which infect more developers, whose credentials are then also stolen.

Cryptocurrency is a significant secondary target. The malware scans for cryptocurrency wallet files and browser-based wallet extensions. The JavaScript payloads downloaded from the C2 server include specialized routines for extracting wallet seeds and private keys from common wallet software. Given that many developers working on blockchain and Web3 projects keep development wallets on their machines, this represents a direct financial theft vector rather than just credential theft.

Beyond stealing things, the malware also installs persistence mechanisms. It places an init.json file in the home directory that allows it to survive reboots. It installs a hidden Node.js runtime, typically in a subdirectory of the home folder, that runs the JavaScript payload components. In some cases it deploys SOCKS proxy servers that route other traffic through the compromised machine, effectively using the developer’s machine as infrastructure for other attack operations. Hidden VNC servers have also been observed, giving the attacker potential remote interactive access to the compromised system.

The malware includes one notable exception in its targeting. It checks whether the system’s locale is set to Russian. If it is, execution halts and the malware does nothing. This is a standard pattern in Russian-speaking cybercriminal circles to avoid liability under laws that apply when attacks target domestic victims. It is not conclusive attribution to a specific nation or group but it is consistent with threat actors operating in certain jurisdictions.

Who Is Being Targeted and Why Python Specifically

The ForceMemo phase of GlassWorm specifically targeted Python repositories, and the choice is not arbitrary. Python is the dominant language for machine learning research, AI tooling, data science, and infrastructure automation. The specific project types named in StepSecurity’s report are Django web applications, ML research code, Streamlit dashboards, and PyPI packages.

The targeting logic becomes clear when you consider the supply chain implications. A compromised ML research repository at a university or research lab might be cloned by dozens or hundreds of other researchers. A compromised PyPI package could be installed by thousands of developers who have never heard of the original project and have no reason to audit it. A compromised Streamlit dashboard shared as a portfolio project or demo gets run by anyone who encounters it. The attack is not trying to hit individuals one at a time. It is trying to establish presence across as many developer environments as possible as quickly as possible.

npm packages were also hit in this wave. StepSecurity discovered on March 16 that two popular React Native packages, react-native-international-phone-number and react-native-country-select, were briefly compromised with malicious versions pushed directly to the registry. These were caught and reported the same day they appeared, but the window between publication and detection is exactly the window attackers rely on.

How Big Is This: 433 Compromised Components and Counting

Four separate security firms, StepSecurity, Aikido Security, Socket, and the OpenSourceMalware community, have been tracking this campaign in parallel and cross-referencing their findings. The number they have collectively confirmed as of the most recent reporting is 433 compromised components across GitHub repositories, npm packages, and VS Code and Open VSX extensions.

Breaking that down: Aikido reported 151-plus GitHub repositories compromised between March 3 and March 9 in the GlassWorm Unicode steganography wave. StepSecurity reported 240-plus repositories compromised between March 8 and March 13 in the ForceMemo force-push wave. These are two parallel attack streams running simultaneously from the same underlying threat actor, confirmed by the shared Solana C2 infrastructure. The same actor was running two different delivery methods against the same target ecosystem in the same week.

The history of this specific campaign goes back further. The October 2025 initial wave reached over 35,000 installations. The November 2025 wave reached approximately 10,000. The January 2026 wave reached over 22,000. The total exposure across all waves is in the range of 70,000-plus developer installations, not counting downstream exposure through compromised repositories that other developers then pulled into their own projects. That downstream number is unknown but is likely considerably larger.

How to Check If You Are Already Infected Right Now

StepSecurity provided specific, concrete indicators of compromise in their disclosure. If you work with Python or have VS Code installed, run these checks before you do anything else today.

Check 1: Search your Python projects for the GlassWorm marker variable. Open a terminal and run a search across your project directories for the string lzcdrtfxyqiplpd. This is the specific variable name embedded in GlassWorm-injected code and its presence in any file is a confirmed indicator of compromise. On Linux or Mac: grep -r "lzcdrtfxyqiplpd" ~/your-project-directories. If you find it, do not run anything in that repository until you have reviewed the git history.

Check 2: Look for the persistence file. Check whether a file named init.json exists in your home directory. Run ls -la ~/init.json in your terminal. If this file exists and you did not create it deliberately, that is a strong indicator the malware has been on your system. Do not delete it immediately: it is useful evidence for understanding what happened.

Check 3: Check for unexpected Node.js installations in your home directory. Run ls ~/node-v22* or search for any node-v directories in your home folder that you did not install yourself. GlassWorm deploys its JavaScript payload runtime here specifically because the home directory is writable without elevated permissions and is less commonly monitored than system directories.

Check 4: Look for suspicious i.js files in recently cloned projects. If you have cloned any Python repositories from GitHub in the last ten days, inspect them for an unexpected i.js file in the project directory. This file is part of the ForceMemo payload delivery.

Check 5: Review recent Git commit histories for anomalies. For any repositories you maintain, check whether there are commits where the committer date is significantly later than the author date. Legitimate commits from normal development typically have matching or very close committer and author dates. A commit where the author date is weeks old but the committer date is within the last week is a red flag consistent with the ForceMemo force-push injection technique.

What to Do Immediately If You Work With Python or VS Code

If you find indicators of compromise on any of the checks above, the immediate priority is containment. Revoke all GitHub personal access tokens and regenerate them. Revoke all NPM authentication tokens. Rotate any SSH keys that were present on the compromised machine. Do this before pushing to any repository, because doing so from a compromised machine would inject the malware into whatever you are pushing to.

If you do not find compromise indicators but you have installed VS Code extensions from Open VSX between October 2025 and now, treat your GitHub and NPM tokens as potentially compromised anyway and rotate them as a precaution. The earlier GlassWorm waves were subtle enough that many developers whose credentials were stolen did not receive any obvious signal that the theft occurred. Rotating tokens costs you a few minutes. Not rotating them if they were stolen costs considerably more.

For ongoing protection, the most impactful change you can make is enabling hardware-backed multi-factor authentication on your GitHub account using a FIDO2 key, which makes token theft insufficient for account access because the attacker also needs the physical key. Short-lived personal access tokens, which expire and need to be rotated regularly, limit the damage window if a token is stolen. Fine-grained PATs that are scoped only to specific repositories rather than all repositories in your account limit the blast radius if one is compromised.

For teams and organizations, StepSecurity recommends monitoring GitHub Actions runners for outbound network connections to Solana RPC endpoints. That is an unusual traffic pattern that has no legitimate reason to appear in a CI/CD pipeline and its presence is a reliable indicator that something injected by GlassWorm is executing in your pipeline environment.

For the actual invisible Unicode character injection technique, the defense is static analysis tooling that specifically scans for variation selectors used outside of their legitimate context. Several security tools have been updated in the last few weeks to detect this, but you need to be running a current version. Older static analysis pipelines will not catch it.

The Bigger Picture

GlassWorm is worth understanding beyond the specific remediation steps because it represents something meaningful about where supply chain attacks are going. Each wave has been more sophisticated than the last. The October 2025 wave was a relatively straightforward malicious extension. The March 2026 ForceMemo wave involves stolen credentials, Git history rewriting, blockchain-based C2, Unicode steganography, and cross-platform targeting of GitHub, npm, and VS Code simultaneously. The sophistication improvement from first wave to current wave took less than five months.

The specific technique of using a public blockchain as a command-and-control channel is not unique to GlassWorm, but it is becoming more common in sophisticated campaigns precisely because it solves the takedown problem that defenders have traditionally used to disrupt malware operations. When the C2 infrastructure cannot be seized or blocked without affecting unrelated legitimate services, defenders lose one of their most reliable tools.

The force-push rewrite technique that makes injected commits look identical to legitimate history in GitHub’s UI is genuinely novel. StepSecurity explicitly stated that no other documented supply chain campaign had used it before ForceMemo. Novel techniques are significant because existing detection systems are not calibrated to catch them. Security teams running tooling that worked against every previous supply chain campaign would not have caught this one.

If you are a developer and you read nothing else from this article, at minimum check your home directory for init.json and your Python projects for lzcdrtfxyqiplpd today. Those two checks take about two minutes and will tell you definitively whether GlassWorm has been on your machine. The campaign is still active. New repositories are being hit today. This is not a historical story about something that already got cleaned up.

Have you found any indicators of compromise in your own environment? Drop a comment with what you found and which platform you were using. A running tally of what people are seeing in the community is genuinely useful information right now while the incident is still active.

References (March 17, 2026):
StepSecurity ForceMemo original disclosure (March 16, 2026): stepsecurity.io
SecurityWeek ForceMemo coverage: securityweek.com
The Hacker News GlassWorm coverage: thehackernews.com
Bleeping Computer: GlassWorm hits 400+ repos (March 17, 2026): bleepingcomputer.com
abit.ee GlassWorm Unicode technical analysis: abit.ee
OpenVPN weekly cybersecurity recap (March 17, 2026): openvpn.net
Solana C2 wallet address (confirmed by StepSecurity): BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC

The code looked exactly right. The commit message was unchanged. The author was correct. The timestamp matched.
The malware was already there.

Leave a Reply

Your email address will not be published. Required fields are marked *