#43288 [BC-Critical] Attackers could force Nodes to process TraAttackers could force Nodes to process Transactions in wrong order, by attacking moveRocks/sequencing implementation

Submitted on Apr 4th 2025 at 10:38:09 UTC by @Berserk for Attackathon | Movement Labs

  • Report ID: #43288

  • Report Type: Blockchain/DLT

  • Report severity: Critical

  • Target: https://github.com/immunefi-team/attackathon-movement/tree/main/protocol-units/sequencing/memseq/sequencer

  • Impacts:

    • Causing network processing nodes to process transactions from the mempool beyond set parameters

    • Temporary freezing of network transactions by delaying one block by 500% or more of the average block time of the preceding 24 hours beyond standard difficulty adjustments

Description

Brief/Intro

There is a vulnerability in the sequencer implementation that allows attackers to force nodes to process transactions from the same account in a wrong sequence number order. (e.g., 3 transactions from Account A: seqnr 1, seqnr 2, seqnr 3. Attackers will force nodes to process tx with seq 3 first, seq 2 second, and seqnr 1 last, leading to all transactions failing (other than seqnr 1 of course)) The mempool allows 32 transactions per account (by abusing this, attackers could force nodes to process at least 31 invalid transactions per account at a time at no cost)

Vulnerability Details

Root cause brief

Ordering of transactions in MoveRocks/RocksDB prioritizes the gas_unit_price first and doesn't differentiate between accounts. So if we have 2 transactions in rocks db from the same account:

  • Transaction 1: seqnr 1 / gas unit price 100

  • Transaction 2: seqnr 2 / gas unit price 150

When the sequencer tries to build the block, it will put Transaction 2 first and then Transaction 1 because Transaction 2 has a bigger gas unit price. => This will mess up the order of transactions as they should be executed in correct ascending seqnr order (so in this case Transaction 2 will fail because seqnr 2 > account_seqnr(0) + 1)

Detailed description of implementation

The sequencer relies on a newly developed mempool implementation that relies on rocks DB. when recieving transactions from the full node through batch_write, the sequencer will insert the batch in the database and will construct for each one of them a key to allow for ordering/prioritizing.

Source:protocol-units/mempool/move-rocks/src/lib.rs#L22-L36

As we can see the first part of the key used contains the transaction application priority, which is U64::max - transaction.gas_unit_price

When building a new block to be submitted to DA the sequencer will pop transactions from the RockDB database:

The order by which those transactions stored in the db wil be poped is defiend by the key constructed.

  1. gas_unit_price (highest first)

  2. addition to rocksdb timestamp (oldest first)

  3. sequence nr (lowest first)

  4. transaction id

Meaning if two transactions from same account if they have a differing gas_unit_price we will order them first based on the gas_unit_price and not the sequence_nr

Impact Details

  • Force network processing nodes to process transactions in the wrong sequence number order

  • DoS network by flooding the network with up to 31 invalid transactions per account at a time at no cost

References

  • Source:protocol-units/mempool/move-rocks/src/lib.rs#L22-L36

  • Source:protocol-units/sequencing/memseq/sequencer/src/lib.rs#L100-L122

Proof of Concept

Proof of Concept

Attack Scenario

To demonstrate the vulenrabilty we will create a batch of 4 transactions and submit them through the rpc. The batch contains the following transactions (valid transfer transactions):

  • Tx1: sender: Account A, seqnr 0, gas_unit_price 100

  • Tx2: sender: Account A, seqnr 1, gas_unit_price 110

  • Tx3: sender: Account A, seqnr 2, gas_unit_price 120

  • Tx4: sender: Account A, seqnr 3, gas_unit_price 130

All of the transactions provided are valid transactions and the order is correct when submitting it through the rpc.

However after exectuting the 4 transactions we will notice from the fullnode logs that the order in which the transactions are executed

  1. Tx4, Result: Discard(SEQUENCE_NUMBER_TOO_NEW)

  2. Tx3, Result: Discard(SEQUENCE_NUMBER_TOO_NEW)

  3. Tx2, Result: Discard(SEQUENCE_NUMBER_TOO_NEW)

  4. Tx1, Result: Keep(Success)

Coded Poc

  • main.rs: https://gist.github.com/aliX40/92e94f9713d17ab22d5801958003f7a2

  • cargo.toml: https://gist.github.com/aliX40/1257e284afc4b01f5f5430d5e37d0a9b

  • full node log: https://gist.github.com/aliX40/72da56e976a6aeabbe3d42860c43d0f0

By executing the main.rs script the following result was printed:

To check the execution result of the 4 submitted transactions we need to grep for compute_status_for_input_txns in the full node logs:

To check the full logs of the full node, please see https://gist.github.com/aliX40/72da56e976a6aeabbe3d42860c43d0f0

Was this helpful?