潜藏19年的WinRAR代码执行漏洞解析

研究人员发现WinRAR的逻辑漏洞,可以完全获取受害者计算机的控制权。漏洞利用只需要从压缩文件中进行提取就可以工作,超过5亿用户受到影响。更重要的是该漏洞已经存在19年了,迫使WinRAR完全放弃对有漏洞的格式的支持。
研究人员使用WinAFL fuzzer对WinRAR进行了模糊测试,发现WinRAR在使用一个2006年编译的没有任何保护机制的过时的动态链接库(dll)文件。研究人员进一步分析发现了一个内部破坏漏洞会导致远程代码执行。
概述
研究人员在WinRAR使用的unacev2.dll中发现一个路径遍历漏洞,漏洞允许提取文件到任意路径并完全忽略目的文件夹,并将提取的文件相对路径作为完全路径。
导致路径遍历漏洞有两个限制:
1. 第一个字符是‘/’、 ‘’;
2. ‘*’ 至少应该包含在文件名中一次。位置没有关系。
WinRAR部分存在路径遍历漏洞:
从WinRAR回调函数(ACE_CALLBACK_RETURN_CANCEL)获取中止代码(abort code)后,unacev2.dll并不中止操作。因为对WinRAR回调的返回代码的延迟检查,创建了漏洞利用文件中指定的目录。
提取的文件也会创建在漏洞利用文件指定的完全路径,但在检查了回调的返回代码后就会删除。
研究人员发现了一种绕过文件删除的方式,但这种方式只允许创建空文件。
根源分析
研究人员想知道目的文件夹被忽视、以及压缩文件的相对路径被当作完全路径的原因。
研究人员使用DynamoRio来记录ACE文件的unacev2.dll的代码覆盖率以及触发漏洞的漏洞利用文件。然后使用IDA的 lighthouse插件,并用一个覆盖路径替代另一个覆盖路径。
下面是获取的结果:

图14: Lighthouse coverage窗口
Lighthouse插件将不同的基本区块背景标记为蓝色,如下图所示。

图15: unacev2.dll中bug的IDA图形显示
从代码覆盖的结果来看,漏洞利用文件并不是通过上图中的蓝色区块,而是相反的红色箭头的基本区块。
如果代码流是红色箭头指向的判定条件,绿色框中的代码会用空字符串(””)替换目的文件夹,后者会调用sprintf函数将目的文件夹和提取出的文件的相对路径和目的文件夹连接在一起。
代码流的正确和错误判断在图中用绿色和红色箭头分别表示,受到对函数GetDevicePathLen调用的影响。
如果调用函数GetDevicePathLen返回的结果是0,sprintf就是:
sprintf(final_file_path, "%s%s", destination_folder, file_relative_path);
否则就是:
sprintf(final_file_path, "%s%s", "", file_relative_path);
第二个sprintf 是有漏洞的,会触发路径遍历漏洞。
也就是说相对路径被当作到文件或文件夹的完全路径来处理了。
下面看一下 GetDevicePathLen函数以更好地理解:

图16: GetDevicePathLen代码
提取的文件的相对路径被传递给GetDevicePathLen。
检查设备或驱动名前缀是否在Path参数中,并返回该字符串的长度:
· 对路径C:some_foldersome_file.ext,函数返回3;
· 对路径some_foldersome_file.ext,函数返回1;
· 对路径LOCALHOSTC$some_foldersome_file.ext,函数返回15;
· 对路径?Harddisk0Volume1some_foldersome_file.ext,函数返回21;
· 对路径some_foldersome_file.ext,函数返回0。
如果GetDevicePathLen的返回值大于0,提取的文件的相对路径就会被认为是完全路径,因为目的文件夹会被调用sprintf的空字符串所替换,这会导致路径遍历漏洞。
但是有一个函数会在调用GetDevicePathLen之前,通过省去不允许的顺序来清除提取文件的相对路径。
下面是清除路径的函数“CleanPath”的伪代码:

图17: CleanPath的伪代码表示
函数会省去像“..” 这样繁琐的路径遍历序列,而且会省略“C:”, “C:”这样的盘符。而且该函数并不关心第一个字母,类似“_:”, “_:”, “_:_:”这样的序列也会被省去。
结合
为了能够成功创建一个使WinRAR将文件提取到人与路径的漏洞利用文件,研究人员尝试将文件提取到开始菜单而不是原来的目的文件夹,这样在重启后就可以获取代码执行的权限了。
为了触发该漏洞,还要绕过两个过滤函数:
为了触发空字符串和压缩文件的相对路径相结合,而不是与目的文件夹相结合:
sprintf(final_file_path, "%s%s", "", file_relative_path);
而不是
sprintf(final_file_path, "%s%s", destination_folder, file_relative_path);
GetDevicePathLen函数返回的结果应该大于0。
这是根据相对路径(file_relative_path)的内容决定的。如果相对路径以设备路径开始:
· option 1: C:some_foldersome_file.ext
· option 2: some_foldersome_file.ext (表示当前盘符)
GetDevicePathLen的返回值就大于等于0。
但是在unacev2.dll中还有一个名为CleanPath (图17)的过滤函数,负责检查相对路径是否是以 C: 开始的,并在调用GetDevicePathLen前将它移除。
函数会省去 option 1字符串中的C:,但是不会省去option 2字符串中的。

为了克服这个限制,研究人员在 option 1中加入了另一个CleanPath函数会省去的C:序列,并使相对路径的形式如下:
· option 1’: C:C:some_foldersome_file.ext  =>  C:some_foldersome_file.ext
但这里有WinRAR代码中的回调函数,用做验证和过滤。在提取的过程中,WinRAR代码中的unacev2.dll 也会被回调函数调用。
回调函数会验证压缩文件的相对路径的有效性。如果发现黑名单序列,就中止提取操作。
回调函数做的一个检查就是以开始的相对路径。
但它并不检查C:。因此,研究人员使用option 1’来进行路径遍历漏洞的利用。
研究人员还发现一个SMB攻击向量,可以连接到任意的IP地址,并在SMB服务器的任意路径创建文件和文件夹。比如:
C:10.10.10.10smb_folder_namesome_foldersome_file.ext => 10.10.10.10smb_folder_namesome_foldersome_file.ext
利用文件示例
研究人员将.ace扩展修改为.rat扩展,因为WinRAR是通过文件的内容而不是扩展来检测格式的。
下面是ace文件的输出:

图18: 简单漏洞利用文件acefile.py的Header输出
研究人员通过文件名域(绿色框)伪造的字符串来触发漏洞。不管路径的目的文件夹是哪里,文件都会被提取到C:some_foldersome_file.txt 。
PoC见https://research.checkpoint.com/extracting-code-execution-from-winrar/。