了解Python Pickle Insecurity

tMC*_*tMC 7 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
>>> 
Run Code Online (Sandbox Code Playgroud)

谁能解释一下?

NPE*_*NPE 9

module(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
Run Code Online (Sandbox Code Playgroud)

注意这一__import__(module)行.

GLOBAL 'os system'执行pickle字节码指令时调用该函数.

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


小智 9

对于编写比标准os.system()示例更远的恶意Pickles的信息太多,请参阅此演示文稿及其随附的文章.


Cha*_*guy 6

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

import pickletools
print pickletools.dis("cos\nsystem\n(S'ls ~'\ntR.")
Run Code Online (Sandbox Code Playgroud)

输出:

 0: c    GLOBAL     'os system'
11: (    MARK
12: S        STRING     'ls ~'
20: t        TUPLE      (MARK at 11)
21: R    REDUCE
22: .    STOP
Run Code Online (Sandbox Code Playgroud)

Pickle使用一个简单的基于堆栈的虚拟机来记录用于重建对象的指令.换句话说,示例中的腌制指令是:

推送self.find_class(module_name,class_name)即推送os.system从最顶层的堆栈项推送字符串'ls~'Build元组在堆栈上应用callable到argtuple.即os.system(*('ls~',))

资源

  • 是的,但为什么这不需要`import os`? (2认同)
  • dir() 尝试提供一组有趣的名称,而不是尝试提供一组严格或一致定义的名称,并且它的详细行为可能会在不同版本中发生变化。当你动态导入一个模块时,不能保证你会用 dir() 看到它 (2认同)