Windows Snip and Sketch中的Acropalypse漏洞:开发者中心安全的教训
Acropalypse是一个最初在Google Pixel手机截图工具中发现的安全漏洞:裁剪图片后,原始图像仍可被恢复。由于被裁剪部分可能包含敏感信息,这构成了严重的安全威胁。该问题的根源在于Android API默认行为从截断文件变为保留现有内容,导致结果图像文件开头包含裁剪后内容,但原始文件的末尾数据仍然存在。图像查看器会忽略这些数据正常打开文件,但通过对所用压缩算法的巧妙分析,原始图像可以(部分)被恢复。
在该漏洞公布后不久,有人注意到Windows默认截图工具Snip and Sketch似乎存在相同问题,尽管这是完全不同操作系统上的无关应用程序。作者早在2004年就发现过JPEG缩略图图像的类似问题。当相同漏洞反复出现时,表明软件构建方式存在系统性问题。
有缺陷的API
研究发现,现代Windows文件保存API存在与Android非常相似的问题:默认情况下不会截断现有文件。这个漏洞甚至更严重,因为与Android不同,Windows没有提供截断文件的选项。Windows文档在是否需要截断文件以及如何实现所需结果方面表述不清。
旧版Win32 API保存文件的流程大致是:显示文件选择器→获取用户选择的文件名→打开文件。打开文件时程序员必须指定是否覆盖文件,示例代码通常会覆盖文件。然而新的"更安全"的通用Windows平台(UWP)将文件选择器沙盒化在单独进程中,支持基于能力的访问控制等高级功能。它会创建所需文件并返回句柄,如果所选文件已存在,则不会覆盖现有内容。
从文档看,程序员会合理假设文件将是空的:“此storageFile的文件名、扩展名和位置与用户指定的匹配,但文件没有内容。“除非程序员显式截断文件,否则现有文件内容将被保留。如果写入数据小于现有文件大小,旧内容将保留,导致StackOverflow上出现困惑的帖子。
FileSavePicker的文档没有提及此问题,尽管示例代码通过使用简单FileIO API(在写入前隐式截断文件)避免了漏洞。但更复杂的程序会使用DataWriter,而这些示例不会截断文件,文档也没有指出两种API之间的这种差异。
重新审视Postel定律?
为什么这个缺陷持续如此之久?Android 10于2019年发布,而Windows Snip and Sketch自2018年发布以来似乎就一直存在漏洞。由于这些应用程序生成的文件已损坏,难道没有人抱怨吗?实际上,标准做法是遵循Postel定律:“发送时要保守,接收时要开放”。图像查看器应用程序可以找到有效裁剪图像的结尾,并将原始文件的残留部分视为可以安全忽略的垃圾数据。因此,长时间没有人发现问题,当最终有人发现时,最初也没有认识到这是严重问题。
也许是时候超越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中覆盖文件时,文件选择器进行一些检查,运行时代理打开文件,Snip and Sketch写入文件。重要的是,NtCreateFile的处置是FILE_OPEN,因此不会覆盖文件。
正确做法是在写入前截断文件。Windows中有几种方法可以实现这一点。如果有人编写代码在Process Monitor日志中搜索此类序列,可能会相当有趣。