#42896 [BC-High] attackers can exploit sequence number tolerance mechanism to to cause movement network da lightnode loose money for submitting failed blocks to celestia

#42896 [BC-High] Attackers can exploit sequence_number tolerance mechanism to to cause Movement Network DA Lightnode loose money for submitting failed blocks to Celestia

Submitted on Mar 28th 2025 at 14:18:07 UTC by @perseverance for Attackathon | Movement Labs

  • Report ID: #42896

  • Report Type: Blockchain/DLT

  • Report severity: High

  • Target: https://github.com/immunefi-team/attackathon-movement/tree/main/protocol-units/execution/maptos/opt-executor

  • Impacts:

    • Direct loss of funds

    • 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

Background Information and Vulnerability Details

sequence_number tolerance

When users submit transactions to mempool, it is allowed that the sequence number is in range from commited_sequence_number (that is sequence number of the last succesfull transaction of the user) + 1 to commited_sequence_number + TOO_NEW_TOLERANCE ( = 32) .

The sequencer node check for valid sequence number in submit_transaction function.

https://github.com/immunefi-team/attackathon-movement/blob/a2790c6ac17b7cf02a69aea172c2b38d2be8ce00/protocol-units/execution/maptos/opt-executor/src/background/transaction_pipe.rs#L267-L272

The check for sequence number can be seen in the function has_invalid_sequence_number

https://github.com/immunefi-team/attackathon-movement/blob/a2790c6ac17b7cf02a69aea172c2b38d2be8ce00/protocol-units/execution/maptos/opt-executor/src/background/transaction_pipe.rs#L179-L218

So 1 user can submit maximum 32 transactions with sequence number from commited_sequence_number + 1 to commited_sequence_number + 32.

Validation separate transaction before commiting to the mempool

Please note that when user submit a transaction to the mempool, the transaction is validated against the latest state view of the blockchain. Each transaction is validated separately. The behavior is seen in the code as following.

As comment and the code show that each transaction is validated with re-creation with latest database.

https://github.com/immunefi-team/attackathon-movement/blob/a2790c6ac17b7cf02a69aea172c2b38d2be8ce00/protocol-units/execution/maptos/opt-executor/src/background/transaction_pipe.rs#L250-L253

So if each transaction passed the validation then it is included in the mempool.

Lifecycle of a transaction

The transactions passed validation from the mempool ==> included in the mempool ==> submit (or batch_write) to DA Lightnode

Then the transactions are included in blocks and sent from DA Lightnode to Celestia. Code is here: https://github.com/immunefi-team/attackathon-movement/blob/a2790c6ac17b7cf02a69aea172c2b38d2be8ce00/protocol-units/sequencing/memseq/sequencer/src/lib.rs#L101-L131

So this function wait_for_next_block batch the transactions to build the block the DA Lightnode batch maximum 10 transactions to a block and build blob to send to Celestia.

The blob will be signed and pay for gas by DA Lightnode.

Executor get / read Blocks from Celestia and execute blocks.

The cost of submitting blob Data is covered by Movement Team and is higher than the cost of a transaction

Gas Unit Price

For the Gas Unit Price Analytics show that users usually submit transactions with the Gas Unit Price from 100 to 125 Octas.

See analytics here: https://explorer.movementnetwork.xyz/analytics?network=mainnet

So the gas unit price is in range from 100 to 200 Octas Move.

Suppose the cost is 200 Octas Move

MOVe price = 0.54 USD

Gas Fee of a simple transfer would be = 0.000402 MOVE = 40_200 Octas MOVE let's assum maximum to be 50_000 Octas

=> Cost of gas fee for a simple transfer : 0.0005 MOVE * 0.54 = 0.00027 USD

Cost of submitting blobs to Celestia

So take some example transactions in celestia, https://celenium.io/tx/9b00e984362bfd0db1b67906608829cf3d9d6a6994e36178bc2de2a9781f9044?tab=messages

The cost of gas on Celestia is 0.034763 for 1019 Bytes => TIA Price = 3.66 ( https://coinmarketcap.com/vi/currencies/celestia/) => Cost for 1 transaction: 0.05 * 3.65 = 0.183 USD

The maximum size of a blob is 2 MB. The blob of blocks that can maximum contains 10 transactions. So the data probably can be bigger and cost more gas, but let's take 0.183 USD as sample for this

So you can notice that the gas pay by users is much less than the gas pay by DA Lightnode.

Attack Scenario

So attacker can submit a transaction 32 transactions as followed

The attacker create 31 transactions with maximum payload data to cause maximum gas pay by DA Lightnode to submit blobs to Celestia.

Attack Analysis

So the attacker sent all of this transactions to the mempool in 1 seconds, so all the transactions entered the mempool.

Each transaction will be validated separately. So each transaction will pass validation. So each transaction is included in the mempool.

They will be sent to DA Lightnode.

The transactions will be included in at least 4 blobs. because each blob can contain only maximum 10 transactions.

When the executor get the blob and execute the transactions in sequence.

  • Transaction 1 will pass. After transaction 1, Attacker_Account_1 sent 0.9995 MOVe and spent 0.0005 Move for gas.

Attacker_Account_1 balance = 0 Attacker_Account_2 balance = 0.9995 MOVe

Governed_gas_pool of Movement received 0.0005 Move

  • After transaction 1, Attacker_Account_1 balance = 0 so all other transactions will fail

The Movement Network received nothing.

But the Movement DA Lightnode need to spend gas to submit to Celestia for at least 3 blobs.

=> Cost for 1 transaction: 0.05 * 3.65 = 0.183 USD

=> Cost for 3 transactions: 0.183 * 3 = 0.549 USD

Cost of attack and Impact analysis

So the cost for attacker: 0.0005 Move = 0.00027 USD

The damage for the Movement project: 0.549 USD - 0.00027 USD = 0.54873

The the cost attack ratio per damage is 1 : 2032 that is quite huge number.

So suppose the cost for attacker is 1000 USD => The damage for project: 1000 * 2032 = 2_032_000 USD

The cost for Movement Network can be bigger when take into calculation the actual payload size of blob. In above calculation, I took the gas paid for a simpe small payload of 1019 Bytes.

With this huge ratio, then if the attacker can gain some profit to cover the cost, then he is likely to execute the attack.

Severity Assessment

Bug Severity: Critical

Impact:

  • Loss of funds for Movement Project

  • 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

Impact:

  • Caused DA Lightnode to spend much gas to pay for failed transactions

  • Resource waste: The sequencer and executor executed almost failed transactions.

  • Severe degredation quality of service since other transactions of valid users can have some delay

Likelihood:

  • High as No special privileges required

  • Can be executed by any user with very cheap cost of attack

The cost of attack vs the damage for project is 1: 2032 ratio

So the cost to cause total denial of service of Movement for 1 day is can be just 7.5 USD depends on how much spam transactions. It is very cheap.

the cost to cause total denial of service of Movement for 1 month is just 224 USD . It is very cheap.

So suppose the cost for attacker is 1000 USD => The damage for project: 1000 * 2032 = 2_032_000 USD

Proof of Concept

Proof of concept

The following attack scenario demonstrate the bug.

Step 1: Setup.

Step 2: The attacker sends 32 transactions within 1 seconds, and these transactions enter the mempool. To bypass the rate limit, can send transactions from different machines, IPs

Step 3: Repeat the attack to continue. Can create more accounts if needed

Exact attack scenario can be refined with more test, but can be easily done.

Attackers can create a script to execute this attack.

Was this helpful?