阻塞关键字是否会阻止此交互式 Python 文件中的代码注入?

信息安全 注射 Python
2021-08-20 15:07:28

我们最近遇到了有人在我们的系统内乱搞的问题。为了防止在我的 python 代码中注入代码,我实现了以下if块:

#! /usr/bin/python3
#-*- coding:utf-8 -*-
def main():
    print("Your interactive Python shell!")
    text = input('>>> ')
    for keyword in ['eval', 'exec', 'import', 'open', 'os', 'read', 'system', 'write']:
        if keyword in text:
            print("You are not allowed to do this!")
            return;
    else:
        exec(text)
        print('Executed your code!')
if __name__ == "__main__":
    main()

少数(用户)人可以在我们的 Ubuntu 系统中使用 sudo 权限运行这个 python 文件。我知道这听起来像是一个安全漏洞,但我看不出有任何逃脱的可能。有没有可能在我的代码块中注入?你有什么防止代码注入的技巧吗?

3个回答

简单的黑名单是修补漏洞的最糟糕方法。它们会产生很多误报,任何坚定的攻击者通常都会找到绕过它们的方法。

Web 应用程序防火墙就是一个很好的例子。他们使用的检测规则比你简单的黑名单要复杂得多,但他们并没有捕捉到所有东西,而且不时地,绕过它们应该捕捉到的东西

我看不出有任何逃脱的可能。

或者你认为。你只是看的时间不够长。

vars(__builtins__)['ex'+'ec']("print('pwned')")

这会直接通过您的过滤器。它调用该exec()函数,然后继续打印' pwned '。

现在你也可以修改你的黑名单来捕捉它,但其他人会想出另一种方法。


exec99% 的情况下,我看到有人使用or之类的东西eval,这是完全没有必要的。如果您可以避免使用exec,请帮自己一个忙,摆脱这个等待被利用的漏洞。但是,如果您绝对需要使用exec,则按照@amon在评论中的建议进行操作:创建一个新管道,在子节点中分叉并删除尽可能多的权限,然后执行代码。使用管道在子进程和父进程之间进行通信。或者按照 Steffen Ullrich 的建议将其沙箱化。

我在您的黑名单中看到的问题是,在 Python 中,可以编写生成附加 Python 代码的 Python 代码,然后运行附加代码。因此,可以从本身不包含单词的程序内部生成单词“eval”。部分问题在于 Python 本身的动态性,而更强类型的静态语言可能更安全。

接受有效字符串的计算机科学理论方法是构造一个语法,然后使用语法解析器。

https://en.wikipedia.org/wiki/Formal_grammar

您需要了解语法解析器的工作原理才能构建一个。但是,在这种情况下,您的语法解析器将是 python 语法解析器本身的一个子集,因此如果您有无限的资源,我会去做很多工作,但否则不要选择这个解决方案。

我想建议设置的方法__builtins__{}即使这样也不安全


def myfunc(a, b):
    return a + b
    

global_namespace = {"__builtins__": {}, "myfunc": myfunc}
exec(yourstring, global_namespace, {})

这会禁用所有内置关键字,只允许显式传递的全局变量(在本例中myfunc),不允许局部变量

您必须明确声明您想要允许的所有内置、全局、本地函数/对象。

然而

在上面的例子中,一个人可以逃脱 object.__globals__["__builtins__"].print("I escaped")(感谢@user236968 指出这一点)

所以似乎唯一安全的事情是禁用所有内置函数、全局变量和本地变量

global_namespace = {"__builtins__": {}}
exec(yourstring, global_namespace, {})

但也许甚至可能存在一个漏洞。

正如其他人指出的那样。可能唯一安全的方法是不使用 execeval而是实现一个解析器,然后使用eval(但你parse必须是无懈可击的)或者使用你的解析器和自己的解释器