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.