#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.
gas_unit_price (highest first)
addition to rocksdb timestamp (oldest first)
sequence nr (lowest first)
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-L36Source: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
Tx4, Result: Discard(SEQUENCE_NUMBER_TOO_NEW)
Tx3, Result: Discard(SEQUENCE_NUMBER_TOO_NEW)
Tx2, Result: Discard(SEQUENCE_NUMBER_TOO_NEW)
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?