The aiocpa malicious npm update
In November 2024, aiocpa — a Python library for interacting with the CryptoPay API, a Telegram-based cryptocurrency payment processing service — received a malicious update that silently exfiltrated cryptocurrency wallet seed phrases and private keys from any developer who used the library.
The attack followed a pattern that is now well-established: a legitimate, useful package with a real user base, maintained by an account with a genuine history, publishing a malicious update. Everything about the package looked normal. The malicious version was live for approximately 6.5 hours.
Background: aiocpa
aiocpa is an asynchronous Python client for the CryptoPay API. CryptoPay is a service that allows Telegram bot developers to accept cryptocurrency payments in their bots. aiocpa had approximately 4,000 weekly downloads — not enormous by PyPI standards, but a highly targeted audience: developers building financial applications that handle real cryptocurrency.
What the malicious update contained
Version 0.1.6 (the malicious version) included a change to the library's initialization code. The change was subtle and targeted:
When the library was imported and initialized with a CryptoPay API token (a required step for any application using the library), the initialization code now also checked whether any of the following environment variables were set: CRYPTO_PAY_API_TOKEN, WALLET_SEED_PHRASE, PRIVATE_KEY, MNEMONIC, or HD_WALLET_KEY.
If any of these were found, the code encoded them using base64 and sent them to a Telegram bot (using the Telegram Bot API) controlled by the attacker. The exfiltration used Telegram specifically because outbound HTTPS to api.telegram.org is extremely common in the applications that would use aiocpa — a Telegram bot developer's CI environment almost certainly allows outbound Telegram API traffic.
The code was structured to look like a telemetry or analytics call — it was in a function called _report_usage() which was called from the main __init__ method. The function name and call site looked like legitimate usage analytics.
Why the targeting was precise
The attacker chose aiocpa specifically because its users are, by definition:
- Building applications that interact with cryptocurrency payments
- Likely to have cryptocurrency-related secrets in their environments
- Less likely to have rigorous security monitoring than large enterprise developers
A developer building a Telegram cryptocurrency payment bot is working with real money. The seed phrases and private keys that aiocpa users store in their environments could directly control significant cryptocurrency holdings.
How the attack was detected
The attack was discovered by a developer who noticed an outbound API call to Telegram being made during their test suite — specifically, during a test that imported aiocpa but shouldn't have made any network calls.
They investigated, found the _report_usage() function, recognized it as malicious, and reported it to PyPI. PyPI pulled the package within hours.
What made this attack harder to catch with static analysis
The exfiltration channel was legitimately used by the library. aiocpa is a Telegram API library. Outbound calls to api.telegram.org are completely expected and legitimate. A static analyzer that flags outbound network calls would produce massive false positives for this library.
The function name was deceptive. _report_usage() is a plausible name for a legitimate analytics function. Many libraries call home to report usage statistics. Without understanding the context — what data is being sent and where — a human reviewer might not flag it.
The environment variable names were targeted. The code didn't blindly dump all environment variables (which might be too noisy). It specifically looked for cryptocurrency-related variable names, which made the payload smaller and harder to notice in network traffic.
How Veln catches it
Veln Lens — diff delta detection. The diff between version 0.1.5 and 0.1.6 showed a new function (_report_usage()) in the initialization code that was not present in the previous version. This function contained an outbound network call to an external endpoint. New network calls added to module initialization code are a High severity diff delta finding.
Canary sandbox. When the sandbox executed the package's initialization with a test API token and test environment variables, it observed an outbound HTTPS POST to api.telegram.org with a payload containing base64-encoded strings. The sandbox records: "Outbound connection made during module initialization. Payload contained base64-encoded content from environment variables." This is a definitive behavioral signal.
Cooling gate would have helped — but only slightly. The malicious version was published at a new minor version number. For users who pinned to 0.1.5, the cooling gate wouldn't apply until they ran an update. For users with loose version ranges (aiocpa>=0.1.0), the cooling gate would have held the 6.5-hour window regardless.
aiocpa targeted cryptocurrency developers specifically. Veln's diff detection and canary sandbox catch this class of targeted payload.