#43177 [BC-Critical] dos vulnerability in da light node via unbounded height parameter

#43177 [BC-Critical] DoS Vulnerability in DA Light Node via Unbounded Height Parameter

Submitted on Apr 3rd 2025 at 09:41:01 UTC by @Blockian for Attackathon | Movement Labs

  • Report ID: #43177

  • 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

Movement Bug Report

DoS Vulnerability in DA Light Node via Unbounded Height Parameter

Summary

The stream_read_from_height gRPC endpoint in the DA Light Node lacks proper validation on the height parameter. An attacker can exploit this by setting height = 0, effectively requesting all blobs from genesis. As the network grows over time, this results in an increasingly large data request, which can overwhelm the node, leading to a denial of service (DoS) scenario.

Root Cause Analysis

The stream_read_from_height endpoint accepts a user-controlled height parameter and internally calls the stream_da_blobs_from_height function:

	fn stream_da_blobs_from_height(
		&self,
		start_height: u64,
	) -> Pin<Box<dyn Future<Output = Result<DaBlobStream<C>, DaError>> + Send + '_>> {
		let certificate_stream = self.stream_certificates().await?;

				// ... (omitted non-relevant code)

				let mut last_height = start_height;
				let mut certificate_stream = certificate_stream;

				while let Some(certificate) = certificate_stream.next().await {
					match certificate {
						Ok(Certificate::Height(height)) if height > last_height => {
							let blob_stream = self
								.stream_da_blobs_between_heights(last_height, height)
								.await?;
							tokio::pin!(blob_stream);

							while let Some(blob) = blob_stream.next().await {
								yield blob?;
							}

							// ... (omitted non-relevant code)
	}

Here, the function stream_da_blobs_from_height uses the user-supplied start_height to stream blobs from the earliest specified height up to the latest known height (retrieved from stream_certificates). Without validation, this can lead to excessive and unbounded data retrieval.

Impact

If an attacker sets start_height = 0 and the latest height is large, the node will:

  • Enter a long-running loop while attempting to stream all blobs from genesis.

  • Suffer from performance degradation or potentially become unresponsive.

  • Compromise the availability and liveness of the network by tying up resources.

Proposed Fixes

  • Introduce a maximum allowed range between start_height and the latest height when calling stream_da_blobs_between_heights.

  • Implement a failsafe to break out of the loop if the stream exceeds a predefined safe limit.

Proof of Concept

Proof of Concept (PoC)

  1. Start the DA Light Node.

  2. Set up a fake DA source populated with a large number of blobs across a high range of heights.

  3. Send a gRPC request to the Light Node’s stream_read_from_height endpoint with height = 0.

Was this helpful?