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



我在某处找到了类似的代码:

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

在另一个文件中:

import settings
x = settings.USER_CONTROLLED * [0]

这是安全漏洞吗?

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

最新更新