#38053 [BC-High] A single signer can continuously prevent signatures from being finalized, halting n

Submitted on Dec 23rd 2024 at 11:35:57 UTC by @n4nika for Attackathon | Stacks

  • Report ID: #38053

  • Report Type: Blockchain/DLT

  • Report severity: High

  • Target: https://github.com/stacks-network/sbtc/tree/immunefi_attackaton_0.9/signer

  • Impacts:

    • Network not being able to confirm new transactions (total network shutdown)

Description

Summary

The current implementation of the wsts library allows a single signer to prevent any signature request from being finalized due to how signature_shares are gathered after the nonces.

Finding Description

Whenever we want to generate a signature with the wsts library, we follow these steps within the coordinator_state_machine:

  1. start signing round (State::Idle)

  2. request nonces

  3. gather nonces

    • Until we have a threshold of nonces (wsts::fire.rs#L829: if nonce_info.nonce_recv_key_ids.len() >= self.config.threshold)

  4. request sig shares

  5. gather sig shares

    • Until every signer who provided a nonce, provided their signature share

Now the problem lies in how we handle steps 3) and 5). Once we got a threshold of nonces, we are reliant on exactly the signers who provided a nonce to ALSO provide a signature share. This can now be exploited in the following way:

  • Malicious signer (alice) provides a nonce when it gets requested

  • Once coordinator has enough nonces, they gather the sig shares

  • alice now doesn't provide her signature share, causing the coordinator's signing round to time out

This can be repeated for every signing round, given that alice manages to be one of the threshold signers who provide a nonce.

Mitigation

This is rather difficult to mitigate since it is a general problem with the design of the wsts library. In order to prevent this, we would need to do nonce and sig share gathering in parallel or at least independently of each other and "succeed" once we got a threshold of signers who provided both a valid nonce and sig share.

Proof of Concept

PoC

In order to show this, please apply the following diff and execute the test with cargo test --package signer --test integration -- transaction_coordinator::sign_bitcoin_transaction_poc --exact --show-output --ignored --nocapture.

The test simulates a signer providing their nonce but not the sig share, causing the coordinator to timeout and be unable to finalize signatures. This test does not guarantee that the modified signer is always one of the nonce providers so the test does not work always, however, in a real-world scenario, with a signer set of 15, this absolutely doable on every signature request.

Last updated

Was this helpful?