【问题标题】:Understanding Python Pickle Insecurity了解 Python Pickle 的不安全性
【发布时间】:2012-04-24 16:42:23
【问题描述】:

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

我不清楚的是,在不导入 os 模块的情况下如何正确解释 os.system

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

谁能解释一下?

【问题讨论】:

    标签: python security namespaces pickle


    【解决方案1】:

    模块的名称(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字节码指令执行时调用该函数。

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

    【讨论】:

      【解决方案2】:

      有关编写比标准 os.system() 示例更进一步的恶意 Pickles 的更多信息,请参阅此 presentation 及其随附的 paper

      【讨论】:

        【解决方案3】:

        如果你使用 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 ~',))

        Source

        【讨论】:

        • 是的,但为什么不需要import os
        • dir() 尝试提供一组有趣的名称,而不是尝试提供一组严格或一致定义的名称,并且其详细行为可能会因版本而异。当您动态导入模块时,无法保证您会在 dir() 中看到它
        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 2023-04-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多