是否有一个字符串`s`使得eval(repr(s))导致任意代码执行?

时间:2015-11-22 18:30:15

标签: python security

我在某处发现了类似的代码:

USER_CONTROLLED = 'a'
open("settings.py", "w").write("USER_CONTROLLED = %s" % eval(repr(a)))

在另一个档案中:

import settings
x = settings.USER_CONTROLLED * [0]

这是一个安全漏洞吗?

1 个答案:

答案 0 :(得分:2)

与你在IRC上被告知的情况相反,肯定会有x使eval(repr(x))变得危险,所以说没有任何限制也是如此。

想象一下以不同方式实现__repr__的自定义对象。文档在__repr__上说它“应该看起来像一个有效的Python表达式,可用于重新创建具有相同值的对象”。但是没有什么可以强制执行这个指南。

相反,我们可以创建一个具有自定义__repr__的类,该类返回一个字符串,在计算时运行任意代码。例如:

class MyObj:
    def __repr__ (self):
        return "__import__('urllib.request').request.urlopen('http://example.com').read()"

在该类型的对象上调用repr()表明它返回的字符串肯定可以被评估:

>>> repr(MyObj())
"__import__('urllib.request').request.urlopen('http://example.com').read()"

在这里,这只涉及向example.com发出请求。但正如您所看到的,我们可以在此处导入任意模块并使用它们运行代码。而且该代码可能有任何副作用。所以这绝对是危险的。

但是,如果我们将x限制为我们知道调用它们repr()的已知类型,那么我们确实可以说什么时候用它来运行任意代码是不可能的。例如,如果x是一个字符串,那么unicode_repr的实现可确保所有内容都被正确转义,并且评估该对象的repr()将始终返回正确的字符串(甚至等于x),没有任何副作用。

因此我们应该在评估之前检查类型:

if type(a) is not str:
    raise Exception('Only strings are allowed!')

something = eval(repr(a))

请注意,我们不会在此处使用isinstance来执行继承感知类型检查。因为我绝对可以使MyObj继承自str

>>> x = MyObj()
>>> isinstance(x, str)
True
>>> type(x)
<class '__main__.MyObj'>

所以你应该在这里测试具体的类型。

请注意,对于字符串,实际上没有理由调用eval(repr(x)),因为如上所述,这将导致x本身。所以你可以直接指定x

然而,在您的实际用例中,您确实存在一个非常大的安全问题。您希望创建变量赋值并将该代码存储在Python文件中,以便稍后由实际的Python解释器运行。因此,您应该绝对确保赋值的右侧不是任意代码,而是实际上是字符串的repr:

>>> a = 'runMaliciousCode()'
>>> "USER_CONTROLLED = %s" % eval(repr(a))
'USER_CONTROLLED = runMaliciousCode()'
>>> "USER_CONTROLLED = %s" % repr(a)
"USER_CONTROLLED = 'runMaliciousCode()'"

如您所见,评估repr()会将实际内容放在作业的右侧(因为它等同于"…" % a)。但是,当您导入该文件时,这可能导致恶意代码运行。所以你应该在那里插入字符串的repr,完全忘记完全使用eval