Incorrect Behavior Order: Early Amplification

Description

Incorrect Behavior Order: Early Amplification is a vulnerability where a product allows an entity to perform a legitimate but expensive operation before authentication or authorization has been completed. This creates a denial-of-service vulnerability because attackers can trigger resource-intensive operations without proving their identity or having appropriate permissions. Common examples include loading files into memory before checking ownership, performing database queries before authentication, or executing cryptographic operations for unauthenticated requests.

Risk

Early amplification vulnerabilities enable denial-of-service attacks with minimal attacker effort. Unauthenticated users can exhaust server resources by repeatedly triggering expensive operations like file reads, database queries, or cryptographic computations. Since no authentication is required, attackers can make unlimited requests from spoofed or rotating IP addresses. The asymmetry between cheap attacker requests and expensive server operations creates an economic advantage for attackers. Systems may crash, become unresponsive, or deny service to legitimate users while processing malicious requests.

Solution

Always perform authentication and authorization checks before executing expensive operations. Structure code so that identity verification occurs at the earliest possible point in request processing. Implement rate limiting for pre-authentication endpoints. Use lazy loading patterns that defer resource-intensive operations until authorization is confirmed. Cache authentication results to avoid repeated expensive checks. Design APIs so that anonymous access is limited to lightweight operations only. Apply the principle of failing fast—reject unauthorized requests before committing resources.

Common Consequences

ImpactDetails
AvailabilityScope: Availability

DoS: Amplification - Attackers can trigger expensive operations without authentication. DoS: Crash, Exit, or Restart - System resources, CPU and memory, can be quickly consumed. This can lead to poor system performance or system crash.

Example Code

Vulnerable Code

<?php
// Vulnerable: Loads entire file before checking authorization
function vulnerableDownloadFile($filename, $username) {
    // Vulnerable: Expensive I/O operation BEFORE authorization
    $file = file_get_contents($filename);

    // Authorization check happens AFTER expensive operation
    if ($file && isOwnerOf($username, $filename)) {
        echo $file;
        return true;
    }

    return false;  // Resources already wasted
}

// Attacker can exhaust disk I/O and memory by requesting
// large files they don't own - the file is still loaded!
?>
# Vulnerable: Database query before authentication
def vulnerable_get_user_data(user_id, auth_token):
    # Vulnerable: Expensive database query BEFORE auth check
    user_data = database.query(
        "SELECT * FROM users WHERE id = %s",
        user_id
    )

    # Expensive joins and aggregations
    orders = database.query(
        "SELECT * FROM orders WHERE user_id = %s",
        user_id
    )

    # Auth check happens AFTER database work
    if not validate_token(auth_token, user_id):
        return None  # Database resources already consumed

    return {"user": user_data, "orders": orders}
// Vulnerable: Cryptographic operation before authentication
public class VulnerableAuthService {

    public boolean authenticate(String username, String password) {
        // Vulnerable: Expensive key derivation BEFORE checking if user exists
        byte[] hashedPassword = PBKDF2.hash(
            password,
            getSalt(username),  // May not even exist
            100000  // Expensive iterations
        );

        // Database lookup happens AFTER expensive crypto
        User user = userRepository.findByUsername(username);

        if (user == null) {
            return false;  // Crypto resources wasted
        }

        return Arrays.equals(hashedPassword, user.getPasswordHash());
    }
}
// Vulnerable: Memory allocation before authentication
#include <stdlib.h>

int vulnerable_process_request(int client_fd) {
    // Vulnerable: Allocates large buffer BEFORE authentication
    char *buffer = malloc(10 * 1024 * 1024);  // 10MB
    if (!buffer) return -1;

    // Read potentially large payload
    ssize_t bytes = recv(client_fd, buffer, 10 * 1024 * 1024, 0);

    // Authentication happens AFTER memory allocation and I/O
    struct auth_header *auth = parse_auth_header(buffer);
    if (!validate_auth(auth)) {
        free(buffer);
        return -1;  // Memory already allocated and used
    }

    process_data(buffer, bytes);
    free(buffer);
    return 0;
}

Fixed Code

<?php
// Fixed: Check authorization BEFORE loading file
function secureDownloadFile($filename, $username) {
    // Fixed: Authorization check FIRST
    if (!isOwnerOf($username, $filename)) {
        return false;  // Fail fast, no resources used
    }

    // Expensive operation only after authorization confirmed
    $file = file_get_contents($filename);

    if ($file) {
        echo $file;
        return true;
    }

    return false;
}

// Alternative: Stream file instead of loading entirely
function secureStreamFile($filename, $username) {
    // Fixed: Check authorization first
    if (!isOwnerOf($username, $filename)) {
        http_response_code(403);
        return;
    }

    // Check file exists without loading
    if (!file_exists($filename)) {
        http_response_code(404);
        return;
    }

    // Stream file to reduce memory usage
    readfile($filename);
}
?>
# Fixed: Authenticate BEFORE expensive operations
def secure_get_user_data(user_id, auth_token):
    # Fixed: Validate authentication FIRST
    if not validate_token(auth_token, user_id):
        raise AuthenticationError("Invalid token")

    # Fixed: Check authorization before data access
    if not is_authorized_for_user(auth_token, user_id):
        raise AuthorizationError("Access denied")

    # Expensive operations only after auth confirmed
    user_data = database.query(
        "SELECT * FROM users WHERE id = %s",
        user_id
    )

    orders = database.query(
        "SELECT * FROM orders WHERE user_id = %s",
        user_id
    )

    return {"user": user_data, "orders": orders}


# Fixed: Rate limit unauthenticated endpoints
from functools import wraps
import time

def rate_limit(max_per_minute):
    def decorator(f):
        calls = {}
        @wraps(f)
        def wrapper(*args, **kwargs):
            ip = get_client_ip()
            now = time.time()
            calls[ip] = [t for t in calls.get(ip, []) if now - t < 60]
            if len(calls[ip]) >= max_per_minute:
                raise RateLimitExceeded()
            calls[ip].append(now)
            return f(*args, **kwargs)
        return wrapper
    return decorator
// Fixed: Check user existence before expensive crypto
public class SecureAuthService {

    public boolean authenticate(String username, String password) {
        // Fixed: Lightweight check FIRST
        User user = userRepository.findByUsername(username);

        if (user == null) {
            // Fixed: Fail fast without expensive crypto
            // Add small delay to prevent user enumeration
            simulateHashDelay();
            return false;
        }

        // Fixed: Expensive operation only for valid users
        byte[] hashedPassword = PBKDF2.hash(
            password,
            user.getSalt(),
            100000
        );

        return MessageDigest.isEqual(hashedPassword, user.getPasswordHash());
    }

    private void simulateHashDelay() {
        // Prevent timing attacks while avoiding expensive crypto
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
// Fixed: Authenticate before allocating resources
#include <stdlib.h>

int secure_process_request(int client_fd) {
    // Fixed: Read only the auth header first (small buffer)
    char auth_buffer[256];
    ssize_t auth_bytes = recv(client_fd, auth_buffer, sizeof(auth_buffer),
                              MSG_PEEK);  // Peek without consuming

    // Fixed: Validate authentication BEFORE large allocation
    struct auth_header *auth = parse_auth_header(auth_buffer);
    if (!validate_auth(auth)) {
        return -1;  // No resources allocated for invalid requests
    }

    // Fixed: Now allocate resources for authenticated request
    size_t content_length = get_content_length(auth_buffer);

    // Fixed: Validate content length before allocation
    if (content_length > MAX_ALLOWED_SIZE) {
        return -1;
    }

    char *buffer = malloc(content_length);
    if (!buffer) return -1;

    // Consume the peeked data and read the rest
    ssize_t bytes = recv(client_fd, buffer, content_length, 0);

    process_data(buffer, bytes);
    free(buffer);
    return 0;
}

CVE Examples

  • CVE-2004-2458 — Tool creates directories before authenticating user, allowing unauthenticated directory creation.

References

  1. MITRE Corporation. "CWE-408: Incorrect Behavior Order: Early Amplification." https://cwe.mitre.org/data/definitions/408.html
  2. OWASP. "Denial of Service Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html