Antoine Poinsot
590B 7292 695A FFA5 B672 CBB2 E13F C145 CD3F 4304
On March 25, @Pythcoiner from the
Wizardsardine team contacted me about a crash bug
in Liana they had identified as
coming from the
rust-miniscript
dependency. After investigating i identified the crash as coming from the
library’s Miniscript satisfier, and found it could be triggered by an attacker.
The impact of this vulnerability is that an attacker could make a victim crash by getting them to satisfy a transaction spending a specially-crafted descriptor. In simpler terms, a victim could crash when “signing” a transaction that spends from a specific “wallet”. This is not critical (but still pretty annoying!) if you consider the victim as being a software running locally. But these are pretty common operations and it would not be surprising that an application running on a server publicly exposes them (for instance through BDK). There, an attacker exploiting this bug could prevent all users from accessing the service.
The bug itself is an out-of-bounds read in the Satisfaction::thresh()
function.
This code assumes that the size of the sat_indices
vector is strictly superior to the threshold
k
. This is not always the case, as thresh(2,A,B)
is valid Miniscript. Therefore trying to
satisfy any thresh()
fragment with a threshold k
equal to the number of sub-fragments will
trigger an out-of-bounds read in the sat_indices
vector. It can be fixed with the following diff:
diff --git a/src/miniscript/satisfy.rs b/src/miniscript/satisfy.rs
index d141340..cca7051 100644
--- a/src/miniscript/satisfy.rs
+++ b/src/miniscript/satisfy.rs
@@ -1026,7 +1026,8 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfaction<Placeholder<Pk>> {
// For example, the fragment thresh(2, hash, hash, 0, 0)
// is uniquely satisfyiable because there is no satisfaction
// for the 0 fragment
- else if !sats[sat_indices[thresh.k()]].has_sig
+ else if sat_indices.len() > thresh.k()
+ && !sats[sat_indices[thresh.k()]].has_sig
&& sats[sat_indices[thresh.k()]].stack != Witness::Impossible
{
// All arguments should be `d`, so dissatisfactions have no
I reported this issue according to rust-bitcoin
’s security
policy by emailing Andrew
Poelstra on March 25th, who acknowledged receipt on the same day. Andrew and Sanket Kanjalkar came
back to me a couple days later. They asked for a 4 weeks embargo on the details and silently fixed
the vulnerability in rust-miniscript PR
#798. The fix was then
backported to the 12.x
release branch
and a 12.3.1
version was
released on April 1st. They also responded to this incident by increasing the fuzz
coverage
of their satisfier.
I’d like to thank the Rust-Miniscript maintainers Andrew Poelstra and Sanket Kanjalkar for the swift reply and the no-bullshit communication throughout this report. Thanks to the Wizardsardine team, and @Pythcoiner in particular, for identifying the crash in the first place. Finally, thanks to Chaincode Labs for sponsoring my Bitcoin open source work.