8

它在 Python 文档中声明pickle不安全,不应解析不受信任的用户输入。如果你研究这个;几乎所有示例都system()通过调用 via 来证明这一点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

4 回答 4

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 字节码指令时被调用。

这种机制是必要的,以便能够取消其模块尚未显式导入调用者命名空间的类的实例。

于 2012-04-24T16:45:00.250 回答
9

有关编写比标准 os.system() 示例更进一步的恶意 Pickles 的全部信息,请参阅此演示文稿及其随附的论文

于 2012-04-25T00:44:28.813 回答
6

如果你使用 pickletools.dis 来反汇编 pickle,你可以看到它是如何工作的:

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 ~' 从最顶部的堆栈项构建元组 将 callable 应用于 argtuple,两者都在堆栈上。即 os.system(*('ls ~',))

来源

于 2012-04-24T16:47:03.747 回答
2

导入一个模块只会将它添加到本地命名空间,这不一定是你所在的。除非它没有:

>>> dir()
['__builtins__', '__doc__', '__name__', '__package__']
>>> __import__('os')
<module 'os' from '/usr/lib64/python2.7/os.pyc'>
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__']
于 2012-04-24T16:45:44.457 回答