Skip to content

Denial of Service

id: LS01H
title: Denial of Service 
baseSeverity: H
category: denial-of-service
language: solidity
blockchain: [ethereum]
impact: Contract becomes unusable or stuck
status: draft
complexity: low
attack_vector: external
mitigation_difficulty: medium
versions: [">=0.4.0", "<0.8.21"]
cwe: CWE-400
swc: SWC-113

๐Ÿ“ Description

  • Denial of Service (DoS) in smart contracts occurs when an attacker can prevent legitimate users from interacting with a contract, either temporarily or permanently.
  • This can be achieved by exploiting resource-intensive operations (e.g., unbounded loops), failing external calls, unexpected reverts, or storage manipulation that causes subsequent operations to fail.
  • In Ethereum, since each transaction has a gas limit, a common DoS vector is introducing logic that exceeds the block gas limit or causes reversion due to invalid assumptions (like looping through a growing array).

๐Ÿšจ Vulnerable Code

mapping(address => uint[]) public userDeposits;

function withdrawAll() external {
    uint[] storage deposits = userDeposits[msg.sender];
    for (uint i = 0; i < deposits.length; i++) {
        _processWithdrawal(deposits[i]); // Assume this costs significant gas
    }
    delete userDeposits[msg.sender];
}

๐Ÿงช Exploit Scenario

Step-by-step exploit process:

  1. Attacker makes hundreds or thousands ...[msg.sender].
  2. Calls withdrawAll() which loops over all entries.
  3. The loop execution exceeds the block gas limit โ†’ reverts.
  4. The attacker is now permanently locked out.

Assumptions:

  • The contract allows unlimited or unbounded entries into arrays or mappings.
  • There is no gas-bound safety mechanism in looping constructs.

โœ… Fixed Code

function withdrawBatch(uint start, uint end) external {
    uint[] storage deposits = userDeposits[msg.sender];
    require(end <= deposits.length, "Invalid end index");

    for (uint i = start; i < end; i++) {
        _processWithdrawal(deposits[i]);
    }

    if (end == deposits.length) {
        delete userDeposits[msg.sender];
    }
}

๐Ÿงญ Contextual Severity

- context: "Reward, withdrawal, or governance functions blocked by one user"
  severity: H
  reasoning: "Entire system availability is compromised"
- context: "Batch loops in admin-only or optional functions"
  severity: M
  reasoning: "Can be circumvented but affects UX"
- context: "Non-critical DoS affecting display or logging"
  severity: L
  reasoning: "No impact on funds or control flow"

๐Ÿ›ก๏ธ Prevention

Primary Defenses

  • Avoid unbounded loops in external/public functions.
  • Introduce batching mechanisms (e.g., start and end indices).
  • Set gas guards and enforce maximum data sizes where feasible.

Additional Safeguards

  • Implement rate limiting or per-call caps.
  • Use gas estimation during testing to simulate worst-case scenarios.

Detection Methods

  • Detect via static analysis tools (e.g., Slither: unbounded-loop, dos detectors).
  • Fuzzing and gas profiling during QA.
  • Formal verification of loop safety and invariants.

๐Ÿ•ฐ๏ธ Historical Exploits

  • Name: GovernMental DoS Vulnerability
  • Date: 2016-06-11
  • Loss: N/A
  • Post-mortem: Link to post-mortem

๐Ÿ“š Further Reading


โœ… Vulnerability Report

id: LS01H
title: Denial of Service
severity: H
score:
impact: 5        
exploitability: 4 
reachability: 5   
complexity: 1     
detectability: 3  
finalScore: 4.3

๐Ÿ“„ Justifications & Analysis

  • Impact: A well-crafted DoS can freeze user funds or key contract functionality, leading to major trust and financial loss.
  • Exploitability: The attack can be triggered externally with minimal effort (e.g., looping, reverting).
  • Reachability: Public/external functions without restrictions can be exploited at will.
  • Complexity: Attack only requires interacting with the contract normally (e.g., through deposits or submissions).
  • Detectability: While many tools detect unbounded loops, nuanced cases (e.g., user-sourced data) may be overlooked without manual review.