Reports
BAJA[DENEGACIÓN DE SERVICIO]#3625600

Burp Suite DAST: el login procesaba contraseñas de varios megas antes de validarlas

El endpoint /api-internal/login de una instancia de prueba de Burp Suite DAST cargaba y parseaba entera una contraseña de megas antes de rechazarla, gastando memoria y CPU con datos sin validar.

Resumen
Resumen en castellano de un reporte público, no una traducción literal. El código y los comandos se mantienen como en el original.

Resumen

En una instancia de prueba de Burp Suite DAST (Enterprise), de PortSwigger, el endpoint de inicio de sesión no ponía ningún límite propio al tamaño del campo password. El problema no era que aceptase textos largos, sino el orden de las operaciones: la aplicación recibía todo el cuerpo de la petición, lo guardaba en memoria, deserializaba el JSON y creaba la cadena completa, y solo después comprobaba las credenciales y devolvía el error. Es lo contrario del principio fail-fast, según el cual lo que no tiene sentido se descarta lo antes posible.

  • Endpoint afectado: POST /api-internal/login
  • Acceso: sin autenticación y accesible públicamente
  • Tipo: validación de entrada incorrecta (CWE-20)
  • Severidad final: baja (el investigador la había propuesto como alta). El título del reporte la marca como fuera del alcance del programa, aunque consta como resuelto y con recompensa (importe no público).

El investigador omitió a propósito el dominio de la instancia, por eso en la prueba de concepto aparece como <target-instance>.

Una contraseña real suele ocupar entre 8 y 128 caracteres. Este endpoint aceptaba valores de varios megabytes. La infraestructura que hay por delante sí cortaba las peticiones más grandes a nivel de red, pero todo lo que quedaba por debajo de ese umbral lo procesaba la aplicación entero.

Pasos de reproducción

  1. Preparar un cuerpo JSON con un usuario cualquiera y una contraseña de unos 10 MB, y enviarlo al endpoint de login:
curl -X POST https://<target-instance>/api-internal/login \
  -H "Content-Type: application/json" \
  --data-binary @<(python3 -c '
payload = "A" * (10 * 1024 * 1024)
print(f\'{{"username":"admin","password":"{payload}"}}\')
')
  1. Observar que la respuesta tarda: el servidor recibe todo el cuerpo, lo parsea y reserva memoria para la cadena antes de contestar.
  2. Comprobar que, tras ese trabajo, devuelve el fallo de autenticación de siempre, no un error por tamaño:
{"code":2,"error":"Login failed."}

Si la petición supera el límite de la infraestructura, se rechaza antes en la capa de red. Si queda por debajo, la aplicación la procesa entera.

Impacto

El coste es desigual: al atacante le basta con mandar un texto grande, mientras que el servidor tiene que reservar memoria, parsear el JSON, gastar CPU y cargar al recolector de basura. Todo eso ocurre en un endpoint sin autenticación, que es una frontera de seguridad y debería hacer el menor trabajo posible con datos que no son de fiar. El reporte no demuestra una caída del servicio: lo que describe es más consumo de recursos del necesario y más superficie de ataque en un componente crítico.

El investigador resumía así la diferencia entre el flujo que había y el que proponía:

Receive request
↓
Buffer entire request body
↓
Parse JSON payload
↓
Allocate large string
↓
Execute authentication logic
↓
Reject invalid credentials
Receive request
↓
Validate Content-Length
↓
Validate field size constraints
↓
Parse JSON payload
↓
Execute authentication logic

Remediación

El reporte no explica qué cambió PortSwigger; solo consta como resuelto. Lo que proponía el investigador:

  • Poner un límite estricto a la longitud de la contraseña en la propia aplicación, por ejemplo 8192 bytes:
if (password.length > 8192) {
    return HTTP 400;
}
  • Hacer que el proxy inverso o el balanceador devuelva HTTP 413 Payload Too Large según la cabecera Content-Length.
  • Comprobar los tamaños de los campos antes de cargar y parsear el cuerpo siempre que se pueda, en vez de depender solo del límite global de la infraestructura.

Qué aprender de este caso

  • En endpoints sin autenticación (login, registro, recuperación de contraseña) merece la pena probar campos de texto enormes y medir el tiempo de respuesta: si el error es el mismo de siempre pero tarda más, la aplicación está procesando la entrada antes de validarla.
  • Que un proxy corte las peticiones gigantes no basta: entre el límite global de la infraestructura y el tamaño razonable de un campo puede quedar un margen de megas que la aplicación parsea entero.
  • La defensa es fijar en la propia aplicación un tamaño máximo de cuerpo para cada ruta y de cada campo, y comprobarlo antes de deserializar el JSON, devolviendo 400 o 413 en lugar de un fallo de autenticación genérico.