Windows Snip and Sketch中的Acropalypse漏洞,开发者中心安全的教训
Acropalypse是一个最初在Google Pixel手机截图工具中发现的安全漏洞,裁剪图片后原始图像仍可恢复。由于被裁剪部分可能包含敏感信息,这构成了严重的安全问题。该问题的产生是因为Android API默认行为从截断文件变为保留现有内容。因此,生成图像文件的开头包含裁剪后内容,但原始文件的末尾仍然存在。图像查看器会忽略这些数据并正常打开文件,但通过对所用压缩算法的巧妙分析,可以(部分)恢复原始图像。
在该漏洞公布后不久,有人注意到Windows默认截图工具Snip and Sketch似乎存在相同问题,尽管这是完全不同操作系统上的无关应用程序。我在2004年也曾发现JPEG缩略图图像的类似问题。当相同漏洞反复出现时,这表明我们构建软件的方式存在系统性问题,因此我着手深入了解Windows Snip and Sketch中存在该漏洞的原因。
有缺陷的API
我发现的第一个问题是,现代Windows文件保存API存在与Android非常相似的问题。具体来说,现有文件默认不会被截断。可以说这个漏洞更严重,因为与Android不同,Windows没有截断文件的选项。Windows文档在说明需要截断文件以及实现所需结果的代码方面,最多只能说是含糊不清。
情况并非一直如此。旧的Win32保存文件API大致是:显示文件选择器,获取用户选择的文件名,然后打开文件。打开文件时,程序员必须指定是否覆盖文件,示例代码通常会覆盖文件。然而,新的"更安全"的通用Windows平台(UWP)将文件选择器沙盒化在单独进程中,允许基于能力的访问控制等简洁功能。它会在需要时创建文件并返回句柄,如果所选文件存在,则不会覆盖现有内容。
然而,从文档来看,程序员可以理解地假设文件将是空的。
"该storageFile的文件名、扩展名和位置与用户指定的匹配,但文件没有内容。"
除非程序员显式截断文件,否则现有文件的内容将被保留。如果写入的数据小于现有文件的大小,旧内容将保留,导致StackOverflow上出现困惑的帖子。FileSavePicker的文档没有提到这个问题,尽管示例代码通过使用简单的FileIO API避免了漏洞,该API在写入前隐式截断文件。
然而,更复杂的程序会使用DataWriter,这些示例不会截断文件,文档也没有指出两种API之间的这种差异。不截断现有文件的默认行为很常见,尽管这不是大多数人想要的。
文档可以更新以澄清风险,这总是受欢迎的,特别是伴随着安全的示例代码(众所周知这些代码会被直接复制粘贴到应用程序中)。然而,这两者都不能弥补Windows UWP或Android中有缺陷的API。使用这些API编写安全代码是可能的,但默认行为既不安全也不是大多数人想要的。以开发者为中心的安全的基本原则是设计默认行为安全的API,并且不应该意外创建不安全的程序。更安全的API是让FileSavePicker默认截断现有文件。或者,OpenAsync可以有打开流进行写入的选项。目前,它只有Read和ReadWrite,不像功能丰富得多的Win32 CreateFile API。
我们应该重新审视Postel法则吗?
但为什么这个缺陷持续了这么长时间?Android 10于2019年发布,而Windows Snip and Sketch自2018年发布以来似乎就一直存在漏洞。当然,因为这些应用程序生成的文件损坏,有人会抱怨?实际上,标准做法是遵循Postel法则:"发送时要保守,接收时要开放"。图像查看器应用程序可以找到有效裁剪图像的末尾,并将原始文件的残留视为可以安全忽略的垃圾。因此,很长时间没有人发现问题,当有人最终发现时,最初并没有认识到这是一个严重问题。
也许是时候超越Postel法则了。它对互联网的发展很重要,但现在正在成为一种负担。拒绝无效输入可以帮助更早地发现问题,此时它们造成危害的机会较少。我不是第一个指出这一点的人,甚至Jon Postel也认为他的原则被误解了。
下一步是什么?
Acropalypse已在Android(CVE-2023-21036)和Windows Snip and Sketch(CVE-2023-28303)上修复,但它也为未来提供了教训。它作为一个案例研究,说明了良好文档的重要性,更重要的是精心设计的API和安全的示例代码。它还表明,解决相同问题的多个程序经常具有相同的漏洞,因此仅仅比较独立实现的结果获得的好处可能比最初预期的要少。该漏洞还提出了一些问题,比如我们应该如何教授安全软件开发。
此外,虽然我没有彻底调查UWP API,但我最初看到的内容确实让我有些担忧。例如,OpenAsync的API非常薄弱。Win32 CreateFile允许在打开前检查现有文件并设置安全参数。CreateFile做了很多工作来确保所有操作原子性进行以避免竞争条件。UWP要求这些相同的步骤分开。是否可能隐藏着一些竞争条件?
最后,鉴于有缺陷的API,期望其他应用程序也容易受到Acropalypse攻击是合理的。扫描此类问题并非易事,但我认为可以在Process Monitor中发现该行为。这是在Snip and Sketch中覆盖文件的样子。文件选择器进行一些检查,Runtime Broker打开它,Snip and Sketch写入文件。重要的是,NtCreateFile的处置是FILE_OPEN,因此不会覆盖文件。
然而,应该发生的是在写入前截断文件。在Windows中有几种方法可以做到这一点,但这是我调用stream.SetLength(0)时发生的情况。请注意,文件总是会存在,因为选择器会在需要时创建它。
因此,我认为如果存在文件被打开、非零长度以及在截断前有写入的序列,那可能是Acropalypse易受攻击应用程序的实例。
如果有人想编写代码在Process Monitor日志中搜索此类序列,我认为会非常有趣。如果你尝试了,请告诉我!
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
公众号二维码