python沙箱栈帧逃逸
python沙箱栈帧逃逸
生成器
生成器(Generator)是 Python 中一种特殊的迭代器,它可以通过简单的函数和表达式来创建。生成器的主要特点是能够逐个产生值,并且在每次生成值后保留当前的状态,以便下次调用时可以继续生成值。
1 | def f(): |
运行结果如下
运行的时候 b的值是114
运行到yield的时候 会中断
yield 用于产生一个值,并在保留当前状态的同时暂停函数的执行。当下一次调用生成器时,函数会从上次暂停的位置继续执行,直到遇到下一个 yield 语句或者函数结束。
1 | def f(): |
如果不用next函数逐步执行 生成器会直接运行相关的所有值 一次性全部输出
生成器的相关属性
1 | gi_code: 生成器对应的code对象。 |
打印一下看看
1 | def f(): |
栈帧
每当 Python 解释器执行一个函数或方法时,都会创建一个新的栈帧,用于存储该函数或方法的局部变量、参数、返回地址以及其他执行相关的信息。这些栈帧会按照调用顺序被组织成一个栈,称为调用栈。
主要包含以下的组成对象
1 | f_locals: 一个字典,包含了函数或方法的局部变量。键是变量名,值是变量的值。 |
我们捕捉栈帧并且输出看看
1 | def test(): |
可以看到 先输出了此时运行到的line7 然后打印出了上一帧 也就是line9
栈帧逃逸
通过生成器的栈帧对象通过f_back(返回前一帧)从而逃逸出去获取globals全局符号表
注意到存在两个可以利用的对象
1 | f_locals: 一个字典,包含了函数或方法的局部变量。键是变量名,值是变量的值。 |
测试
1 | def test(): |
输出
我们可以获取到全局对象
使用一个沙盒试一下
1 |
|
由于设置的exec环境里面不存在flag 无法拿到这个值
使用栈帧逃逸时
1 |
|
成功获取到沙盒外面的flag对象
globals中的__builtins对象
__builtins__
模块是 Python 解释器启动时自动加载的,其中包含了一系列内置函数、异常和其他内置对象。
1 | dir(__builtins__) |
可以使用这里面的对象来完成rce
1 | flag="flag" |
有点多此一举的感觉(笑 但是好玩)
不使用next获取栈帧
可以使用for语句去获取
1 | a=(a.gi_frame.f_back.f_back for i in [1]) |
CISCN 初赛 morecc
1 | def source_simple_check(source): |
主要还是看runner的逻辑
过滤了”__”, “getattr”, “exit” 不允许是非ascii字符 不允许出现”LOAD_GLOBAL”, “IMPORT_NAME”, “LOAD_METHOD” 除非是导入原来的库 然后就是监听了沙盒的内的允许 不允许出现”marshal”, “__new__", “process”, “os”, “sys”, “interpreter”, “cpython”, “open”, “compile”, “gc”等字符 同时制空了builtins
可以使用栈帧逃逸去获取沙箱外的对象 然后完成rce
1 | import requests |
成功获取帧对象
尝试ping和curl无反应 rce命令不回显
注意到
读入flag之后 使用随机的值替换掉flag
co_consts常量列表
此字段是常量列表,例如代码对象中包含的字符串文字和数字值
可以通过这个获取到flag
1 | import requests |
但是会打印出THIS_IS_SEED 这里就不能输出
可以获取到str 然后进行字符串处理
1 | import requests |
成功获取到flag
参考资料
Python利用栈帧沙箱逃逸 - 先知社区 (aliyun.com)