了解Python Pickle Insecurity

时间:2012-04-24 16:42:24

标签: python security namespaces pickle

在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
>>> 

有人可以解释一下吗?

4 个答案:

答案 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~',))

Source

答案 3 :(得分:2)

导入模块只会将其添加到本地命名空间,而本地命名空间不一定是您所在的命名空间。除非它不存在:

>>> dir()
['__builtins__', '__doc__', '__name__', '__package__']
>>> __import__('os')
<module 'os' from '/usr/lib64/python2.7/os.pyc'>
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__']