If you build Telegram bots in Python, you almost certainly know pyrogram ; and you should be aware that a malware campaign we’re calling Operation Navy Ghost is targeting developers who adopt pyrogram and related modules as a dependency.
It is one of the most popular Telegram MTProto client libraries in the Python ecosystem. A clean, modern, async-first, library that has become trusted by developers worldwide. Its numbers speak for themselves:
347,395 downloads every month: enough to be worth an attacker’s time, not so much that it’s likely to attract significant attention from researchers.
Between November 2025 and June 2026 , a threat actor (likely a small group operating under multiple identities) published at least eight separate trojan-infected pyrogram forks to PyPI. Each one looked like a legitimate pyrogram variant but carried a hidden backdoor that gives the attacker full remote control over any server running the infected package.
The attackers took the legitimate pyrogram source code, added a hidden file that acts as a backdoor, packaged it under slightly different names, and published it to PyPI (Python Package Index).
We are calling this campaign Operation Navy Ghost due to its attempt to bait developers by claiming to be a “Navy fork” of pyrogram.
Here’s what you need to know to defend your organization:
These packages have been removed from PyPI ; however, they may be present in private package registries (like your Artifactory), cached on developer workstations, included in third-party applications, etc.
Exfiltration / C2 (Command and Control) occurs via Telegram. If your org uses or is unwilling to block Telegram itself, block the attacker’s Telegram channel: “https[:]//TokoWann[.]t[.]me/2” and attacker Telegram user IDs: “842320686”, “845521076”, “1675073032”, “1054295664”, “1928772230”, “6710439195”, “984144778”, “1992087933”, “7028669261”, “6321616956”, “278475769”, “1964437366”, “327471892”, “5092757079”, “273057737”, “8721707252” (NOTE: Telegram’s architecture generally makes it impossible to block specific channels/users at the network level; this type of blocking is only possible at an application level, and therefore likely only applies to automation or other clients you fully control.)
your infrastructure, including third-party application footprint, for these packages or indicators of compromise
Checkmarx customers can use their Global Inventory to assess the presence of these packages in your organization’s first-party applications
Use YARA or similar tool to examine desktops and deployed applications for affected files (see below for detection options and a basic YARA rule for this campaign)
Here is a summary of every malicious package discovered in this campaign:
Most packages have now been taken down from PyPI thanks to our reports. But the damage window — across multiple months and dozens of versions — means any organization or developer that installed one of these during that period should treat their environment as compromised.
One of your first concerns should be if your own developers consumed any of these packages. Checkmarx customers with MPP (Malicious Package Protection) are currently protected against new installs and can check Global Inventory to determine if any projects were affected in the past.
Customer or not, you can examine individual developer desktops, CI runner instances, etc. using the steps below. To detect third-party applications and other sources of entry, see the YARA detection rule in the section.
Step 1 — Check your installed packages:
If any of these return information, you had a malicious package installed.
Step 2 — Check your pip install history:
Step 3 — Check for the malicious file:
If this file exists anywhere on your system, your environment was compromised.
Step 4 — Check for unknown Telegram handlers on your bot: Any bot running one of these packages will have hidden handlers registered. If you cannot account for all registered handlers in your own code, treat the session as compromised.
If you use YARA for malware detection, or another tool that ingests YARA rules, you can import this rule directly. Otherwise, examine the rule for IOCs that you can then enter in your own infrastructure:
The campaign started quietly, grew more sophisticated over time, and kept spawning new variants:
After our initial discovery and reports in May, we still see new packages being published in this campaign.
One reason this campaign is dangerous is how convincing the packages look. Since the attackers did not take over a legitimate developer account, they spent time making their packages appealing and legitimate-looking to appeal to their targets.
Consider VLifeGram. Its pyproject.toml reads in part:
It had a proper README, a legitimate-looking license, correct Python version classifiers, a real GitHub repository, and even a Telegram community link. To a developer searching PyPI for pyrogram, this looks like a credible fork that might even provide some real advantages.
kelragram went further, describing itself as a “Navy Fork” in the package readme — a deliberate hint at the pyrogram-navy package, linking the packages together as a branded suite.
This is a supply chain social engineering attack, crafted to trick developers into inviting a malicious package into their environment.
Every package in this campaign carried one key malicious file: pyrogram/helpers/secret.py
This file does not exist in any legitimate pyrogram release. It was injected by the attacker into the helpers module — a location that sounds routine and trustworthy to anyone quickly scanning the package structure.
Here is what it contains:
OWNERS = [842320686, 845521076, 1675073032]
These are hardcoded Telegram user IDs. Any Telegram account matching one of these IDs gets unconditional remote control over any server running the infected package. Think of them as master keys.
Different package versions carried different OWNER lists — a detail we will return to when discussing attribution.
The moment this runs, two invisible command handlers are registered on the victim’s Telegram client:
Notice the self-exclusion guard at the top: if client.me.id in OWNERS: return. The backdoor will not activate on the attacker’s own accounts. This is a detail that reveals careful planning — the attacker has thought accidentally triggering the backdoor on their own bots.
When the attacker sends /asu print(os.environ) to the victim’s bot, this function compiles and executes that Python code on the victim’s machine — with full access to the live Telegram client, session, chats, contacts, and environment variables.
When the attacker sends /asi cat /etc/passwd, this runs /bin/bash -c “cat /etc/passwd” on the victim’s server and returns the output. This is repeatable with any shell command, and runs under the infected application’s authority, meaning the malware can access and exfiltrate whatever the infected application could legitimately access.
Here is the clever part. The attacker does not need a separate C2 server or HTTP endpoint. All stolen data comes back through Telegram itself via the victim bot’s own replies.
Large outputs are automatically written to a file and sent as a Telegram document attachment back to the attacker. This means all exfiltration traffic looks like normal Telegram bot traffic : it bypasses HTTP monitors, firewall rules, and DNS-based network detection entirely.
Including secret.py the package is subtle, but its payload activation method is even more interesting. The attacker was careful to make this nearly invisible to common analysis methods.
In vlifegram, the activation is wired directly into the helpers module’s __init__.py :
The moment any code does import pyrogram, the helpers module is loaded, secret.py is imported, and init is ready to be called. There is no way to use the package without loading the backdoor.
In the other packages, the injection is buried deeper — inside the Client.start() method, which every pyrogram bot calls when it starts up:
Three things to notice here:
1. Bot-exclusive targeting. The if self.me.is_bot check means the backdoor only activates on Telegram bot accounts — not userbots. This is deliberate. Bots typically run on production servers with access to databases, credentials, cloud APIs, and sensitive infrastructure. This suggests that attacker specifically wanted server access, not personal account access, and likely reasoned that this would be less likely to be noticed compared to compromising userbots.
2. Silent suppression. The entire injection is wrapped in try / except: pass. If anything goes wrong — the file is missing, an import fails, anything — the exception is silently swallowed. No error message, no log entry, no indication anything went wrong. The bot starts normally. The developer sees nothing unusual.
3. Deeper hiding. Compared to vlifegram’s obvious __init__.py import, the start.py injection requires an analyst to trace through the client lifecycle code to find it. A quick file scan of the helpers module would not catch it.
The most significant evidence for attributing this to a coordinated single threat actor group is the common thread connecting all packages: shared Telegram user ID 327471892
This single Telegram user ID appears as an OWNER in, for example:
Despite different PyPI accounts in use, these packages all using that same shared Telegram user ID is an incredibly clear signal that this is a coordinated campaign.
kelragram describes itself explicitly as a “Navy Fork” , apparently connecting pyrogram-navy as part of a “branding” effort. This seems to be the attacker branding their malicious toolkit as a product suite, likely to build perceived legitimacy among a target developer community.
Beyond the shared OWNER IDs, the code itself is forensically identical across all identified packages:
This is a strong signal that this is one threat actor, whether that’s a single individual or a coordinated group.
Here is every attacker-controlled Telegram ID found across the campaign:
VLifeGram (most versions) + VLife-Gram (all versions): 842320686, 845521076, 1675073032
VLifeGram versions 2.0.0.9 & 2.1.0.1 + VLife-Gram (all versions): 1054295664, 1928772230, 6710439195, 984144778, 1992087933, 7028669261, 6321616956, 278475769, 1964437366, 327471892
kelragram: 5092757079, 273057737, 8721707252
pyrogram-navy + pyrogram-styled + pyrogram-zeeb + sepgram + pyrogram-kelra: 327471892, 1054295664, 1964437366, 1928772230, 6710439195, 984144778, 1992087933, 7028669261, 6321616956, 278475769, 5092757079
The expansion from 3 owners to 10 owners in specific vlifegram versions — and the overlap of 327471892 across multiple packages and author accounts — suggests this campaign involved a small coordinated group with one primary operator.
Let us make this concrete. Once a developer installs one of these packages and their bot is running, here is what the attacker can do from a Telegram chat:
Read any file on the server:
Dump all environment variables (API keys, database passwords, tokens):
Read the bot’s own Telegram session (giving access to all its chats and messages):
Download the entire database:
Install a persistent backdoor:
Exfiltrate files directly to the attacker via Telegram: The shellrunner function automatically sends files larger than 4096 bytes as Telegram document attachments — no extra steps needed for the attacker.
And all of this happens through Telegram messages. No suspicious HTTP connections. No unusual DNS queries. Nothing that a standard network monitor would flag.
The bot-exclusivity check (if self.me.is_bot) tells us exactly who the attacker was after: developers who build and deploy Telegram bots .
This is a high-value target group. Telegram bots used in production environments commonly have access to:
A developer who installs one of these packages to build their bot — on a VPS, a cloud server, or even their local machine — hands the attacker everything on that system the moment the bot starts.
842320686, 845521076, 1675073032, 1054295664, 1928772230, 6710439195, 984144778, 1992087933, 7028669261, 6321616956, 278475769, 1964437366, 327471892, 5092757079, 273057737, 8721707252
https[:]//t[.]me/+842320686, https[:]//t[.]me/+845521076, https[:]//t[.]me/+1675073032, https[:]//t[.]me/+1054295664, https[:]//t[.]me/+1928772230, https[:]//t[.]me/+6710439195, https[:]//t[.]me/+984144778, https[:]//t[.]me/+1992087933, https[:]//t[.]me/+7028669261, https[:]//t[.]me/+6321616956, https[:]//t[.]me/+278475769, https[:]//t[.]me/+1964437366, https[:]//t[.]me/+327471892, https[:]//t[.]me/+5092757079, https[:]//t[.]me/+273057737, https[:]//t[.]me/+8721707252
https[:]//TokoWann[.]t[.]me/2
/asu, /wann (Python eval) · /asi, /wann2 (shell exec)
Callback Query Triggers
secretruntime · secretforceclose
Immediately stop any running bots that used these packages
Verify package names carefully. The legitimate pyrogram package is simply pyrogram. Any package named vlifegram, pyrogram-navy, kelragram, or similar is not an official fork endorsed by the pyrogram project.
Check the PyPI author. The legitimate pyrogram is published by delivrance. Before installing any fork, check who published it and what else they have published.
Audit your requirements.txt and pyproject.toml. If these packages are pinned in your project’s dependencies, remove them immediately and replace with the legitimate package.
Enable dependency scanning in your CI/CD pipeline. Tools like Checkmarx MPIAPI can flag newly published or suspicious packages before they infect, while SCA with MPP can monitor for use that may have slipped into your code repositories.
Treat any pyrogram fork with caution. There are legitimate pyrogram forks (hydrogram, pyrofork, etc.) maintained by known community developers with transparent histories. Before adding any fork as a dependency, check its GitHub commit history, compare it against upstream pyrogram, and look for files that do not exist in the original.
pyrogram/helpers/secret.py pyrogram/methods/utilities/start.py (modified) pyrogram/helpers/__init__.py (modified in VLifeGram)
As of June 24, 2026, the following packages are impacted:
Operation Navy Ghost is an excellent example of how open-source supply chain attacks work in practice, without requiring an account takeover. The attacker did not need compromise anything to make their attack available. They simply published packages that looked legitimate, waited for developers to install them, and silently took over every server that did.
It also showcases the patience and sophistication of modern threat actors. This campaign spanned eight months of active publishing, three publisher identities across multiple related packages, and two different injection techniques: one wired at import time, one buried in the client lifecycle. A Telegram-based exfiltration and C2 channel that is likely impossible for network controls to block or effectively monitor without blocking Telegram entirely. And a shared toolkit fingerprint that links the whole operation back to a single threat actor.
It’s a lesson that supply chain attacks are evolving: becoming more targeted, more advanced, and higher stakes. And that’s a clear reminder that proactive defense of the open-source supply chain is no longer optional.
Checkmarx Security Research Team
Supply Chain Security
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
