攻击Python的pickle

33

我正在编写一个Web应用程序,将用户输入存储在一个对象中,该对象将被pickle。

如果用户能够构造恶意输入,那么在对象被解pickle时可能会执行一些非常严重的操作吗?

这里有一个非常基本的代码示例,它忽略了封装等优秀原则,但是代表了我的研究重点:

import pickle

class X(object):
    some_attribute = None

x = X()
x.some_attribute = 'insert some user input that could possibly be bad'

p = pickle.dumps(x)

# Can bad things happen here if the object, before being picked, contained
# potentially bad data in some_attribute?
x = pickle.loads(p)

1
它只是一个字符串吗?不,它是安全的。它是任意对象吗?当然,它可能会做坏事。 - Chris Morgan
3
+1 这是一个很好的问题。 - dfb
1
pickle.loads(p) 将字符串视为任意对象处理。 - dfb
@spinning_plate 我并不太担心这个问题,因为我的代码只会反序列化先前已经序列化的数据,我的关注点在于通过公共 Web 接口传递进来的对象数据是否能够以某种方式欺骗 pickle.loads() 以表现出邪恶的 eval() 并执行任意代码或造成其他不良影响。 - Matty
是的,我想说@Not_a_Golfer是对的,但你在这里要非常小心。这里有很多欺诈的空间。 - dfb
3个回答

15

是和不是...

不是 - 除非解释器或pickle模块存在错误,否则您无法通过pickled文本运行任意代码,或类似的操作。除非以后对pickled文本进行了eval操作,或者您正在执行诸如创建具有在此数据中提到的类型的新对象之类的操作。

是 - 取决于您计划稍后在对象中使用信息来做什么,用户可以做各种事情。从SQL注入尝试,到更改凭据,暴力密码破解,或者任何应该在验证用户输入时考虑的事情。但您可能已经检查了所有这些。


编辑:

Python文档指出:

 

警告pickle模块不打算针对错误或恶意构造的数据进行安全保护。永远不要从不受信任或未经身份验证的来源取消pickle数据。

但这不是您的情况-您接受输入,将其经过常规验证,然后进行pickle处理。


一个人可以编写一个对象,当调用它时,会做一些愚蠢的事情,但是问题在于,仅仅反序列化可能会导致一些更复杂的问题。 - dfb
4
这里有一个例子,但是你需要在任何情况下保护你的代码免受用户输入的影响,因此我几乎不认为这是一个真正的威胁,除非你实际上允许用户向你发送一个被pickled的对象。http://www.securityfocus.com/bid/5257/exploit - Not_a_Golfer
1
但在这种情况下,P是由OP从用户输入构建的对象,而不仅仅是来自用户发送的序列化数据,如果我理解正确的话。数据本身已经转义,因此除非对象的构造函数在检查数据时出现异常,否则应该是安全的。 - Not_a_Golfer
1
@Woot4Moo 即使他们有它,如果只是输入(字符串/数字/布尔值)放入在服务器端构建的对象中,然后进行了pickle处理,你如何运行任意代码? - Not_a_Golfer
1
@Not_a_Golfer 当我下班回家后,我会查看所提供示例的漏洞利用代码。 - Woot4Moo
显示剩余3条评论

7

根据文档所述

警告pickle 模块不保证安全,不能防御错误或恶意构造的数据。 永远不要反序列化来自不受信任或未经身份验证的来源的数据。

这就意味着,如果数据结构存在某种状态,使得 pickle 算法进入程序行为无法保证的状态,则可能通过调用此功能来攻击该功能。

根据网站的说法

import pickle
pickle.loads(b"cos\nsystem\n(S'ls ~'\ntR.") # This will run: ls ~

只需执行任意代码即可。还有其他示例,以及为安全目的改进的“腌制”。


1
我看到了那个网站。只有被我的代码pickle的对象才能被unpickle。担心的是,在被pickle之前,对象是否可能包含一些字符串,可以欺骗pickle.loads()函数执行任意代码。 - Matty
将示例中的字符串进行 Pickling,然后再进行 unpickling 将 不会 运行 ls - Michael J. Barber
4
@Woot4Moo,你确实声称“你可以很好地将其pickle,然后解除pickle作为该字符串。这将导致您执行我的语句。” 这是不正确的。虽然你在回答中说的话都没有错,但我不确定它是否回答了问题,因为你并非处理一个不受信任的pickle,而是一种对某些不受信任数据进行pickle的形式。也就是说,我们能否构造一个字符串s,它会在pickle.loads(pickle.dumps(s))中引起问题? - Michael J. Barber
1
在Ubuntu 16和Python 3.6上,此代码会出现错误:pickle.loads("cos\nsystem\n(S'ls ~'\ntR.")。 回溯(最近的调用最先): 文件“<stdin>”,第1行,<module> 类型错误:需要类似字节的对象,而不是'str'。 - InLaw
@InLaw 我也遇到了同样的错误;在Ubuntu 20.04和Python 3.8.10上讨论的代码可以运行,只需在字符串前加上b来使其成为字节文字(即pickle.loads(b"cos\nsystem\n(S'ls ~'\ntR."))。 - Roland Maio
显示剩余3条评论

0

我在多进程模块的文档中找到了这个答案:

警告

Connection.recv() 方法会自动反序列化接收到的数据,这可能会存在安全风险,除非您信任发送消息的进程。

因此,除非连接对象是使用 Pipe() 生成的,否则在执行某些身份验证后再使用 recv() 和 send() 方法。请参见身份验证密钥。

(强调我的)

结论是,如果连接对象是使用受信任的 Pipe(即受信任的 pickle)生成的,则可以安全地进行反序列化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接