Allocation of Resources Without Limits or Throttling

Description

Allocation of Resources Without Limits or Throttling occurs when a product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated. This allows attackers to consume excessive amounts of resources such as memory, CPU cycles, file handles, network connections, disk space, or database connections. Without proper limits or throttling mechanisms, a single malicious or misbehaving client can exhaust resources and cause denial of service for all users of the system.

Risk

Resource exhaustion attacks are among the most common denial of service vectors. CVE-2025-69228 in aiohttp allows attackers to exhaust server memory through specially crafted HTTP POST requests. CVE-2025-66560 in Quarkus causes worker thread exhaustion when clients drop connections during response transmission, leading to complete service denial. CVE-2025-48976 in Apache Commons FileUpload enables DoS through multipart headers without size limits. CVE-2024-26308 in Apache Commons Compress affects file decompression. These vulnerabilities can bring down entire services, affecting business continuity, and in cloud environments can also lead to significant unexpected costs.

Solution

Implement strict resource limits at all levels. Set maximum sizes for uploads, request bodies, and allocations. Implement rate limiting per user, IP, and globally. Use connection pooling with fixed maximum sizes. Set timeouts on all operations. Implement backpressure mechanisms to slow down when resources are constrained. Monitor resource consumption and alert on anomalies. Use circuit breakers to prevent cascade failures. Design systems to degrade gracefully under load rather than crash. Configure web servers and frameworks with appropriate limits.

Common Consequences

ImpactDetails
AvailabilityScope: Denial of Service

Resource exhaustion prevents legitimate users from accessing the service.
FinancialScope: Cost Amplification

In cloud environments, attackers can trigger significant computing and storage costs.
System StabilityScope: Cascade Failures

Resource exhaustion in one component can trigger failures in dependent systems.

Example Code + Solution Code

Vulnerable Code

# VULNERABLE: No limit on upload size
from flask import Flask, request

app = Flask(__name__)

@app.route('/upload', methods=['POST'])
def upload():
    # No size limit - attacker can upload gigabytes
    file = request.files['file']
    file.save(f'/uploads/{file.filename}')
    return 'OK'

# VULNERABLE: Unbounded list allocation
@app.route('/process', methods=['POST'])
def process():
    data = request.json
    # No limit on items array size
    items = data.get('items', [])  # Could be millions of items
    results = [process_item(item) for item in items]
    return jsonify(results)

# VULNERABLE: No rate limiting
@app.route('/api/search')
def search():
    # Attacker can make unlimited requests
    query = request.args.get('q')
    return jsonify(expensive_search(query))
// VULNERABLE: Unbounded thread creation
public class RequestHandler {
    public void handleRequest(Socket socket) {
        // Creates new thread for each request - no limit!
        new Thread(() -> processRequest(socket)).start();
    }
}

// VULNERABLE: No memory limit on data structures
public class DataCollector {
    private List<Record> records = new ArrayList<>();

    public void addRecord(Record record) {
        // List can grow without bound
        records.add(record);
    }
}

// VULNERABLE: No limit on file reading
public String readFile(String path) throws IOException {
    // Could be a multi-gigabyte file
    return new String(Files.readAllBytes(Paths.get(path)));
}
// VULNERABLE: No request body size limit
const express = require('express');
const app = express();

app.use(express.json());  // Default has high limit

app.post('/data', (req, res) => {
    // req.body could be enormous
    const data = req.body;
    processData(data);
    res.json({ status: 'ok' });
});

// VULNERABLE: Regex without timeout (ReDoS)
app.get('/validate', (req, res) => {
    const email = req.query.email;
    // Evil regex with catastrophic backtracking
    const valid = /^([a-z]+)+@[a-z]+\.[a-z]+$/.test(email);
    res.json({ valid });
});

Fixed Code

# SAFE: Request size limits and rate limiting
from flask import Flask, request, jsonify
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16 MB max

limiter = Limiter(
    app=app,
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)

@app.route('/upload', methods=['POST'])
@limiter.limit("10 per minute")
def upload_safe():
    file = request.files.get('file')
    if not file:
        return 'No file', 400

    # Additional size check
    file.seek(0, 2)  # Seek to end
    size = file.tell()
    file.seek(0)

    if size > app.config['MAX_CONTENT_LENGTH']:
        return 'File too large', 413

    # Save with secure filename
    filename = secure_filename(file.filename)
    file.save(f'/uploads/{filename}')
    return 'OK'

# SAFE: Bounded list processing
MAX_ITEMS = 1000

@app.route('/process', methods=['POST'])
@limiter.limit("30 per minute")
def process_safe():
    data = request.json

    if not isinstance(data, dict):
        return 'Invalid request', 400

    items = data.get('items', [])

    if len(items) > MAX_ITEMS:
        return f'Too many items (max {MAX_ITEMS})', 400

    results = [process_item(item) for item in items]
    return jsonify(results)

# SAFE: Rate limited expensive operation
@app.route('/api/search')
@limiter.limit("10 per minute")
def search_safe():
    query = request.args.get('q', '')

    if len(query) > 100:
        return 'Query too long', 400

    return jsonify(expensive_search(query))
// SAFE: Thread pool with fixed size
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SecureRequestHandler {
    private static final int MAX_THREADS = 100;
    private static final int MAX_PENDING = 1000;

    private final ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS);
    private final Semaphore semaphore = new Semaphore(MAX_PENDING);

    public void handleRequest(Socket socket) throws InterruptedException {
        // Limit pending requests
        if (!semaphore.tryAcquire(5, TimeUnit.SECONDS)) {
            socket.close();
            throw new RejectedExecutionException("Too many pending requests");
        }

        executor.submit(() -> {
            try {
                processRequest(socket);
            } finally {
                semaphore.release();
            }
        });
    }
}

// SAFE: Bounded data structure
public class BoundedDataCollector {
    private static final int MAX_RECORDS = 10000;
    private final List<Record> records = new ArrayList<>();

    public synchronized void addRecord(Record record) throws CapacityExceededException {
        if (records.size() >= MAX_RECORDS) {
            throw new CapacityExceededException("Maximum records reached");
        }
        records.add(record);
    }
}

// SAFE: Streaming file reading with limits
public String readFileSafe(String path, long maxBytes) throws IOException {
    Path filePath = Paths.get(path);
    long fileSize = Files.size(filePath);

    if (fileSize > maxBytes) {
        throw new IOException("File too large: " + fileSize + " bytes");
    }

    // Read with limit
    try (InputStream is = Files.newInputStream(filePath);
         ByteArrayOutputStream bos = new ByteArrayOutputStream()) {

        byte[] buffer = new byte[8192];
        long totalRead = 0;
        int read;

        while ((read = is.read(buffer)) != -1) {
            totalRead += read;
            if (totalRead > maxBytes) {
                throw new IOException("File exceeded max size during read");
            }
            bos.write(buffer, 0, read);
        }

        return bos.toString(StandardCharsets.UTF_8);
    }
}
// SAFE: Express with proper limits
const express = require('express');
const rateLimit = require('express-rate-limit');
const slowDown = require('express-slow-down');

const app = express();

// Limit request body size
app.use(express.json({ limit: '100kb' }));
app.use(express.urlencoded({ limit: '100kb', extended: true }));

// Global rate limiter
const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // Limit each IP to 100 requests per window
    message: 'Too many requests, please try again later'
});

// Speed limiter - slows down repeat requests
const speedLimiter = slowDown({
    windowMs: 15 * 60 * 1000,
    delayAfter: 50,
    delayMs: 500
});

app.use(limiter);
app.use(speedLimiter);

app.post('/data', (req, res) => {
    const data = req.body;

    // Validate data structure
    if (!data || typeof data !== 'object') {
        return res.status(400).json({ error: 'Invalid data' });
    }

    // Check array sizes
    if (data.items && data.items.length > 100) {
        return res.status(400).json({ error: 'Too many items' });
    }

    processData(data);
    res.json({ status: 'ok' });
});

// SAFE: Use safe regex library or validate input length
const validator = require('validator');

app.get('/validate', (req, res) => {
    const email = req.query.email || '';

    // Limit input length to prevent ReDoS
    if (email.length > 254) {
        return res.json({ valid: false });
    }

    // Use well-tested validation library
    const valid = validator.isEmail(email);
    res.json({ valid });
});

Exploited in the Wild

aiohttp Memory Exhaustion (aiohttp, 2025)

CVE-2025-69228 in aiohttp 3.13.2 and earlier allows attackers to send specially crafted HTTP POST requests that cause unbounded memory allocation, leading to server crashes and denial of service for asynchronous Python web applications.

Quarkus Worker Thread Exhaustion (Quarkus, 2025)

CVE-2025-66560 in Quarkus before 3.31.0 causes worker threads to block indefinitely when client connections drop during HTTP response transmission, leading to thread pool exhaustion and complete service denial.

Apache Commons FileUpload DoS (Apache, 2025)

CVE-2025-48976 in Apache Commons FileUpload 1.0-1.6 and 2.0.0-M1 to 2.0.0-M4 allows denial of service through multipart headers without sufficient size limits.


Tools to test/exploit

  • slowloris — slow HTTP denial of service tool.

  • wrk — HTTP benchmarking and load testing.

  • vegeta — HTTP load testing tool.


CVE Examples


References

  1. MITRE. "CWE-770: Allocation of Resources Without Limits or Throttling." https://cwe.mitre.org/data/definitions/770.html

  2. OWASP. "Denial of Service Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html