#40770 [BC-Low] Unvalidated withdrawal events allow data manipulation and denial of service in Emily

Submitted on Mar 3rd 2025 at 15:03:52 UTC by @Cartel for Attackathon | Stacks II

  • Report ID: #40770

  • Report Type: Blockchain/DLT

  • Report severity: Low

  • Target: https://github.com/stacks-network/sbtc/tree/immunefi_attackaton_1.0

  • Impacts:

    • API crash preventing correct processing of deposits

    • Temporarily Freezing Network Transactions

Description

Brief/Intro

The Emily service blindly trusts and processes withdrawal events received from signers without verifying their authenticity or correctness. This allows a malicious signer to manipulate legitimate withdrawal data or inject fake withdrawal records, ultimately corrupting Emily’s database and enabling a DoS attack.

Vulnerability Details

Whenever a user initiates a withdrawal on Stacks, a withdrawal-create event is emitted by sbtc-registry.clar.new_block.rs listens for these emitted events, writes them to the signers' database, and also informs Emily:

    // Create any new withdrawal instances. We do this before performing any updates
    // because a withdrawal needs to exist in the Emily API database in order for it
    // to be updated.
    emily_client
        .create_withdrawals(created_withdrawals)
        .await
        .into_iter()
        .for_each(|create_withdrawal_result| {
            if let Err(error) = create_withdrawal_result {
                tracing::error!(%error, "failed to create withdrawal in Emily");
            }
        });

    // Execute updates in parallel.
    let futures = vec![
        emily_client
            .update_deposits(completed_deposits)
            .map(UpdateResult::Deposit)
            .boxed(),
        emily_client
            .update_withdrawals(updated_withdrawals)
            .map(UpdateResult::Withdrawal)
            .boxed(),
    ];

The problem here is that Emily does not validate the correctness of the events received from the signers upon create_withdrawals and update_withdrawals.

A malicious signer can manipulate legitimate withdrawal requests, altering parameters such as the amount, status, and more.

  • Since a malicious signer can monitor the Stacks mempool, they can detect legitimate withdrawal requests as they appear in the mempool and immediately call create_withdrawals on Emily before other signers, using the same withdrawal-related data (such as the requestId and other identifiers), but for example with a manipulated amount set to 0. It also does not matter if the signer is the coordinator or not, they can do this anytime.

  • Additionally, the malicious signer can also update a withdrawal request in Emily database arbitrarily by calling update_withdrawals function.

Furthermore, a malicious signer can inform Emily about non-existent withdrawal requests, which Emily will blindly process and record. This allows an attacker to flood Emily with a large number of junk requests, ultimately causing it to run out of memory and fill its database with junk data. Since the malicious signer can send a large number of requests within a single call, they can DoS Emily with just a few calls.

Impact Details

A malicious signer can:

  • Manipulate data for all legitimate withdrawal requests in Emily’s database.

  • Cause a DoS by exhausting Emily's memory and filling its database with junk data.

References

None

Proof of Concept

Proof of Concept

In this PoC, we modify the amount and status of a legitimate withdrawal request (emitted and caught by new_block.rs), and we also inject 999 junk records (each being a copy of that legitimate request) into Emily’s database.

To test the scenario please apply the following changes.

Changes to new_block.rs:

Add the following test case to sbtc/signer/tests/integration/stacks_events_observer.rs:

Run the test:

Results:

Was this helpful?