#43290 [BC-Critical] Anyone can send a write_batch to the DA node, enabling a DOS attack that shuts down the network
Submitted on Apr 4th 2025 at 10:43:24 UTC by @niroh for Attackathon | Movement Labs
Report ID: #43290
Report Type: Blockchain/DLT
Report severity: Critical
Target: https://github.com/immunefi-team/attackathon-movement/tree/main/protocol-units/da/movement/protocol/light-node
Impacts:
Network not being able to confirm new transactions (total network shutdown)
Description
Vulnerability Details
The block building flow in movement includes the following steps (based on the walkthrough description of how the system will work initially, with a single validator):
a (single) full node accepts submit_transaction requests from users
The node applies various logic and validations (including transaction validation, prioritization, using core_mempool logic etc.) to create a transaction batch that is sent to the da light node every 2 seconds.
The DA light node receives these batches (through the write_batch api) and adds them to its own mempool (RockDB)
Periodically the DA light node constructs blocks from its mempool (every 0.5 seconds or when at least 2048 txs accumulate) and writes them to celestia
The full node fetches the ordered block from celestia, executes it and adds it to its da_db.
The issue arises from the fact the the DA node has no sender validation in place when it receives batches through write_batch. It only validates that the transactions themselves are well formatted and properly signed. This means anyone can generate a batch of well formed, well signed transactions and send it to the DA node, completely bypassing any full node logic or safeguards such as sending schedule, full transaction validation, sequence number validity, batch size limits/timing and so on.
This enables various attacks (e.g. front running mempool transactions by timing a batch to be sent before the IngressTask window ends) however the most severe is causing a full network DOS using the following scenario:
The following describes the attack scenario:
The attacker creates batches of transactions that are well formed and signed, but are designed to fail execution without having any gas charged, for example by sending them from an account with zero balance to pay gas. Had these transactions been submitted through the full node they would never reach execution, because of the TransactionPipe validations, but since the attacker submits directly to the DA node, they are accepted.
The attacker sets the gas price of all transactions to the highest possible value (knowing they will never get charged).
The batches are of the largest size acceptable by the DA node API. Since no batch size limit is enforced by the DA, the only limit is the API message size limit (typically defaults at 4Mb) enabling batches of thousands of transactions.
The attacker sends the batches automatically at millisecond intervals.
The DA node builds a block every 0.5 seconds, or whenever more than 2048 transactions are waiting in the mempool as can be seen here (block_size is set to 2048 by default):
This means each huge batch would trigger a block building cycle (creating a Block from the dummy transactions, writing it to celestia)
The full node will picks block from the DA as they are written, as can be seen here:
This means the stuffed blocks will be constantly picked up from the DA and executed by the full node.
Since there are no restrictions on the number of transactions per block, nor on block execution gas or block building frequency, the full node will eventually lag to the point where legitimate transactions are never processed (or processed with such delay that they are likely to expire.)
Impact Details
As described above, the result of the attack is a full network shutdown since legitimate transactions can not be processed it time.
Proof of Concept
Proof of Concept
The following POC shows how anyone can submit a transaction batch to the DA lightnode (no special authrization required) and the transaction will be picked up by the da node, and trigger a block that will be executed by the full node.
To run:
First run Movement init using the CLI to initialize and fund the default account defined in the config.yaml file under movement-client.
Run just movement-celestia-da-light-node native build.setup.test.local to launch the test environment
Copy the default account private key to the marked place in the test function
Add the test function code below to mod.rs under movement-client/src/tests and run it with cargo test
Check the DA node log files to see that a block has been created based on the batch_write, and the full node logs to see that the block (and the test transaction) were executed successfully.
test function
Was this helpful?