0x00 前言
本文介绍了我在Panda反病毒软件中发现的一个提权漏洞,该漏洞可以将低权限账户提升至SYSTEM
权限。
受影响版本:版本号低于18.07.03的Panda Dome、Panda Internet Security、Panda Antivirus Pro、Panda Global Protection、Panda Gold Protection以及版本号高于15.0.4的老版本Panda反病毒软件
18.07.03中已经修复了该漏洞。
0x01 漏洞描述
存在漏洞的系统服务为AgentSvc.exe
,该服务会创建一个全局Section对象以及一个对应的全局事件,每当进程将数据写入共享内存待服务处理时,该事件就会被触发。这些对象在权限管理上存在问题,导致Everyone
(包括非特权用户)可以控制共享内存以及事件。
0x02 逆向分析及漏洞利用
该服务会创建一个线程,无限期等待内存更改事件,并在事件触发后解析内存数据。这里我们简单描述下该服务希望获取的内存数据以及对这些数据的解析方式。
当共享内存起始处的第二个word
不等于0时,该服务就会调用如下函数,其中指针指向事件列表的头部地址。
列表元素的结构如下所示(后面我们将分析其中字符串的具体含义):
typedef struct StdList_Event
{
struct StdList_Event* Next;
struct StdList_Event* Previous;
struct c_string
{
union
{
char* pStr;
char str[16];
};
unsigned int Length;
unsigned int InStructureStringMaxLen;
} DipsatcherEventString;
//..
};
如下所示,代码希望获取位于共享内存offset 2处的一个unicode字符串,然后使用该字符串来实例化一个wstring
对象,并在一个string
对象中将该字符串转换为ANSI编码。此外,代码在第50行会使用3sa342ZvSfB68aEq
来初始化一个字符串,将该字符串、攻击者可控的ANSI字符串以及一个字符串对象指针(用于输出)一起传递给DecodeAndDecryptData
函数。
该函数使用base64解码字符串,然后使用RC2算法(秘钥为3sa342ZvSfB68aEq
)解密解码后的结果。因此我们输入共享内存的数据必须先经过RC2加密,然后再使用base64编码。
当该函数返回时,解码后的数据会被转化成一个wstring
。在do-while
循环中,代码提取由|
分割的子字符串,将每个子字符串插入列表中。
从该函数返回时,我们会回到线程的主函数(如下所示),其中代码会遍历该列表,将所有字符串传递给CDispatcher
类中的InsertEvent
方法(该类位于Dispatcher.dll
)中。很快我们就知道这里事件(event)所代表的具体含义。
在Dispatcher.dll
的CDispatcher::InsertEvent
方法中,我们可以看到该方法会将事件字符串插入一个CQueue
队列中。
队列元素由CDispatcher::Run
方法负责处理,该方法运行在另一个线程中,反汇编代码如下所示:
CRegisterPlugin::ProcessEvent
方法会解析攻击者可控的字符串。查找调试错误信息,我们发现目标使用的是开源的JSON解析器:https://github.com/udp/json-parser
知道目标服务希望我们输入数据后,我们需要知道待提交的JSON属性。
CDispatcher::Initialize
方法会调用一个有趣的方法:CRegisterPlugins::LoadAllPlugins
,后者会从注册表中读取Panda的安装路径,然后访问Plugins
目录,加载其中所有的DLL。
这里我注意到有一个DLL:Plugin_Commands.dll
,从名字来判断该DLL会执行命令行命令。
由于这些DLL包含调试错误信息,因此定位其中包含的方法也非常简单。几秒之后我就找到了Plugin_Commands.dll
中的Run
方法。
在该函数中,我们从输入信息中找到了所需的JSON属性:
我们也可以从内核调试器中拦截其中某些JSON信息(我花了几分钟才拦截到一个命令行执行事件):
与反汇编代码一致,我们可以找到ExeName
字段、URL以及两个md5哈希。此时我比较好奇我们是否可以执行磁盘上的某些程序,哪些是强制性属性、哪些是可选属性。
跟踪Run
方法反汇编代码的SourcePath
属性后,我们找到了解析该属性值的一个函数,该函数会判断该属性指向的是URL还是磁盘上的文件。因此,似乎我们可以使用file://URI
这种方式来执行磁盘上的文件。
在强制性属性方面,我发现我们至少要提供2个属性:ExeName
以及SourcePath
,如下所示:
然而,当我们只使用这两个字段将CmdLineExecute
事件加入队列中时,我们的进程并没有被成功创建。在调试过程中,我发现ExeMD5
也是一个强制属性,该属性必须包含待运行程序的MD5哈希。
CheckMD5Match
函数会动态计算文件哈希,将其与我们在JSON属性中提供的值进行比较。
如果这两个值相匹配,则目标服务会使用CreateProcessW
来创建进程。
使用如下JSON数据进行测试后,我们就能以SYSTEM
权限成功执行cmd.exe
:
{
"CmdLineExecute":
{
"ExeName": "cmd.exe",
"SourcePath": "file://C:\Windows\System32",
"ExeMD5": "fef8118edf7918d3c795d6ef03800519"
}
}
然而,如果我们想执行自己的程序,Panda就会将其标记为恶意软件并删除该程序,即使该文件实际上无害也难以避免。
这其实很容易绕过,我们可以让cmd.exe
来运行我们的进程。最终我们构造的JSON载荷如下所示:
{
"CmdLineExecute":
{
"ExeName": "cmd.exe",
"Parameters": "/c start C:\Users\VM\Desktop\run_me_as_system.exe",
"SourcePath": "file://C:\Windows\System32",
"ExeMD5": "fef8118edf7918d3c795d6ef03800519" //MD5 hash of CMD.EXE
}
}
在最终的利用代码中,我们从资源区中提取待执行文件并保存到磁盘上,计算目标主机上cmd.exe
的MD5哈希,构造JSON数据并进行加密及编码处理,然后在事件触发之前将处理结果写入共享内存中。
需要注意的是,在所有支持的Windows系统上,只要安装了受影响的产品,我们都无需重新编译漏洞利用代码就可以顺利运行。