In this blog post, I’ll be describing two bugs I found inside the MalwareFox AntiMalware drivers (zam32.sys/zam64.sys) that allow a non-privileged process to “authenticate” itself with the driver and issue special IOCTLs leading to privilege escalation.
This process of registration or authentication is used by the driver to know
which processes to trust when receiving a device control request. Normally, these processes should be the
antimalware’s own processes.
A process that is authenticated by the driver can send special IOCTLs
that cannot be sent by other non-authenticated processes. These special IOCTLs can be used to:
-
Enable/Disable real-time protection
-
Read/Write to raw disk
-
Create full access user-mode process handles
-
…etc
Registered processes are stored in an array located in the data section
of the driver. In zam64.sys, each element of the array has 0x980 bytes and the
maximum number of elements is 100. An element contains information on the process
such as its PID, its session id and the name of the image file name from the
EPROCESS structure.
During the run-time of the anti-virus only a single process is
registered with the driver, and that is MalwareFox’s own process ZAM.exe, which runs within session
1. There’s also a ZAM.exe process running as
a Windows service but it doesn’t seem to be
registered.
Figure 1 – MalwareFox’s entry in the
registration array
So, by registering our process with the driver, we enable the god-mode
capability to send special IOCTLs and basically make use of them to escalate privileges
on the system.
I have found two ways to do so.
CVE-2018-6593: Register the process
by connecting to the mini-filter communication port:
As shown in the figure below, a default security descriptor is built for the mini-filter communication port allowing access only to SYSTEM and the administrators. But right after that, RtlSetDaclSecurityDescriptor is called with a NULL DACL pointer. This leads to the DACL pointer, that was setup by FltBuildDefaultSecurityDescriptor, being overwritten with NULL. As a result, everyone has access to the object.
In addition, the maximum number of connections allowed to the port are
100 even though only a single connection appears to be needed by the
Antimalware.
Figure 2
And here’s what it looks like under Windbg.
Figure 3
The interesting thing here is that when a process connects to the port,
the driver automatically registers it as a trusted process in the array we saw
above. The figure below displays the first and second entries in the array when our process (exploit.exe) is connected to the port.
Figure 4
Our process is now registered and can send special IOCTLs as it pleases.
It turns out, the developers zeroed to DACL pointer of the port’s security descriptor because their own process (ZAM.exe) doesn’t run with administrator privileges and turns at medium IL. This of
course isn’t an excuse to disable all access
checks and from everything we saw until now this is probably an anti-virus you
don’t want on your machine; and this is not all!
CVE-2018-6606: Registering the process
by sending IOCTL 0x80002010
It turns out there’s a straightforward way to register a process as trusted. Send IOCTL 0x80002010 with a process id of your choice and voilà the process with the PID you supplied is now registered and fully trusted by the driver!!!
What the driver fails to do here is check if the requestor process itself
is a registered process. It does check for this when a process sends a special
IOCTL, but it fails to do so if the process wants to register another process
as trusted; rendering all other checks useless.
Thus, all we need to do to register our process is send IOCTL 0x80002010
with our process’s PID.
Figure 5
We can now send any special IOCTLs we want, and we’ll be using IOCTL 0x8000204C to elevate privileges.
Getting SYSTEM
MalwareFox seems to need user-mode full access handles to processes. And
since we saw how its usermode process lacks the necessary privileges, it delegates this task of opening handles to its driver.
IOCTL 0x8000204C is a special IOCTL that must be sent by a registered
process to the driver. The requestor simply provides a PID as an input and gets
a full access handle as an output from kernel-mode; how cool is that for us ?
We use this opportunity to open a full access handle to winlogon.exe,
inject a cmd.exe shellcode and then create a remote thread.
Figure 6
CVE-2018-6593:
CVE-2018-6606:
A video demonstrating the first bug
(CVE-2018-6593):
Source code for CVE-2018-6606: https://goo.gl/YtsYEo
No comments:
Post a Comment