Windows窗口消息模糊测试:19年后93%应用仍崩溃

本文重现了2000年NT模糊测试报告,使用相同工具测试现代Windows应用程序对畸形窗口消息的处理能力。结果显示93%的应用仍然崩溃,并发现了一个Windows系统bug。

Windows窗口消息模糊测试:19年后93%应用仍崩溃

概述

这是我们重现现代系统上原始模糊测试研究的第二部分。本次我们通过在Windows上重现Justin E. Forrester和Barton P. Miller于2000年发表的《使用随机测试对Windows NT应用程序鲁棒性的实证研究》(又称"NT模糊测试报告")的结果来探讨Windows模糊测试。

NT模糊测试报告测试了33个Windows NT和早期Windows 2000应用程序对畸形窗口消息以及随机生成的鼠标和键盘事件的敏感性。由于Miller博士发布了模糊测试器代码,我们使用与原作者完全相同的工具来发现现代Windows应用程序中的错误。

结果几乎相同:19年前,100%的测试应用程序在使用畸形窗口消息进行模糊测试时崩溃或冻结。如今,93%的测试应用程序在面对相同的模糊测试器时崩溃或冻结。未崩溃的应用程序中包括我们的老朋友计算器(图1)。我们还在Windows中发现了一个错误(但不是安全问题)。

图1:受伤但未被击败。 最近开源的Windows计算器是两个测试应用程序之一,在面对2000年的窗口消息模糊测试器后没有冻结或崩溃。模糊测试后计算器被调整大小以展示模糊测试会话的痕迹。

Windows快速介绍

那么什么是窗口消息,为什么它们会使程序崩溃?

显示GUI的Windows应用程序由事件驱动:鼠标移动、按钮点击、按键等。事件驱动的应用程序在收到事件通知之前不会执行任何操作。一旦收到事件,应用程序会根据事件采取行动,然后等待更多事件。如果这听起来很熟悉,那是因为这种架构在node.js等平台中正在复兴。

窗口消息是Windows中的事件通知方法。每个窗口消息都有一个与特定事件关联的数字代码。每个消息都有一个或多个参数,按惯例称为lParam和wParam,用于指定事件的更多细节。这些细节的示例包括鼠标移动的坐标、按下了什么键或在窗口中绘制什么文本。这些消息可以由程序本身、操作系统或其他程序发送。它们可以在任何时间和任何顺序到达,并且必须由接收应用程序处理。

安全影响

在Windows Vista之前,低权限进程可以向高权限进程发送消息。使用正确的消息组合,可以在高权限进程中获得代码执行。这些"粉碎攻击"自Vista以来通过UIPI和将系统服务隔离在单独会话中得到了很大缓解。

错误处理窗口消息不太可能对现代Windows系统产生安全影响,原因有两个。首先,窗口消息不能通过网络发送。其次,以您已经拥有的相同权限级别崩溃或获得代码执行没有用处。NT模糊测试报告的作者可能已经意识到这一点。他们没有提出安全声明,但正确指出窗口消息处理期间的崩溃意味着缺乏严格测试。

在某些领域,相同权限的代码执行可能会违反真正的安全边界。一些应用程序结合各种安全原语来创建操作系统中原本不存在的特权级别。主要的例子是浏览器的渲染器沙箱。浏览器供应商很清楚这些问题并采取措施来缓解它们。另一个例子是防病毒产品。它们的控制面板以普通用户权限运行,但受到产品其他部分的检查和篡改保护。

测试方法

我们使用原始NT模糊测试报告中描述的相同核心模糊测试代码和方法来模糊测试我们测试集中的所有应用程序。具体来说,在SendMessage和PostMessage两种模式下,模糊测试器使用种子42进行三次500,000条消息的迭代,并使用种子1,337进行三次500,000条消息的迭代。我们在执行每种方法的一次迭代后就看到了结果。

由于时间限制和希望纯粹专注于窗口消息,省略了使用"随机鼠标和键盘输入"方法的模糊测试。我们鼓励您也复制这些结果。

注意事项

在Windows 10上使用模糊测试器需要进行两个小改动。第一个是在64位Windows上构建模糊测试器的小改动。第二个改动是使模糊测试器能够通过命令行参数定位特定的窗口句柄。模糊测试特定句柄是模糊测试通用Windows平台(UWP)应用程序问题的快速解决方案。窗口消息模糊测试器面向模糊测试属于特定进程的窗口,但UWP应用程序都通过同一进程显示其UI(图2)。这意味着模糊测试器无法定位UWP应用程序的主窗口。

图2:UWP应用程序窗口都属于同一进程(ApplicationFrameHost.exe)。 为了模糊测试这些应用程序,修改了原始NT模糊测试器以允许模糊测试用户指定的窗口句柄。

在修改模糊测试器时,发现了一个严重缺陷:为两个主要随机输入源选择的值,即SendMessage和PostMessage的lParam和wParam参数,仅限于16位整数。这两个参数在32位Windows上是32位,在64位Windows上是64位。问题出现在Fuzz.cpp中,其中设置了lParam和wParam值:

1
2
wParam = (UINT) rand();
lParam = (LONG) rand();

rand()函数返回范围在[0, 2^16]的数字,大大限制了测试值的集合。在评估期间故意保留了这个错误,以确保结果与原始工作准确可比。

测试的应用程序

NT模糊测试报告测试了33个程序。这次重现只测试了28个,因为每个程序只使用一个版本进行测试。自2000年以来,Windows软件生态系统发生了巨大变化,但也存在惊人的保守性。Microsoft Office套件包含与原始测试相同的程序。Netscape Communicator演变成了现在的Firefox。Adobe Acrobat更名为Adobe Reader,但仍然很强大。甚至Winamp在2018年发布了新版本,允许与原始NT模糊测试报告进行公平比较。然而,一些遗留软件已经随着上个千年而消失。以下是更改列表及原因:

  • CD Player → Windows Media Player:Windows Media Player已包含CD Player功能
  • Eudora → Windows Mail:Qualcomm现在制造基带,而不是电子邮件客户端
  • Command AntiVirus → Avast Free Edition:Command产品不再可用
  • GSView → Photos:GSView应用程序不再维护
  • JavaWorkshop → NetBeans IDE:JavaWorkshop IDE不再维护
  • Secure CRT → BitVise SSH:Secure CRT仍然存在,但需要很长的网络表单下载试用版
  • Telnet → Putty:telnet应用程序仍然存在于Windows上,但现在是一个控制台应用程序
  • Freecell和Solitaire从Windows应用商店中的Microsoft Solitaire Collection应用程序运行

具体应用程序版本出现在结果表中。所有模糊测试都是在64位Windows 10 Pro版本1809(OS Build 17763.253)上完成的。

结果

正如NT模糊测试报告中提到的,结果不应被视为安全漏洞,而应作为软件鲁棒性和质量的衡量标准。

“最后,我们的结果形成了一个定量起点,从中可以判断软件鲁棒性的相对改进。”

——摘自Justin E. Forrester和Barton P. Miller的《使用随机测试对Windows NT应用程序鲁棒性的实证研究》

数字并不特别令人鼓舞,尽管情况正在改善。在原始NT模糊测试报告中,每个应用程序在模糊测试时要么崩溃要么冻结。现在,两个程序,计算器和Avast防病毒软件,在窗口消息模糊测试器中幸存下来,没有不良影响。我们赞扬Avast和Windows计算器团队考虑了错误的窗口消息。计算器团队因开源计算器并向所有人展示如何构建高质量的UWP应用程序而获得额外赞誉。参见表1了解我们所有的模糊测试结果以及使用的软件具体版本。

表1:在Windows 10上复制原始NT模糊测试报告的结果。 19年后,很少有应用程序正确处理畸形窗口消息。

程序 版本 SendMessage PostMessage
Microsoft Access 1901 crash crash
Adobe Reader DC 2019.010.20098 crash ok
Calculator 10.1812.10048.0 ok ok
Windows Media Player 12.0.17763.292 crash crash
Visual Studio Code 1.30.2 crash ok
Avast Free 19.2.2364 ok ok
Windows Mail 16005.11231.20182.0 crash crash
Excel 1901 crash ok
Adobe FrameMaker 15.0.2.503 crash crash
Freecell 4.3.2112.0 crash crash
GhostScript 9.26 crash ok
Photos 2019.18114.17710.0 crash crash
GNU Emacs 26.1 crash crash
IE Edge 44.17763.1.0 crash crash
NetBeans 10 crash crash
Firefox 64.0.2 crash crash
Notepad 1809 crash ok
Paint 1809 crash crash
Paint Shop Pro 2019 21.1 crash crash
Powerpoint 1901 crash ok
Bitvise SSH 8.23 crash crash
Solitaire 4.3.2112.0 crash crash
Putty 0.70 freeze freeze
VS Community 2017 15.9.5 crash crash
WinAmp 5.8 5.8 Build 3660 crash ok
Word 1901 crash ok
Wordpad 1809 crash crash
WS_FTP 12.7.0.1903 crash crash

Windows中的一个错误?

不幸的是,我们的好奇心战胜了我们,我们不得不做一个例外。一个常见的问题似乎困扰着多个不相关的应用程序。一些调试显示负责的消息是WM_DEVICECHANGE。当模糊测试器发送该消息时,它甚至会崩溃最简单的应用程序——官方的Windows API HelloWorld示例(图3)。

图3:32位HelloWorld.exe在面对窗口消息模糊测试器时崩溃。 这不应该发生,因为程序非常简单。这意味着问题出在Windows的某个地方。

使用HelloWorld示例,我们很快意识到问题只影响32位应用程序,而不影响64位应用程序。一些快速调试显示崩溃发生在wow64win.dll中,即32到64位兼容层。我对问题的快速(可能错误)分析表明,wow64win.dll!whcbfnINDEVICECHANGE函数会将wParam视为目标程序中DEV_BROADCAST_HANDLE64结构的指针。该函数将该结构转换为DEV_BROADCAST_HANDLE32结构以与32位应用程序兼容。崩溃发生是因为模糊测试器生成的wParam值指向无效内存。

将wParam视为本地指针是一个坏主意,尽管这可能是为了确保可移动设备通知与遗留32位Windows应用程序一起工作而故意设计的选择。无论如何,确实感觉不对的是,可以在不显式调试的情况下崩溃另一个应用程序。我们向MSRC报告了这个问题,尽管没有跨越安全边界。他们确认这个错误不是安全问题。我们希望在未来版本的Windows中看到这个 admittedly obscure问题的修复。

结论

窗口消息是Windows程序中一个未被充分重视且经常被忽略的不受信任输入源。即使在第一个开源窗口消息模糊测试器部署19年后,93%的测试应用程序在面对完全相同的模糊测试器时仍然冻结或崩溃。一些应用程序优雅地处理这些畸形输入的事实是一个令人鼓舞的迹象:这意味着在某些组织中存在避免这些错误的框架和制度知识。

窗口消息模糊测试也有很大的改进空间——最简单的方法可能使93%的应用程序崩溃。甚至可能存在窗口消息跨越真正安全边界的例子。如果您进一步探索这个领域,我们希望您能分享您的发现。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计