Summary of the threat
Between May 4 and May 24th a threat actor published 11 different malicious python packages to the PyPI registry. This malware campaign targeted the Solana cryptocurrency ecosystem, and was designed to steal source code, crypto currencies and other sensitive data on the systems that used the affected Python libraries.
Threat campaign chronology
There are four distinct iterations to this attack lifespan. Each part has its own focus and variation of the attack payload. We will walk through all 4 iterations of this attack chronologically.
First payload: prices.py
The very first iteration of this threat campaign involved six packages that were published between May 4 and May 20, 2025. All six of these packages used a prices.py file to deliver its payload.
The prices.py payload steals python files with the .py suffix and exfiltrates them to the http://84[.]54.44.100:3000/nodes/register endpoint.

The publish date and name for the six packages in this first iteration are:
May 4, 2025: solana-test, solana-token
May 8, 2025: solana-charts
May 11, 2025: solana-test-suite
May 13, 2025: solana-data
May 20, 2025: solana-coin
Second payload: helpers.py
The second iteration of this attack involved a single file that was published on May 20th, 2025. This iteration of the Solana-Drainer malware used the helpers.py to deliver its payload. This payload exfiltrated data to a new destination: http://89[.]110.96.251/client
The publish date and package name in this iteration is:
May 20, 2025: dexscreener-charts
Third payload: coins.py
The third iteration of this malware was published on May 21 and used a new payload file: coins.py. This variation exfiltrates data to a new destination: http://89[.]110.93.132/client
The publish date and package name in this iteration is:
May 21, 2025: dexscreener-data
Fourth payload: markets.py
The final variation of this malware was published on May 22nd and 24th in a set of three new PyPI packages. The final package, solana-live was published by a PyPI user named “george_bull”. This version uses a new attack vector in which it targets Jupyter notebooks that the compromised developer might be using. While the payload is different, this version of the malware uses the same exfiltration destination: http://89[.]110.93.132/client
The publish date and package name in this iteration are:
May 22, 2025: solana-trade, sol-prices
May 24, 2025: solana-live
The PyPI user appears to be a brand new throw away "code puppet" named george_bull:

Let's focus on the last package: solana-live
The solana-live package is the most recent package, and uses a unique attack vector so we will focus on it for the rest of this blog post. The package purports to be a “price fetching library” that automatically gets the price of certain cryptocurrencies on the Etherium and Solana blockchains.

There is only one version of the solana-live package, version 0.1.0. This naming construct is used across all nine of the malicious packages in this attack chain, and most of the packages only have one version.
Analyzing the solana-live source code
If we unzip the contents of the package we can see one file named ‘markets.py’.

Okay, I guess there’s only one thing to do: Let’s take a look at markets.py file!
If we inspect the code, the first thing that pops out is that for a library that says its fetching pricing information it sure does want to find out all my crypto balances!

And its calling out to several crypto platforms like Uniswap, Sushi and Pancakeswap ostensibly for pricing information:

But all of this crypto pricing functionality and subsequent API calls is not legitimate. It’s all for show!
The threat actors are trying to make it look like its doing what it says its supposed to be doing: Fetching crypto pricing. But its not.
What does this latest version of the malicious payload do?
The answer comes on line 115 when there is a POST action.

If we drill into what comes right before this we can see that the library is importing functionality from a Jupyter module (IPython) on line 109. Basically, this payload is looking for Jupyter Notebooks that the developer is using and grabbing the complete execution history of the Jupyter notebook on line 111. The execution history is like a shell history and source code rolled into one. There will be intellectual property in any notebooks that are compromised, but there will also be API keys, crypto logins, and other credentials that threat actor wants.
It then posts all returned data as a JSON object to an IP address 89[.]110.93.132 on line 115.
That IP address belongs to a VPS server in Moscow, Russia:

As detailed above, other versions of this malware use other IP addresses: 89[.]110.96.251 and 84[.]54.44.100. Both of these servers are hosted in the same VDSina hosting provider in Moscow:

Russian sanctions are paying off
We are seeing many Russian based threat actors use Russian domestic hosting providers like VDSina. One potential explanation is that western sanctions on Russia and the use of the Ruble currency means that Russian threat actors can no longer pay for international hosting.
VDSina for example only accepts Rubles and only hosts in Russia:

IP reputation signal
Unfortunately, Virustotal only alerts on one of the three IP addresses:


And only one vendor (Fortinet) alerts on the flagged IP address:

Why the different payloads?
To us, this appears as though the threat actors are testing different attack vectors and finding new ways to get at important and sensitive data quickly. The use of Jupyter Notebooks is a good example of this. We have seen other malware target Notebooks in the past, but this is the first time we've seen native package source code try to steal content from a Jupyter Notebook.
Our research team worked with PyPI to remove the malicious packages
The Safety team worked with PyPI to remove the malicious packages from the pypi.org registry. With an hour of our notification to the abuse team at PyPI new banners appeared on the solana-live package:

Within 24 hours after notifying the PyPI team the malicious packages were taken down. I only wish that NPM was as responsive at taking down malicious packages!
Big shout out to Mike and the PyPI security team!
Safety's malicious package detection protects you from threats that other platforms don't
Other research companies have identified one of the packages in this threat campaign solana-token, but Safety was the only platform to identify all 11 packages in this threat campaign and to tie them together. We did this with our unique identification system that looks for threat indicators that other products don't.
If you would like to see more about how we protect our customers from threats like this one, please reach out to us. Our new Safety Firewall product runs on developers workstations and laptops. Firewall also runs in the CI/CD pipelines companies use and our IOC data is sent to those Firewall endpoints to protect them in real-time. So, when we identify a new malicious package we send the package name, IP addresses, c2 domains, exfiltration endpoints and more to all Safety Firewall users.
Indicators of Compromise (IOCs)
IP addresses:
89[.]110.96.251
89[.]110.93.132
84[.]54.44.100
NPM Packages:
solana-token
solana-test
solana-charts
solana-test-suite
solana-data
solana-coin
dexscreener-data
dexscreener-charts
solana-trade
sol-prices
solana-live
Let us know if this blog post helped your org!

I hope you have enjoyed going down this rabbit hole with me.
Hit me up directly if you have any questions about this campaign.
You can find me on LinkedIn and BlueSky.
Paul McCarty - Head of Research, Safety