Windows截图工具中的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的disposition是FILE_OPEN,因此不会覆盖文件。
然而,应该发生的是在写入前截断文件。在Windows中有几种方法可以做到这一点,但这是我调用stream.SetLength(0)时发生的情况。请注意,文件总是会存在,因为选择器会在需要时创建它。
因此,我认为如果存在文件被打开、它不是零长度、以及在截断前有写入的序列,那可能是Acropalypse易受攻击应用程序的一个实例。
如果有人想编写代码在Process Monitor日志中搜索此类序列,我认为会非常有趣。如果你尝试了,请告诉我!
Unsplash的Fotis Fotopoulos提供的照片