A malicious PyPI package named graphsync was identified as part of the graphalgo fake crypto developer recruitment campaign. Armando's analysis of graphsync v1.1.1 found an install hook in setup.py that triggers graph_config.py and load_libraries.py as droppers -- reverse-base64-zlib stubs whose decoded payload contacts ryanthompson4323/axios-net on GitHub. That stager account is the same one used by six npm packages in the campaign's OLD-ryanthompson pattern (bignumberx, bigmathex npm, graphlink, graphflowx, graphrix, graphhub, graphchain, graphorbit, graphkitx, graphnetworkx). graphsync represents a cross-ecosystem reuse of C2 infrastructure: the same stager GitHub repository serves both npm JavaScript downloaders and this PyPI Python dropper. The publisher front used graphsync-discuss@googlegroups.com, a fake Google Groups community address. The package attracted 551 downloads.
Key Findings
- Malicious version: 1.1.1 (only version on PyPI)
- Entry point: Install hook in
setup.py executes dropper stubs at install time
- C2 staging: Decoded payload fetches from
https://raw.githubusercontent.com/ryanthompson4323/axios-net/refs/heads/main/README.md -- the same stager repo used by npm packages in the OLD-ryanthompson track
- Cross-ecosystem: This PyPI package reuses the npm campaign's ryanthompson4323 GitHub C2 account
- Publisher: "graphsync Developers" /
graphsync-discuss@googlegroups.com (fake Google Groups community address, same tactic as graphnode)
- Downloads: 551
- Detection: Script-Python.Dropper.SupplyChain, Archive-ZIP.Dropper.SupplyChain
Package Background
graphsync presents itself as a graph synchronization library. The publisher "graphsync Developers" with graphsync-discuss@googlegroups.com mirrors the fake community front used by graphnode (graphnode-discuss@googlegroups.com), indicating a consistent social engineering approach: impersonate a community-maintained project to reduce scrutiny. Total downloads: 551. First published December 9, 2025. The fake repository link points to https://github.com/miltonkaufman/graphsync/issues, another throwaway account.
Attack Analysis
At install time, setup.py executes graph_config.py and load_libraries.py. Each file contains a reverse-base64-zlib dropper stub identical to other campaign PyPI packages (graphex, graphlibx, bigpyx, bignum, bigmathex, bigmathutils). What distinguishes graphsync is the destination of the decoded payload: it reaches the ryanthompson4323/axios-net GitHub repository, the same stager used by the npm OLD-ryanthompson track.
This cross-ecosystem C2 sharing demonstrates a deliberate infrastructure choice: the ryanthompson4323 stager account serves both the npm JavaScript downloader chain (via big.min.js / graph-alg.min.js) and the PyPI Python dropper chain, allowing a single GitHub account to stage payloads across both ecosystems.
Malicious Files
graph_config.py classified as Script-Python.Dropper.SupplyChain. Install-hook dropper whose decoded payload contacts the ryanthompson4323 stager.
load_libraries.py classified as Script-Python.Dropper.SupplyChain. Second install-hook dropper with identical stub pattern.
setup.py classified as Script-Python.Dropper.SupplyChain -- registers the install hook.
Recommendations
- Identify exposure: Check for graphsync at version 1.1.1.
- Update immediately: No clean version. Remove graphsync entirely.
- Check lateral exposure: Also audit npm packages bignumberx, bigmathex, graphlink, graphflowx, graphrix, graphhub, graphchain, graphorbit, graphkitx, graphnetworkx -- all use the same ryanthompson4323 stager account.
- Scan your software supply chain: Use Spectra Assure Community (https://secure.software/pypi/packages/graphsync).
Network IOCs
| IOC | Type |
|---|
| https://raw.githubusercontent.com/ryanthompson4323/axios-net/refs/heads/main/README.md | C2 staging URL |
| https://raw.githubusercontent.com/ryanthompson4323/axios-net/refs/heads/main/rc.json | C2 auxiliary URL |
Indicators of Compromise
Malicious Files
| Filename | SHA256 | Detection |
|---|
| graphsync-1.1.1-py3-none-any.whl | aed890b9d24ba01e1a79c9c08c1e32219c52a23f74116be5a6776d156b1684f1 | Archive-ZIP.Dropper.SupplyChain |
| graph_config.py | 74ab0bb80b97cc72f5e4570d77562885dd9547df2553ec9c6033060b5bc7060b | Script-Python.Dropper.SupplyChain |
| load_libraries.py | bf0d761f59bdd5e3fe972c28700d863e82e9a6cd49d2aeed20211b2b87425c09 | Script-Python.Dropper.SupplyChain |
| setup.py | e9c1c0b146d076af2bebb31fd87c6db82c69608a27ef329a730f3d7418f68f4d | Script-Python.Dropper.SupplyChain |
Archive Hashes
| File | SHA256 |
|---|
| graphsync-1.1.1.tar.gz | cfa311221d2e2b22a6d60ec71ec69b7f8ba044aee43da0763be02529ffe99f1d |
| graphsync-1.1.1.tar | e3e2681990b7dc89ccd287903939ae8adf7d0a8fff962c83e0631c2fd130f27e |