编译任意 C 有危险吗?

信息安全 C 海合会
2021-08-30 07:21:28

我有一个小型服务器,我想检查用户提供的 C 程序的编译时间。这些程序永远不会只编译运行。

允许用户使用 gcc 5.4.0 编译任意 C 有什么风险?

4个回答

有点奇怪,但是:这是拒绝服务风险,或潜在的信息泄露。

因为 C 的预处理器会愉快地包含#include指令中指定的任何文件,所以有人可以#include "../../../../../../../../../../dev/zero"并且预处理器会尝试读取到末尾/dev/zero(祝你好运)。

类似地,特别是如果您让人们看到他们的编译尝试的输出,那么有人可以尝试包含可能存在或可能不存在于您的系统上的各种文件,并且可以了解有关您机器的信息。结合巧妙地使用,即使您没有提供完整的错误消息#pragma poison,他们甚至可能会了解有关文件内容的信息。

相关地,pragma 可以改变很多预处理器、编译器或链接器的行为,并在源文件中指定。可能没有一个可以让某人做一些事情,比如指定输出文件名或类似的事情,但如果有的话,它可能会被滥用来覆盖敏感文件,或者让自己执行(通过写入 cron 或类似文件)。可能有同样危险的事情。你真的应该小心编译不受信任的代码。

编译器炸弹

C 是一种非常强大的语言,你可以用它做的一些可怕的事情会让你震惊。例如,您可以创建一个需要27 分钟编译的16 字节 C 程序,当它最终完成时,它会编译成一个16 GB的可执行文件。这仅使用 16 个字节。当您考虑预处理器和更大的源代码文件时,我相信您可以创建更大的编译器炸弹。

这意味着任何有权访问您的服务器的人都可以有效地对您的服务器进行DoS攻击。现在公平地说,这比让某人滥用编译器中的漏洞或包含敏感文件以获取有关您的服务器的信息(就像其他回答者谈到的那样)要危险得多。

但这仍然是您在编译任意代码时可能会遇到的另一个烦恼。我相信您可以为所有构建设置时间限制,并确保永远不要存储二进制文件。当然,您仍然需要在创建它时将其保存在磁盘上,因此如果有人假设编译器炸弹比您的硬盘驱动器大,那么您就会遇到麻烦(如果您让构建完成)。

@AndréBorie是正确的。编译器和相应的配置不会因为安全问题而得到很好的审查,所以一般来说你不应该编译不受信任的代码。

风险在于缓冲区溢出或某种类型的库执行漏洞被利用,并且攻击者获得了对root运行编译器的(希望不是!)用户帐户的访问权限。在大多数情况下,即使是非root黑客攻击也是严重的。这可以在一个单独的问题中详细说明。

创建 VM 是一个很好的解决方案,可以包含任何潜在的漏洞,这样它们就不会损害您的应用程序的其余部分。

最好有一个模板 Linux VM,您可以根据需要使用干净的 slate 编译器环境启动。

理想情况下,您会在每次使用后将其丢弃,但这可能不是绝对必要的。如果您将 VM 隔离得足够好,并正确清理来自 VM 的响应数据,那么无论如何您都应该这样做;那么黑客可以做的最糟糕的事情就是 DoS 或创建错误的编译时间。这些本身并不是严重的问题;至少不像访问应用程序的其余部分那么严重。

但是,在每次使用后(即,而不是每天)重置 VM 确实提供了一个更稳定的整体环境,并且可以在某些边缘情况下提高安全性。

一些操作系统提供容器作为虚拟机的替代品。这可能是一种更精简的方法,但同样的原则也适用。

是的,这很危险:但正如人们所说的那样,这是可能的。我是https://gcc.godbolt.org/上在线编译器的作者和维护者,我发现使用以下组合使其安全非常可行:

  • 整个站点运行在一个虚拟机实例上,几乎没有什么权限可以做任何事情。网络受到严重限制,只有端口 80 可见,并且只能从白名单 IP(我自己的)启用 ssh。
  • 每个编译实例都在一次性 Docker 容器中运行,权限更少
  • 编译器从将所有进程限制(内存、CPU 时间等)设置为下限以防止代码炸弹的脚本执行。
  • 编译器使用LD_PRELOAD包装器(source here)运行,它可以防止编译器打开任何不在显式白名单上的文件。这可以防止它读取 /etc/passwd 或其他类似的东西(这并不是那么有帮助)。
  • 作为一个细节,我会解析命令行选项,如果有什么特别可疑的地方就不要执行编译器。这不是真正的保护;只是一种给出“说真的,不要尝试这个”错误消息而不是LD_PRELOAD捕捉不良行为的方法。

整个源代码都在GitHub 上docker 容器图像和编译器等的源代码也是如此。

我写了一篇博文,解释了整个设置是如何运行的。