编译一个随机陌生人的源代码有多安全?[关闭]


41

假设我正在审查求职者发送的证明其技能的代码。显然,我不想运行它们发送的可执行文件。不太清楚,我宁愿不运行其代码的编译结果(例如,Java允许在注释中隐藏可运行的代码)。

编译他们的代码呢?我需要编译器警告,但是如果它们的代码包含一些巧妙的字符序列,这些序列会利用我的编译器并且我的编译器危害了我的机器,该怎么办?

当我用Google搜索“编译器漏洞”时,得到的所有信息都与编译器优化和代码发布有关,以及所发出的代码是否与原始源代码一样安全。

编译器是否通常经过验证,以确保它们在编译一些巧妙的代码时不会危害用户计算机?从陌生人那里编译一段代码有多安全?


40
只需使用虚拟机...
Florian Margaine 2015年

14
如果您实际上正在查看代码,那么很难获得像“伪装用户计算机”之类的东西而不会被发现,对吧?
RemcoGerlich 2015年

17
那么,您如何判断他们的技能呢?我之所以这样问,是因为我经常审查求职者发送给我们的代码,但是我一直只是阅读它,从来没有觉得需要执行它。
RemcoGerlich 2015年

9
Java确实允许在注释中隐藏可运行的代码。并不是所有的Java IDE在进行语法突出显示时都执行unicode转换,这远非同一回事。
Pete Kirkham 2015年

68
甚至都不看代码。他们可能利用您大脑中的脆弱性。
亨里克

Answers:


34

这取决于。

这段makefile可能会删除您的主目录:

all:
    rm -rf ~

因此,如果您需要使用工具(例如cmake或makefile系统),那么它是不安全的。这仅取决于编码器的恶意程度。

另一方面,编译器是由人编程的,因此会出现错误。因此,也许有人可能会在编译过程中找到执行恶意代码的方法。

如注释中所建议,如果要确保对计算机没有有趣的事情,请使用虚拟机。


32
“使用虚拟机”-如果您真的很偏执,请记住,利用多个漏洞,恶意软件可能会从您的VM中爬出来并勒死您(venom.crowdstrike.com
Steve Jessop

23
@SteveJessop是的,最好使用嵌套VM;)编码人员可能没有意识到脱离一个VM之后,他仍然在另一个VM中。
Ruslan 2015年

3
或者只是使用一台旧笔记本电脑来测试代码。只要没有网络功能,您就可以始终确保恶意软件不会跳出来。除非软件错误神奇地变成了真正的课程错误。
2015年

5
@Ruslan:不幸的是,大多数黑客都看到了Inception。
史蒂夫·杰索普

2
@Ruslan从字面上看,这是我在此页之前打开的选项卡:matrix.wikia.com/wiki/Matrix_in_a_Matrix_theory,由于与SciFi SE网站无关的问题,我正在阅读该标签。
阿玛尼

23

我敢肯定,在企业的某个地方,已经有一些聪明的家伙为特定的语言和编译器版本创建了这样的hack。我最喜欢寻找类似内容的地方可能是国际混淆C竞赛 -(不知道Java是否有可比的东西)。但是,实际上,您认为风险有多高,

  • 申请人给您一个真实的印象,他真的想要您公司的工作(而不是诉讼)

  • 这个家伙不知道你做了多少审查

  • 他/她不知道您使用的是哪个确切的编译器版本

  • 为了安全起见,他/她不知道您使用的是虚拟环境还是在线编译器

  • 您不接受太大而无法有效审查的程序

  • 您不编译任何看起来可疑的东西

  • 实际上,在世界上没有多少人真正知道如何从技术上完成这样的任务(仅谷歌搜索并不能为您提供“快速参考”或教程,因为您已经自己发现了)。

因此,尽管从理论上说编译不是“完全安全的”,但是恕我直言,实际上,“编译器被植入”的风险极低。


15
混淆是好的。 人手不足更好。underhanded-c.org

11
“申请人真的希望在您的公司找到一份工作(而不是诉讼)”尚不清楚发送申请的陌生人真的想要这份工作。
基督教徒

@克里斯蒂安:显然。但是我想,如果OP至少没有提出关于其工作要求的合理表象,那么OP将不会花费任何时间来审查其代码。而且,一个陌生人可能也不想被起诉。我的观点是:以上各点都可以自己绕开,但全部在一起吗?那是相当低的风险。
布朗

13

我们必须区分几种情况:

  1. 编译器中的错误。像每个复杂的程序一样,编译器可能会存在错误,并且其中一个错误可能是可利用的。
  2. 特洛伊木马。在编译过程中,攻击者可能会让您执行一些任意代码。A Makefile,a build.xmlconfigureshell脚本等。从技术上讲,这不是由于编译攻击者的代码,而是由于设置了编译环境。
  3. 允许任意代码在编译时运行的语言。Scala的宏语言是Scala,Common Lisp的宏语言是Common Lisp,Template Haskell的宏语言是Haskell。Scala还具有编译器插件,它们又是在编译时运行的任意Scala代码。F#具有类型提供程序。
  4. 允许在编译时进行图灵计算的语言。Scala和Haskell的类型系统以及C ++的模板都是图灵完备的。您可以让编译器在编译时执行任意的Turing计算,包括但不限于无限循环。请注意,图灵完备仅意味着您可以计算每个可计算图灵的函数,并不意味着您可以访问文件系统或类似的东西。但是,您可以制作一个将花费无限长的时间来编译的程序。
  5. 编译时间很长。例如,C#的过载解析规则非常复杂,您可以将任何3-SAT问题编码为C#过载解析。当然,3-SAT是著名的NP-complete。换句话说,根据我们目前的知识,不可能找到一种有效的C#重载解析算法。您不能使编译花费无限长的时间,但是不需要花费大的程序就可以使编译花费的时间超过Universe的寿命,这实际上是同一件事。

#4。和#5。最多会导致拒绝服务。实际上,C ++和Scala编译器限制了您可以执行的递归量,因此实际上不可能编写无限循环。我相信,在Scala中,这只是实现方面的限制,但是在C ++中,规范明确允许这样做。

#2。从技术上讲不在问题的范围内,因为问题是关于不运行代码的编译(OTOH,这是一个深层的哲学问题:如果对Haskell程序进行类型检查可以执行任意的Turing计算,是编译还是运行程序?)

#1。不太可能。一方面,生产编译器非常复杂,因此发生错误的可能性很高。另一方面,它们经过严格的测试,毕竟,优雅地处理格式错误的输入是编译器工作描述的一部分。即使未经测试,它们也仍然会受到格式错误的代码的轰炸……只需查看一些StackOverflow问题,以了解垃圾邮件对他们的编译器有何影响!

这给我们留下了3。一些编译器可能会限制编译时代码对系统的访问类型,但是对于某些用例,完全访问是不可避免的。例如,F#类型提供程序的目的是为类型系统与F#不匹配的数据“伪造”合成类型,以便您可以与具有强类型WSDL模式的Web服务进行交互。时尚。但是,为此,类型提供程序需要访问文件系统或Web上的WSDL模式资源,因此它需要具有文件系统和网络访问权限。

那么,安全吗?从技术上讲,没有。有风险吗?并不是的。


1
C ++确实将模板的实例化深度限制为某些依赖于实现的值。过去只有几十个。现代的实现将限制提高到了数百个。尽管如此,将每个级别的模板实例化数量加倍是完全不重要的,因此100个级别将要求编译器实例化2 ^ 100个模板。直到发生DoS攻击。
MSalters

3

仅仅编译代码就不会有任何风险。从理论上讲,编译器中可能存在一个聪明的黑客可以利用的错误,但这听起来极不可能。

请注意,建筑物可能不安全。例如,在C#中,“ build event”使您可以指定在构建之前和之后执行的任意命令行,这显然很危险,并且比编译器代码中的缓冲区溢出要容易得多。


3
Scala,Template Haskell,几乎所有Lisps都可以在编译时执行任意代码。具有图灵完备类型系统的所有语言(Scala,Haskell)都可以在编译时执行任意图灵计算,包括但不限于无限循环。C ++的Template系统也是图灵完备的,允许您在编译时执行包括无限循环在内的任意计算。C#的重载分辨率等效于3-SAT,因此是NP完整的,不是turing完整的,但仍可以根据需要在整个生命周期内挂起编译器。
约尔格W¯¯米塔格

3
图灵完备型系统并没有让你PWN计算机。最糟糕的是,如果您利用它,它将使编译器挂起。但是,是的,如果您使用的语言中,编译步骤可以执行任意代码,那么显然您不应该编译不受信任的代码。
JacquesB 2015年

3

实际上,我没有猜测,而是在回答之前就费心去做一些有关此主题的研究,而转到了我能想到的最权威的资源(CVE Details)。这份全面公开的安全漏洞清单可能是评估各种类型软件的威胁级别所能做到的最好的清单。

我当然没有花时间阅读所有可用的材料,但是我选择了一些“主要”编译器,IDE和文本编辑器来进行威胁评估示例。如果您真的想运行任何软件,则至少应查看那里存在哪些威胁。另请注意,较新的软件通常比较新的软件有漏洞,因此,无论您运行什么软件,都最好运行最新的软件。

首先,我们可以看一下各种文本编辑器。最好的编辑器似乎最简单。如果您使用的是Linux Shell,则为Vi;如果您使用的是Windows,则为记事本。如果没有单个字符超出当前编码方案,就没有格式化功能,没有解析功能,只能直接查看数据并自动终止解析功能。甚至Notepad ++也存在一些漏洞。查看不受信任的文件时,请避免任何复杂的事情。

其次,我们可以看一下IDE。如果选择在IDE中打开文件,则应注意某些IDE已报告了错误。显然,Visual Studio已通过扩展机制获得了漏洞利用,因此打开解决方案可能会出现问题。避免使用IDE可以避免您和不受信任的代码之间的整个问题。坚持使用VI似乎更安全。

第三,我们可以看一下实际的编译器。我浏览了几个,其中包括Adobe,微软,Java和GNU的C / C ++,发现总体来说,编译代码(甚至建设,假设没有定做文件)是相对安全的,但每个这些编译器做或做实际运行已编译的二进制文件可能会产生安全漏洞。换句话说,它们不能仅仅通过编译来接管您的系统,而是可以通过运行代码来接管您。

因此,总而言之,假设传递方法尚未劫持您的系统(例如,您的电子邮件客户端被黑客入侵,或者它的USB驱动器已被感染...),则读取源代码并编译源代码可能是安全的。通过研究您的特定软件,可以通过验证文件是否在正确的代码页等中来使其更加安全。仅应在根本不关心的硬件上运行代码。不是VM,而是一台物理上完全不同的计算机,没有网络访问权限,没有敏感文件或外部设备。即使您认为自己了解代码,简单的研究也表明,即使编译器也存在一些错误,这些错误可能会使隐藏的缓冲区溢出漏洞从后面偷偷溜走并执行任意代码,但前提是您选择运行调试程序。实际编译应该是安全的。


2

好吧,我将从“审查他们的代码”开始。为什么需要实际运行代码?

除此之外,还有许多在线编译器,您可以在其中放入代码并进行编译和/或运行。您可以提出一个要求:它可以在该在线编译器中进行编译。

这是带有在线编译器的页面示例:在线编译器

无论如何,面试的审查代码不应太大,以至于您不了解正在发生的事情。


3
“为什么需要实际运行代码?”。当然,要查看它是否有任何好处,只需进行回顾,就会发现它的缺点是微妙的:-)。“当心上面代码中的错误;我只是证明了它是正确的,没有尝试过。” -努斯
史蒂夫·杰索普

2

编译器通常是否经过验证,以确保在编译一些巧妙的代码时不会伪装用户计算机?

通常,它们过于复杂,并且经常使用无法证明该属性的语言编写。

可能不具有此特定意图,但是模糊测试编译器的概念至少是已知的(LLVM现在可以对其进行模糊测试了)。旨在捕获由于编译器错误而使编译器崩溃的输入的测试也往往会发现可利用的缺陷。

自然,您必须调查是否对您感兴趣的特定编译器进行了测试或经过了绒毛测试,以发现潜在的崩溃,以及发现的错误是否已修复。经验法则是,如果崩溃比未发生的内存不足异常严重,那么在不进一步调查详细信息的情况下,您必须考虑很有可能将崩溃利用到漏洞利用中。

从陌生人那里编译一段代码有多安全?

不幸的是,一根绳子要花多长时间。原则上,电子邮件甚至在到达编译器之前都可能会利用您的邮件客户端,或者源代码可能会利用您的文本编辑器或cppcheck。Sebastian在评论中建议使用在线编译器,这是一个很好的建议,但是当然,代码必须采用编译器可以接受的形式。

当然,任何具有通用代码编译时执行功能的语言或编译器都值得怀疑。C ++模板在功能上是完整的,但是没有(打算)访问系统,因此风险相对较低。BЈовић提到make了极高的风险(由于它正在执行陌生人的代码,因此代码恰巧是用make语言编写的,而不是用C ++ 编写的)。如果编译器将运行,system那么您就在同一条船上。我曾经和一个汇编程序一起工作,如果我没记错的话,可以执行任意的编译时代码执行。它原本是用于计算查询表的,但我认为没有什么会阻止您进行系统调用。

在实践中,如果代码对我来说看起来还可以,并且我认为我理解它,那么我认为编译它的风险极低,比“用锁定的浏览器浏览互联网”的风险要低得多。我通常在我的通用计算机上做一些冒险的事情,但是其中许多我是不会做的,例如在病毒实验室或关键服务器上。如果代码看起来很有趣或明显被混淆,那么我可能不会冒险编译它,因为除了可能包含隐藏在不可读垃圾中的漏洞利用的风险之外,它还是垃圾代码。不熟练的代码很困难,但是可能的。通过编译器漏洞利用机器伪装的不常用代码需要包含非平凡的可执行负载,因此非常困难。

如果您想进一步研究,请尝试咨询托管在线编译器的人员。如果尚未对他们进行处理(除非引起NSA或类似机构的注意),您可以合理地认为不会对您进行处理。他们付出了一些努力来在适当的沙箱中运行其编译器,这可能比您愿意付出的努力更多,但他们至少可以告诉您该沙箱为您省去了多少麻烦。


由于犹他大学的John Regehr教授的工作,clang和gcc已通过了相当严格的模糊测试,从而消除了数百个缺陷,这些缺陷可能导致编译器崩溃,甚至产生与其他编译器行为不同的代码。寻找的东西将是仍然存在的错误,以及它们是否构成足够的威胁。
Phil Miller

@Novelocrat:同意,感谢您提供具体信息。我担心的是编译器开发团队可能会错误评级为低优先级,因为“没有人会写代码”,他们也没有得到修复尚未,而一旦你的编译器的思想作为攻击面你会考虑他们至关重要。提醒您,希望您能感到骄傲,以确保编译器编写器不会让某些令人尴尬的事情成为缓冲区写溢出问题;-)
Steve Jessop 2015年

1

尽管通常这是一个令人担忧的问题,但由于设置,我认为该问题不存在。

申请人向您发送了一些源代码。这是怎么发生的或为什么发生的?

显然,只有三种可能性:

  1. 您给申请人分配了一个解决特定(定义明确)问题的作业,以评估其技能。
  2. 申请人想炫耀自己写的很酷的东西。
  3. 申请人是混蛋,间谍或其他恶意的人,实际上对受雇不感兴趣。他只希望您足够愚蠢以运行他的代码。

关于2)和3)

主要风险是区分2)和3)。很有可能如果他写的东西值得一看,那是您可以在线获取源代码(从“中立”源)并且甚至已经熟悉的东西,或者您实际上不知道的东西不想看,因为您会侵犯竞争对手(以前的雇主)的知识产权。后者意味着您无论如何都不想雇用那个人。
如果您可以在线获取源代码,请这样做。如果您可以通过学分中某人的姓名来验证申请人对知名软件(包括专有软件)的贡献,请执行此操作。
在任何其他情况下,只需忽略他发送给您的任何内容即可。要么不值得研究,要么违法,要么高风险。

约1)

申请人给您发送了一些邮件,因为您给了他一个作业。如果您有任何能力(我假设您有能力!),那么对于典型的编程任务(...您甚至选择了自己!),您将能够判断出这是否是一个可行的解决方案,看起来似乎可行通过查看源代码的时间少于30秒(很可能是10秒)。

如果您不能确定该程序在30秒内可能会起作用(或根本不起作用),那么编写该程序的人就不是您想雇用的那种人了。您需要编写其他人可以理解和维护的代码的人。您既不想要试图变得聪明的人,也不希望那些经常赢得混淆不清的C竞赛的人。该程序是否有效甚至都没有关系。一旦另一个人不理解该代码,它就永远不会“起作用”。
如果该程序看起来可能可行,但是您发现任何看起来“怪异”的东西(例如,Java unicode转义序列,C ++原始字符串文字,看起来像三字母组合的东西,等等),则将赋值视为“失败”,移动转到下一个申请人。不必在所有程序的99%中包含类似内容(当然,不要在您的任务中-我希望如此)。因此,如果您发现像这样的“怪异”东西,那么申请人就不是您想雇用的人。

如果该代码通过了第一次分类,则您可能还需要花费2-3分钟的时间对其进行更彻底的研究。如果您对之后看到的结果仍然感到满意,则可以通过静态分析器运行它,并在警告级别很高的虚拟机中对其进行编译。

这应该会带来您在阅读源代码时可能错过的问题(例如,调用未定义的行为或缩小转换范围)。
编译将首先告诉您申请人是否具有必要的勤奋和对细节的关注,与其说他是否具有编程技能,不如说是。就像在您的申请表上正确地写上雇主的名字并在递交之前对您的简历进行拼写检查一样,最佳实践是您确保所提交的任何源代码都不会出错(最好没有警告)。如果某人没有做到这一点,则您不想雇用他。

在这一点上发生恶作剧的风险(利用编译器破坏VM)可以忽略不计,看看您如何对代码进行合理性检查。不会发生。


0

如果可能让您担心,请使用较旧的计算机(我们大多数人难道没有闲逛吗?),安装最新版本的Linux和编译器&c,将源代码复制到其中,拔出网络电缆(或关闭WiFi) ),然后进行编译。如果发生任何令人讨厌的事情,它将不会*影响其他任何事情。

对于Makefile中的恶意软件,请使用-n标志(IIRC,RTMF)运行它,以查看不实际执行操作的情况。

*除非您的程序员当然为该恶意软件编写了代码,以使其等待重新连接,但在这种情况下,您a)擦除机器;b)将这个人的简历转发给NSA,因为他在商业世界中被浪费了:-)


0

底线是,有风险。正如其他答案所指出的那样,风险很小。这意味着您需要问两个问题:

  1. 我该如何减轻风险?
  2. 风险是否足够我应该关心的?

第二个是您在此问题中提出的内容,但这是此特定情况的错误重点。减轻风险的答案很明确,并且随时可用:不要在计算机上编译代码。您有两种不使用机器即可进行编译的明显方法:

  1. 使用虚拟机(如@FlorianMargaine在注释中立即指出的)。您只需在编译之前对其快照即可,然后在完成后还原快照。
  2. 使用托管服务(例如,在线编译器)。

这些减轻风险的方法是如此明显,便宜且易于访问,以致不值得花费大量时间来尝试分析风险有多大。只需执行其中一项并完成即可。


0

如果您从不受信任的位置(例如,下载或网络共享)打开项目,Visual Studio实际上会警告您。

一个利用WPF项目的示例是:可以从XAML引用.NET类,并提供IntelliSense,VS在设计时加载并执行引用的类。

这意味着攻击者可以将恶意.dll放入bin目录,将源代码替换为非恶意代码,然后在设计时执行DLL。首次构建后,恶意二进制文件的所有痕迹都消失了。

因此,即使提供的所有代码都是“干净的”,编译器也没有漏洞,并且您当然绝不会手动执行任何提供的.EXE,但恶意代码仍然可以在后台执行。(为安全起见,可以在打开解决方案之前确保目录树中没有二进制文件。然后,VS将提示您构建解决方案,然后在设计时提供IntelliSense。)

其他语言/操作系统可能也存在类似的载体。


0

阅读源代码:完全安全。编译源代码:完全安全。执行已编译的二进制文件:好吧……这取决于。

编译只是计算机读取源代码并以二进制形式编写其等效代码。编译后,您只有2个文档:一个是人类可读的,另一个是计算机可读的。除非您让计算机读取(即运行)第二个文档,否则什么都不会发生。


3
为什么编译是完全安全的任何解释?可以通过发送巧妙的消息来伪装服务器-为什么他不能通过提供巧妙的输入来伪装编译器?
Sharptooth

2
我想您会注意到巧妙设计的代码,旨在利用服务器或编译器中的漏洞。但是将其推到极点,为什么还要冒着查看代码的风险?编辑器中有几个漏洞,仅通过查看文件即可利用。
gbjbaanb 2015年

2
这个答案完全是胡言乱语。完全没有理由为什么编译器本质上是安全的,或者至少比完成诸如建立SSL连接之类的简单任务的安全性更高,但是最近一个流行的库包含一个漏洞。我什至会争辩说,由于编译器通常不用于敌对环境(如Internet),因此对漏洞的检查较少,因此更有可能出现漏洞。
Dorus 2015年

1
不确定编译代码是否完全安全。即使在Java中,使用Maven(例如),粗心的“ mvn package”也可以使用其他您可能不容易知道的插件来拉东西并执行任务。我敢肯定,其他构建系统也一样。
布鲁诺

1
如果允许编译器运行无限制的时间,则图灵完备的语言可能允许程序花费无数的时间来编译,但是许多编译器将创建有限数量的线程,而不管其中可能出现的任何内容。被编译的代码,这意味着将限制一次尝试编译一个程序的CPU负担。潜在的更大问题将是磁盘空间需求。1KB的源文件可能会产生大量的目标代码,这完全是合理的。
2015年

-1

我认为您担心以下两种口味之一:

  • 复杂的,由漏洞利用驱动的恶意软件:不太可能,尤其是因为它们针对的是非常特定的硬件和/或软件,并且[根据您的问题]您的攻击者可能没有该级别的系统知识。
  • 与环境有关的事情:恶意的恶作剧指令(例如,删除主目录)或不体面/不称职的指令会改变您的系统行为(例如,重写PATH或LIBRARY环境变量)

有人建议使用虚拟机或旧系统,但我提供了一个更简单的解决方案:以具有不同权限其他用户身份进行编译。比设置虚拟机或专用计算机要容易得多。

如果您的系统被编译时漏洞利用几乎不可能,那么请从备份中还原(您有备份,对吗?)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.