This blogpost is about a vulnerability that I found in Panda Antivirus that leads to privilege escalation from an unprivileged account to SYSTEM.
The affected products are : Versions < 18.07.03 of Panda Dome, Panda Internet Security, Panda Antivirus Pro, Panda Global Protection, Panda Gold Protection, and old versions of Panda Antivirus >= 15.0.4.
The vulnerability was fixed in the latest version : 18.07.03
The Vulnerability:
The vulnerable system service is AgentSvc.exe. This service creates a global section object and a corresponding global event that is signaled whenever a process that writes to the shared memory wants the data to be processed by the service. The vulnerability lies in the weak permissions that are affected to both these objects allowing "Everyone" including unprivileged users to manipulate the shared memory and the event.
(Click to zoom) |
(Click to zoom) |
Reverse Engineering and Exploitation :
The service creates a thread that waits indefinitely on the memory change event and parses the contents of the memory when the event is signaled. We'll briefly describe what the service expects the contents of the memory to be and how they're interpreted.When the second word from the start of the shared memory isn't zero, a call is made to the function shown below with a pointer to the address of the head of a list.
(Click to zoom) |
The structure of a list element looks like this, we'll see what that string should be representing shortly :
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;
//..
};
As shown below, the code expects a unicode string at offset 2 of the shared memory. It instantiates a "wstring" object with the string and converts the string to ANSI in a "string" object. Moreover, a string is initialized on line 50 with "3sa342ZvSfB68aEq" and passed to the function "DecodeAndDecryptData" along with the attacker's controlled ANSI string and a pointer to an output string object.
(Click to zoom) |
The function simply decodes the string from base64 and decrypts the result using RC2 with the key "3sa342ZvSfB68aEq". So whatever we supply in the shared memory must be RC2 encrypted and then base64 encoded.
(Click to zoom) |
When returning from the above function, the decoded data is converted to a "wstring" (indicating the nature of the decrypted data). The do-while loop extracts the sub-strings delimited by '|' and inserts each one of them in the list that was passed in the arguments.
(Click to zoom) |
When returning from this function, we're back at the thread's main function (code below) where the list is traversed and the strings are passed to the method InsertEvent of the CDispatcher class present in Dispatcher.dll. We'll see in a second what an event stands for in this context.
(Click to zoom) |
In Dispatcher.dll we examine the CDispatcher::InsertEvent method and see that it inserts the event string in a CQueue queue.
(Click to zoom) |
The queue elements are processed in the CDispatcher::Run method running in a separate thread as shown in the disassembly below.
(Click to zoom) |
The CRegisterPlugin::ProcessEvent method does parsing of the attacker controlled string; Looking at the debug error messages, we find that we're dealing with an open-source JSON parser : https://github.com/udp/json-parser
(Click to zoom) |
Now that we know what the service expects us to send it as data, we need to know the JSON properties that we should supply.
The method CDispatcher::Initialize calls an interesting method CRegisterPlugins::LoadAllPlugins that reads the path where Panda is installed from the registry then accesses the "Plugins" folder and loads all the DLLs there.
A DLL that caught my attention immediately was Plugin_Commands.dll and it appears that it executes command-line commands.
(Click to zoom) |
Since these DLLs have debugging error messages, they make locating methods pretty easy. It only takes a few seconds to find the Run method shown below in Plugin_Commands.dll.
(Click to zoom) |
In this function we find the queried JSON properties from the input :
(Click to zoom) |
It also didn't hurt to intercept some of these JSON messages from the kernel debugger (it took me a few minutes to intercept a command-line execute event).
(Click to zoom) |
The ExeName field is present as we saw in the disassembly, an URL, and two md5 hashes. By then, I was wondering if it was possible to execute something from disk and what properties were mandatory and which were optional.
Tracking the SourcePath property in the Run method's disassembly we find a function that parses the value of this property and determines whether it points to an URL or to a file on disk. So it seems that it is possible to execute a file from disk by using the file:// URI.
(Click to zoom) |
Looking for the mandatory properties, we find that we must supply at minimum these two : ExeName and SourcePath (as shown below).
Fails (JZ fail) if the property ExeName is absent |
Fails if the property SourcePath is absent |
However when we queue a "CmdLineExecute" event with only these two fields set, our process isn't created. While debugging this, I found that the "ExeMD5" property is also mandatory and it should contain a valid MD5 hash of the executable to run.
The function CheckMD5Match dynamically calculates the file hash and compares it to the one we supply in the JSON property.
(Click to zoom) |
And if successful the execution flow takes as to "CreateProcessW".
(Click to zoom) |
Testing with the following JSON (RC2 + Base64 encoded) we see that we successfully executed cmd.exe as SYSTEM :
{
"CmdLineExecute":
{
"ExeName": "cmd.exe",
"SourcePath": "file://C:\\Windows\\System32",
"ExeMD5": "fef8118edf7918d3c795d6ef03800519"
}
}
(Click to zoom) |
However when we try to supply an executable of our own, Panda will detect it as malware and delete it, even if the file is benign.
There is a simple bypass for this in which we tell cmd.exe to start our process for us instead. The final JSON would look like something like this :
{
"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
}
}
The final exploit drops a file from the resource section to disk, calculates the MD5 hash of cmd.exe present on the machine, builds the JSON, encrypts then encodes it, and finally writes the result to the shared memory prior to signaling the event.
Also note that the exploit works without recompiling on all the products affected under all supported Windows versions.
(Click to zoom) |
The exploit's source code is on my GitHub page, here is a link to the repository : https://github.com/SouhailHammou/Panda-Antivirus-LPE
Thanks for reading and until another time :)
Follow me on Twitter : here