BLOCKCHAIN AI.NEWS

Security · Original reporting

Since 2022, a Bitcoin Core Wallet Name Could Carry a Shell Command

Bitcoin Core 32.0 entered testing on Monday with a fix for a bug that let a crafted wallet name run commands on some nodes, dating back to version 24.0. A reviewer said the fix should be backported. As of Thursday, none of the three maintained release lines has it, and the latest stable release still carries the vulnerable code.

Editorial illustration: a polished chrome tag hanging from a frosted glass ring, a thin golden thread running from it into a small chrome box with a blue-lit seam
✓ Release reported first by CoinDesk (Sep 16), then Decrypt · Read by this desk: the 32.0 draft release notes, fix PRs #36048 and #36123 with their review threads, the 31.x and 30.x backport PRs, and src/util/string.cpp at v31.1

Bitcoin Core tagged its first 32.0 release candidate on September 14, and the draft release notes mostly describe the kind of work node operators like: faster block validation, a second fee estimator, PSBT version 2 by default. Tucked into the wallet section is a sentence of a different kind. On non-Windows systems, it says, "an authenticated RPC caller allowed to create wallets could execute arbitrary commands as the node process account" when -walletnotify was configured, "by crafting a wallet name with regex replacement characters."

CoinDesk reported the release candidate on Wednesday and noted that the bug affected versions 24.0 through 31.x. What neither the release notes nor that coverage says is where the fix stands for people who aren't about to move to 32.0. This desk checked. The fix hasn't been backported to any maintained release line.

How a name became a command

-walletnotify is an old operator feature: give Bitcoin Core a shell command, and it runs that command whenever a wallet transaction changes. According to the option's help text, %s in the command is replaced with the transaction ID and %w with the wallet name. Because the wallet name comes from whoever created the wallet, Bitcoin Core shell-escapes it before substituting.

The escaping was fine. The substitution wasn't. Pull request #36048, opened on August 20 by contributor l0rinc, traces the problem to 2022. That year, PR #25803, a refactor meant to drop a Boost dependency, swapped a literal string-replacement helper for C++'s std::regex_replace(). With that function, the replacement text isn't plain text. Sequences such as $' are instructions: $' means "insert everything after the match."

So a wallet name containing $' gets escaped correctly, then passed to regex_replace() as replacement text, where the $' pulls the rest of the command into the middle of the escaped name. In a review, Bitcoin Core contributor stickies-v described the result: the expansion ends up "breaking the quote accounting by removing the first single quote," so part of the wallet name reaches the shell unescaped. The PR says runCommand() hands the result to system(). The PR's new functional test reproduces this: it creates a wallet with such a name and checks for a marker file the injected command writes.

The fix is small. It removes <regex> from src/util/string.cpp and replaces the one-line regex_replace() call with a plain find-and-append loop, which restores what the PR calls "the literal, non-recursive contract" the helper had before 2022. The PR credits the discovery to "the Red Team" and lists Rob Hamilton as a co-author of the core commit.

Who was actually exposed

The conditions are narrow, and the PR is careful about them. An attacker needs authenticated RPC access with permission to create wallets. The node has to run with -walletnotify set, on a non-Windows system, since the help text says %w "is not currently implemented on windows." The PR says the flaw "is not reachable over P2P or by an unauthenticated network peer."

That rules out most home nodes. It doesn't rule out setups where RPC credentials are shared with software or people other than the node's owner, or where RPC access is supposed to be limited to certain calls. Reviewer stickies-v argued for a release note on exactly those grounds: operators "under this specific configuration" should know their machine may have been exposed. Nothing in the PR or its review says the bug was exploited, and this desk isn't suggesting it was.

From a 2022 refactor to a fix that only ships in 32.0

DateEvent
Aug 16, 2022PR #25803 merged; ReplaceAll() now uses std::regex_replace() (first shipped in v24.0, per #36048)
Jul 8, 2026v31.1, the current stable release, published with the regex version
Aug 20, 2026PR #36048 opened in public, with a test that reproduces the injection
Aug 31, 2026Reviewer: fix "should probably have release notes, and be backported"
Sep 2, 2026#36048 merged to master, milestone 32.0
Sep 11, 202631.x and 30.x backport batches merged; neither includes #36048
Sep 14, 2026v32.0rc1 tagged with the fix
Oct 10, 2026Target date for final v32.0
Compiled by this desk from GitHub merge and tag timestamps, bitcoin/bitcoin releases, and the 32.0 release schedule. Branch contents checked on September 17.

The backport that hasn't happened

Bitcoin Core's lifecycle page says the project maintains the latest three major versions, which today are 31.x, 30.x and 29.x. On August 31, stickies-v wrote in the #36048 review that the change "should probably have release notes, and be backported." It got the release note. As of Thursday it hasn't been backported.

The record here is clear. The last backport batches for 31.x and 30.x were merged on September 11, nine days after the fix reached master. Each backported a single macOS build change. This desk found no open pull request against the 31.x, 30.x or 29.x branches. The source file on the 31.x branch still calls std::regex_replace(), as it does in the v31.1 tag.

None of that means a backport won't come. Bitcoin Core batches its backports, and a maintainer could add this one before the next point release. The project hasn't published a security advisory or a severity rating for the bug. Under its disclosure policy, low-severity issues get a write-up two weeks after the major release containing the fix, while medium- and high-severity ones wait until the last affected version reaches end of life. This bug didn't go through either route. The mechanism, and a working test for it, have been public in the PR since August 20.

The second fix, found with an AI model

The other security change CoinDesk highlighted affects no one yet. PR #36123, from maintainer Matthew Zipkin (pinheadmz), patches a memory-exhaustion flaw "found while auditing the new http server with kimi-k3." That server replaces libevent. It was merged to master on June 22, and v32.0 will be its first release.

The flaw: while the server was busy with one request from a client, it kept reading and buffering whatever that client sent next, with no size limit. The PR's own description originally said the risk was "limited to authenticated clients." A reviewer, jeanpablojp, tested that claim and disagreed. With -rest enabled, "16 connections with no credentials at all" took a node from 46 MB to about 3 GB of memory in a minute, because REST requires no authentication. After Zipkin rewrote the patch, the same reviewer measured 3 MB of growth over 90 seconds. Another reviewer, hodlinator, called the fix nice to have in v32 but "not critical as the HTTP interface shouldn't be exposed publicly." It was merged on September 5 and is in rc1.

The Take

Nobody is in a panic here, and nobody should be. The walletnotify bug needs an authenticated caller on an unusual configuration, and the review was careful, quick and candid about the risk. But once a fix lands in a public PR with a test that shows how to trigger it, the question for operators on 31.1 is simple: when do we get it? A reviewer asked for a backport. The patch replaces one line with a short loop, and two backport batches have gone out since without it. If maintainers think a backport isn't worth it, that's a defensible call for a bug this narrow, but they should say so in the release notes of the next point release. Otherwise the practical advice for anyone running -walletnotify with shared RPC credentials is to wait for 32.0, rebuild with the patch, or turn the option off, and so far the project hasn't told them any of that.

More on the subject