在Python文档中指出pickle
不安全,不应解析不受信任的用户输入。如果你研究这个;几乎所有示例都通过system()
进行os.system
调用来证明这一点。
我不清楚,如果没有导入os.system
模块,os
是如何正确解释的。
>>> import pickle
>>> pickle.loads("cos\nsystem\n(S'ls /'\ntR.") # This clearly works.
bin boot cgroup dev etc home lib lib64 lost+found media mnt opt proc root run sbin selinux srv sys tmp usr var
0
>>> dir() # no os module
['__builtins__', '__doc__', '__name__', '__package__', 'pickle']
>>> os.system('ls /')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
>>>
有人可以解释一下吗?
答案 0 :(得分:9)
模块的名称(os
)是操作码的一部分,pickle
会自动导入模块:
# pickle.py
def find_class(self, module, name):
# Subclasses may override this
__import__(module)
mod = sys.modules[module]
klass = getattr(mod, name)
return klass
请注意__import__(module)
行。
执行GLOBAL 'os system'
pickle字节码指令时调用该函数。
这种机制是必要的,以便能够解开其模块尚未显式导入调用者名称空间的类的实例。
答案 1 :(得分:9)
关于编写比标准os.system()示例更远的恶意Pickles的信息太多,请参阅此presentation及其随附的paper。
答案 2 :(得分:6)
如果您使用pickletools.dis来拆卸泡菜,您可以看到它是如何工作的:
import pickletools
print pickletools.dis("cos\nsystem\n(S'ls ~'\ntR.")
输出:
0: c GLOBAL 'os system'
11: ( MARK
12: S STRING 'ls ~'
20: t TUPLE (MARK at 11)
21: R REDUCE
22: . STOP
Pickle使用一个简单的基于堆栈的虚拟机来记录用于重建对象的指令。换句话说,示例中的腌制指令是:
推送self.find_class(module_name,class_name),即推送os.system 推字符串'ls~' 从最顶层的堆栈项构建元组 在堆栈上应用可调用到argtuple。即os.system(*('ls~',))
答案 3 :(得分:2)
导入模块只会将其添加到本地命名空间,而本地命名空间不一定是您所在的命名空间。除非它不存在:
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__']
>>> __import__('os')
<module 'os' from '/usr/lib64/python2.7/os.pyc'>
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__']