我发现处理不同于我自己的观点很容易。当我看到人们以错误的理由同意我的观点时,我会感到更不安。
我是软件自由的热情支持者,这已经不是什么秘密了:我写了两篇文章,阐述了自由、自由和开源软件(FLOSS)对维护用户自主性的必要性,但还不够:
Whatsapp和用户本土化一类掠夺性商业模式的现象我称之为“用户本土化”和防御措施:牙线、开放平台和简单性。
保持平台开放开放平台如何失去开放性,以及有什么措施可以防止这种情况。Web、XMPP、电子邮件和矩阵都是突出问题双方的例子。
自由开源软件定义中最大的一个部分是研究和修改程序的自由;换句话说,访问可编辑的源代码。我同意这种接触是必要的;然而,太多人出于错误的原因支持源可用性。其中一个原因是,源代码对软件的运行方式具有一定程度的透明性是必要的,因此有必要确定它是否安全或可信。虽然通过模糊性实现安全性肯定不是一个可靠的措施,但这种说法有两个问题:
源代码描述程序的设计目的;确定其实际功能是否与预期设计相符是不必要且不充分的。
我想进一步讨论这些问题,主要关注已编译的二进制文件。请记住,我不认为从安全角度来看,源可用性是无用的(这当然会使审核更容易),我确实认为源可用性是用户自由所必需的。我只是认为源不可用并不意味着不安全,源可用性也不意味着安全。对二进制文件执行安全性分析是可能的(而且通常更可取),而不一定要有源代码。事实上,漏洞发现通常不依赖于源代码分析。
附言:这种立场不是绝对的;我承认底部有几个很好的反驳!
我不认为有人真的认为软件的安全性会在源代码发布后立即得到改善。我要回应的论点是,源代码对于理解一个程序的功能和安全性是必要的,没有它我们就无法确定。
假设从根本上改变程序架构的重写不是选项1,软件安全通常会通过类似以下过程的方式修复漏洞来提高:
源代码通常对第3步很有帮助(有时是必不可少的)。如果有人完成了步骤3,他们将需要源代码才能继续执行步骤4。步骤1和2不需要源代码;这些步骤依赖于理解程序的错误行为。为此,我们使用逆向工程和/或模糊化。
理解一个程序是如何设计的和理解一个程序做什么是不一样的。一种理解的合理水平并不意味着另一种。
源代码2对于描述程序的高级、人类可理解的设计至关重要;它代表了一份合同,概述了开发人员对程序的预期行为。然后,编译器或解释器3必须将其翻译成机器指令。但源代码并不总是容易直接映射到机器指令,因为它是复杂系统的一部分:
编译器(有时甚至是解释器)可以应用难以推理的优化和强化措施。对于利用运行时信息的JIT编译器来说尤其如此。
开发人员可能不太了解操作系统本身,并且以与开发人员期望相矛盾的方式运行程序。
以上所有要点都适用于每个依赖项和底层操作系统,这可能会影响程序的行为。
此外,所有程序员都是有缺陷的凡人,他们并不总是完全理解源代码。每一个做过大量编程的人都熟悉在运行时遇到错误的感觉,原因是不可能找到的……直到他们注意到它在第12行盯着他们看。想想那些不容易被注意到的错误。
阅读源代码、编译并通过测试并不足以向我们展示程序的最终行为。知道程序在运行时做什么的唯一方法是…运行它。4.
几乎所有的程序员都充分意识到自己的能力有限,这就是为什么大多数程序员已经使用不依赖源代码的技术来分析运行时行为。例如,几种编译语言5的开发人员可以构建带有净化程序的二进制文件,以检测人眼在读取源代码时可能错过的未定义行为、种族、未初始化读取等。虽然构建这些二进制文件需要源代码,但没有必要运行它们并观察失败。
向测试人员分发带有消毒器和调试信息的二进制文件是收集有关程序潜在安全问题的数据的有效方法。
通过读取大型程序的源代码,很难确定它需要哪些系统调用和文件,尤其是在某些库(例如libc实现/版本)可能不同的情况下。像strace(1)6这样的系统调用跟踪器使这个过程变得微不足道。
个人例子:我从strace那里获得的理解对我编写bubblewrap脚本是必要的。这些脚本使用BubbleRap(1)以尽可能低的权限对程序进行沙箱处理。分析每一个相关的程序和库的源代码都会花费我几个月的时间,而strace在一个下午就给了我我需要知道的一切:分析strace的输出会准确地告诉我允许哪些系统调用以及允许访问哪些文件,甚至不需要知道程序是用什么语言编写的。我使用以下命令7生成了syscall allow list的初始版本:
程序args 2的strace名称>&;1\|rg'^([a-z]*)\(.';--替换';$1';\|排序| uniq
这还扩展到确定程序如何利用网络:Wireshark等数据包嗅探器可以确定程序何时连接到网络,以及连接到哪里。
这些方法并非完美无缺。系统调用跟踪器的设计只是为了阐明程序如何与内核交互。内核交互告诉了我们很多(有时这是我们所需要的),但它们并不能说明全部情况。此外,通过传输加密8,包检查可能会有点痛苦;跟踪程序的执行和数据包检查可以提供清晰的信息,但这并不容易。
更多信息,我们转向核心转储,也称为内存转储。内核转储共享程序在执行期间或崩溃时的状态,让我们更清楚地了解程序正在处理的数据。包含调试符号(例如DWARF)的构建有更详细的核心转储。发布预发布版本的每日快照的供应商通常会包含一些符号,以向测试人员提供有关崩溃原因的更多细节。Web浏览器是一个常见的例子:Chrome开发快照、Chrome Canary、Firefox Nightly、WebKit Canary构建等等。所有这些都包括调试符号。直到最近,Minecraft:Basefine版还包括调试符号,这些符号被modding社区大量使用。9
2020年,Zoom Video Communications因将其“Zoom”软件作为安全的端到端加密视频会议解决方案进行营销而受到审查。Zoom的文档声称它使用了“AES-256”加密。如果没有源代码,我们是否必须按照文档的要求行事?
公民实验室没有。2020年4月,它发布了一份报告,揭示了Zoom加密的关键缺陷。它利用Wireshark和mitmproxy分析网络活动,并检查核心转储以了解其加密实现。公民实验室的研究人员发现,Zoom实际上使用了一个缺陷极大的AES-128弱版本(ECB模式)实现,并且很容易绕过它。
系统调用跟踪、数据包嗅探和核心转储都很好,但它们依赖于手动执行,而手动执行可能不会命中所有所需的代码路径。幸运的是,还有其他形式的分析可用。
跟踪执行和检查内存转储可以被视为逆向工程的形式,但它们只提供了正在发生的事情的表面视图。当我们分析二进制工件时,逆向工程变得更加有趣。
静态二进制分析是检查程序底层设计的强大方法。反编译(尤其是在添加调试符号时)可以重新构造二进制程序集或源代码。符号名称在精简的二进制文件中可能看起来不可理解,注释也将丢失。剩下的足以破译控制流,揭示程序如何处理数据。这个过程可能会很乏味,尤其是当程序使用某种形式的二进制模糊处理时。
目标不一定是完全理解一个程序的设计(没有源代码是非常困难的);它通常是为了回答一个特定的问题,填补跟踪/模糊留下的空白,或者找到一个著名的属性。当开发人员发布关于其封闭源代码软件的安全体系结构的文档时,反编译器等逆向工程工具正是验证其诚实性(或缺乏诚实性)所需要的。
在这种情况下,反编译器很少单独使用。相反,它们通常是逆向工程框架的一个组成部分,也支持内存分析、调试工具、脚本编写,有时甚至是IDE。我用的是radare项目,但Ghidra也很受欢迎。如果你感兴趣,他们的文档可以帮助你开始。
这些反向工程技术——追踪、数据包嗅探、二进制分析和内存转储的组合——构成了大多数现代恶意软件分析的工作原理。请参见此Zoom Windows安装程序的全自动分析示例。它列举了大量有关Zoom的信息,而无需访问其源代码:读取唯一的机器信息、反VM和反反向工程技巧、读取配置文件、各种类型的网络访问、读取已装入卷的信息,等等。
要亲自尝试,请使用为动态分析设计的沙盒。布谷鸟是一种常见且易于使用的解决方案,而德拉克夫则更为先进。
英特尔管理引擎(ME)是所有英特尔处理器(2008年之后)的必选子系统,对主机系统具有极高的访问权限。主动管理技术(AMT)运行在带有“vPro”品牌的英特尔处理器子集上。后者可以被禁用,用于组织远程管理其库存(安装软件、监控、远程通电/睡眠/唤醒等)。
Intel ME对主机系统的访问如此深入,而且它是专有的,这两个事实都使它成为了高度审查的主题。许多人(大多数人在这方面几乎没有经验)将这两个事实联系在一起,声称ME是一个后门,通常是通过混淆地引用Intel AMT的功能而不是ME。真的不可能确定吗?
我选择“英特尔ME+AMT”作为一个极端的例子:它显示了所涵盖的分析方法的威力和局限性。ME不是由简单的可执行文件组成的,你可以在操作系统中运行,因为它位于操作系统的下方,有时被称为“Ring-3”。10分析仅限于外部监测(例如,通过监测网络活动)和反向工程(在官方文件的帮助下,未打包的部分模糊固件更新)。这比分析典型的可执行文件或库更慢、更难。
答案有点复杂……比耸人听闻的头条新闻更无聊。伊戈尔·斯科钦斯基(Igor Skochinsky)和尼古拉·科纳(Nicola Corna,分别是me tools和me_cleaner的开发者)等逆向工程师对me进行了分析,而瓦西里奥斯·维弗里斯(Vassilios Verververis)等研究人员在2010年对AMT进行了彻底的分析。有趣的是,前两人认为审计二进制代码比审计源代码更可取。
只需监控网络活动并系统地测试文档中的所有声明,Ververis就可以发现Intel AMT中的大量安全问题。然而,据我所知,还没有发现任何未记录的特征。这些有问题的发现围绕着有缺陷/不安全的文档化功能实现展开。换句话说:没有证据表明AMT是“后门”,但它的安全缺陷可能也有类似的影响。幸运的是,AMT可以被禁用。那我呢?
这就是一些二进制分析的用武之地。无论是斯科钦斯基的《我的秘密》演讲,还是之前他与科纳的关联演讲,似乎都没有列举任何与官方文件相矛盾的地方。
不幸的是,由于使用哈夫曼压缩和未知字典进行混淆,一些组件的理解很差。了解模糊组件的内部工作原理模糊了软件逆向工程和芯片实际制造之间的界限,如果你没有足够的资金进入芯片实验室,后者几乎是不可能的。然而,黑盒分析确实告诉我们这些组件的功能:请参阅“我的秘密”第21页。感谢zdctg对此的澄清。
Skochinsky和Corna的分析足以澄清(但并非完全反驳)耸人听闻的说法,即ME可以远程锁定任何PC(这是以前的选择加入功能),可以监视用户所做的任何事情(他们澄清,访问仅限于主机内存和集成GPU的未阻塞部分,但不包括帧缓冲区等),等等。
虽然诸如“我是一个可以做任何事情的黑匣子”之类的说法是误导性的,但我并非没有自己的弱点。我最喜欢看的问题是马克·埃莫洛夫和马克西姆·戈里亚希在2017年黑帽欧洲大会上的演讲:如何破解关闭的计算机,或在英特尔管理引擎中运行未签名的代码。
简言之:我拥有所有权并不意味着我们无法了解它的安全性。二元分析与运行时检查相结合,可以让我们很好地理解使用二元分析所做的权衡。虽然我有过严重的脆弱性历史,但它们与边缘阴谋论所宣称的相去甚远。11
(注:英特尔并不是唯一一家这样做的公司。其他芯片通常也有类似的产品,例如AMD安全技术)。
手动调用一个与strace这样的跟踪器相匹配的程序并不总是能够执行所有代码路径或找到边缘情况。模糊化有助于弥合这一差距:它通过生成随机或格式错误的数据来为程序提供数据,从而使导致程序失败的过程自动化。然后,研究人员研究故障和故障条件,以隔离一个bug。
模糊不一定依赖于对源代码的访问,因为它是一种黑盒技术。像American Fuzzy Loop(AFL)这样的模糊程序通常使用特殊的构建,但其他模糊设置可以使用几乎任何二进制文件。事实上,某些类型的模糊测试(例如,模糊web服务的API)几乎不需要任何实现细节。
Fuzzing经常捕获只有通过运行程序而不是通过读取源代码才能发现的bug。即便如此,模糊化的最大受益者还是开源项目。cURL、OpenSSL、web浏览器、文本呈现库(HarfBuzz、FreeType)和工具链(GCC、Clang、官方Go工具链等)都是一些值得注意的例子。写了关于起毛卷曲:
我';我以前说过,但让我再说一遍:一旦我们';我们修复了静态分析器指出的所有缺陷。curl的主要fuzzing是由OSS Fuzz完成的,它不懈地不断修改最新的curl代码。
如果你想开始模糊化,我建议你看看《美国模糊循环快速入门指南》。Go 1.18等一些语言的标准库中也提供了模糊工具。
最近的一个例子是CVE-2022-0185,它说明了模糊处理如何帮助发现开源项目中的漏洞:几周前,Crustaders of Rust发现了一个Linux 0-day。它是使用SyzCaller内核模糊器发现的。该过程记录在Will的根目录中:
我强烈建议大家读一读;这是一个完美的例子,可以使用消毒剂进行模糊处理,找到漏洞,复制漏洞(通过编写一个小型C程序),然后深入源代码查找并修复原因,最后报告问题(使用补丁!)。当来源不可用时,供应商将承担“查找并修复”步骤的责任。
现有的一些最常用的牙线是脆弱性分析中来源不可知方法的最大受益者,这一事实应该很有启发性。这些项目的源代码已经受到了数百万人的关注,但他们仍然投资于模糊基础设施,漏洞搜寻者更喜欢分析工件而不是检查源代码。
从安全角度来看,我很乐意承认几点支持源可用性:
源代码可以通过补充与源代码无关的方法来简化分析。我在四步漏洞修复过程中提到的步骤之间的界限很模糊。
修补漏洞很重要。源可用性使社区、软件包维护人员或报告者能够发现补丁软件的漏洞。软件包维护人员经常通过帮助项目从废弃/不安全的依赖关系中迁移,模糊“打包者”和“贡献者”之间的界限。想到的一个例子是针对Calibre等项目的Python 2到Python 3的转换。12能够独立于上游支持解决问题,是防止用户驯化的重要措施。
一些开发人员/供应商不发布使用现代工具链级漏洞缓解措施的二进制文件(例如PIE、RELRO、stack Canaris、自动变量初始化、CFI等13)。在这些情况下,使用这些缓解措施自己构建软件(或将其委托给实施这些缓解措施的发行版)需要源代码可用性(或至少某种中间表示)。
封闭源代码软件可能有也可能没有包含消毒器和调试符号的可用版本。
虽然模糊化发布二进制文件是可能的,但在源代码可用时,模糊化要容易得多。专有软件的供应商很少发布特殊的模糊友好版本,如果不了解高级设计,过滤误报可能会非常繁琐。
当然,在源代码中可能会发现一个漏洞。除了静态代码分析和同行评审通常会带来的低效成果外,这并不是当今发现大多数漏洞的主要方式(感谢您提醒我源代码分析的作用)。
软件即服务的分析可能非常困难,因为我们通常只具备查询服务器的能力。服务器不发送核心转储、服务器端二进制文件或跟踪日志进行分析。此外,很难验证服务器正在运行哪个软件。14对于需要信任服务器的服务,从安全和用户自由的角度来看,访问服务器端软件都很重要
本文的大部分内容都假设二进制文件是可检查和可追踪的。二进制混淆和某些形式的内容保护/DRM违反了这一假设,实际上会使分析变得更加困难。
除了源代码,开发过程的透明度有助于确保用户遵守
......