
CVE-2017-8570 Exp及利用样本分析
Cause: When opening an Office document, FLTLDR.EXE is used to render an embedded EPS file containing the vulnerability. The file is written in PostScript language and can be exploited by an attacker through a "save-restore" operation, essentially a UAF vulnerability. The vulnerability can be exploited when a user opens a file containing a malformed graphic image, or when a user inserts a malformed graphic image into an Office file.
Affected versions: Microsoft Office 2010 Service Pack 2, Microsoft Office 2013 Service Pack 1, Microsoft Office 2016
POC: kcufId's Github
The author searched online for a long time and did not find an Office installation package containing EPSIMP32.FLT. Fortunately, Master kcufId provided a LoadEps.exe to load EPS files. Thanks to Master kcufId.
LoadEps.exe first loads EPSIMP32.FLT:

Then calls ImportGr to start loading the EPS file:

Directly press F7 to follow here, and then you can successfully break at the breakpoint set within EPSIMP32.FLT.
Before getting into the main topic, let's first lay out the PostScript object structure.
// PostScript Object
struct PostScript object
{
dword type;
dword attr;
dword value1;
dword value2; // if array, point to userdict where store the array object
}ps_obj;
The corresponding values for different type are as follows:
0x0 nulltype
0x3 integertype
0x5 realtype
0x8 booleantype
0x10 operatortype
0x20 marktype
0x40 savetype
0x300 nametype
0x500 stringtype
0x900 filetype
0x30000 arraytype
0x0B0000 packedarraytype
0x70000 packedarraytype
0x110000 dicttype
0x210000 gstatetype
Taking strings as an example, we elaborate on their storage structure. By setting a breakpoint on the forall function, you can further examine how it handles strings (for how to locate the forall function, refer to https://paper.seebug.org/368/).

Image 1 corresponds to ps_obj, and its value2 field points to the corresponding item in the index list (Image 2); the index item points to a structure of size 0x30. At offset 0x24 of this structure is a pointer to a pointer to a structure of size 0x28 (Image 5), and at offset 0x2C is the string size (Image 3); in the structure of Image 5, offset 0x4 stores the address of the corresponding item in the index list for this structure (i.e., 0x01DB5E94 from Image 4), offset 0x20 points to the final storage location of the string (Image 6), and offset 0x24 is the actual memory size occupied — string size + 1.
Structure of size 0x30:
+0x0 dword
+0x4 dword
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword
+0x24 dword pp_struct // pointer to pointer to structure of size 0x28
+0x28 dword
+0x2c dword size // actual string size
Structure of size 0x28 (for arrays, this structure has size 0x2C, and offset 0x28 points to array elements, each being ps_obj):
+0x0 dword
+0x4 dword // stores the address of the corresponding item in the index list for this structure
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword ptr_object // points to the final storage location of the string
+0x24 dword size // actual memory size occupied, actual string size + 1
First trigger of the vulnerability:

First, the VM state is saved in variable l62. Then, for each character in variable l63, the processing procedure l61—>>l59—>>l56 is called; l62 restore restores the previous state. As a result, the memory space allocated by l63 after the /l62 save def statement is freed, becoming a dangling pointer.

Variables l95-l99 determine the subsequent flow, and their values are all 0 (i.e., 32-bit):

Second trigger of the vulnerability: first, memory space of size 0x27 (actually occupying 0x28) is allocated to store l63:

Then l62 restore restores the previous state, causing the memory space allocated by l63 to be freed, becoming a dangling pointer; next, l100 is executed, and the memory space previously occupied by l63 is used to store the 0x28 structure of the l102 (i.e., l136) string (this explains why l63 allocates memory of size 0x27):

Retrieve the values at offsets 0x4, 0x20, and 0x24 of this structure respectively:

Finally, modify the content of the l136 string (the image only shows part of the modifications):

These modifications are carefully constructed and will be used during the third trigger of the vulnerability.
Third trigger of the vulnerability: allocate an array containing 0x37 elements, then execute l62 restore when looping to the 0x34th element:

After executing restore, the 0x30 structure of the array is overwritten with the content of the l193 string:

Thus, the object executed in the last (0x36) forall process becomes the 0x30 structure in the image above, and retrieving its 0x36th element leads to the carefully constructed string from the second vulnerability trigger:

The retrieved array element is an array of size 4, and the first element of that array is a string with a starting address of 0 and a size of 0x7FFFFFFF:

This array is stored in variable l159, and its first element — the string with start address 0 and size 0x7FFFFFFF — is stored in variable l201. After that, any address's value can be obtained via variable l201.
Obtain kernel32.dll base address:


Thus, variable l314 stores the base address of EPSIMP32.FLT.

Note: The search command syntax is as follows:

Look for a specific gadget:


Construct a file type structure:
l199 l201 get_dword
/l487 exch def
l487 l201 get_dword
/l488 exch def
l488 36 my_add l201 get_dword
/l489 exch def
l489 l201 get_dword
/l490 exch def
l490 32 my_add l201 get_dword
/l491 exch def
l199 l491 l201 put_data_to_array
l199 12 my_sub 2304 l201 put_data_to_array

Write the constructed data to address l492 (l491+0x32):
l492 0 l201 put_data_to_array %% 0x00 0
l492 4 my_add l375 l201 put_data_to_array %% 0x04 Address of <5E C3>
l492 8 my_add l373 l201 put_data_to_array %% 0x08 Address of <94 00 00 00 00 5E C3>
l492 12 my_add l377 l201 put_data_to_array %% 0x0C Address of <C2 0C 00>
l492 16 my_add l370 l201 put_data_to_array %% 0x10 Address of VirtualProtect()
l492 20 my_add 0 l201 put_data_to_array %% 0x14 0
l492 24 my_add 0 l201 put_data_to_array %% 0x18 0
l492 28 my_add 0 l201 put_data_to_array %% 0x1C 0
l492 32 my_add l368 l201 put_data_to_array %% 0x20 Address of Shellcode
l492 36 my_add l368 l201 put_data_to_array %% 0x24 Address of Shellcode——lpAddress
l492 40 my_add l349 l201 put_data_to_array %% 0x28 Size of Shellcode——dwSize
l492 44 my_add 64 l201 put_data_to_array %% 0x2C PAGE_EXECUTE_READWRITE——flNewProtect
l492 48 my_add l493 l201 put_data_to_array %% 0x30 lpflOldProtect
Finally, when executing the closefile instruction, it jumps to the Shellcode:

The EPS exploit script is located in the \word\media directory; it can be seen after extraction. The exploit samples for this vulnerability are basically the same except for the Shellcode part. Therefore, we will analyze a sample from the Patchword organization as an example.
Filename: Cyber_Secure_Pakistan.docx
MD5: DD89BBB916A2C909630EC78CBB0E13E5
Jump to Shellcode, restore stack:

Allocate memory:

Obtain function call addresses:

During debugging, possibly due to environmental issues, the address for the CreateToolhelp32Snapshot function call was not successfully obtained:

Manually fill in the address and open Word to continue analysis. Enumerate processes to find WINWORD.exe:

Create a program named MSBuild.exe in the C:\ProgramData\Microsoft\DeviceSync directory:

Write file content, which is stored in the payload_32 variable of the EPS script:


Create vmtools.dll file:

Write file content, which is stored in the payload_32_f2 variable of the EPS script:


Create VMwareCplLauncher.exe file:

Its content is stored in the payload_32_f1 variable of the EPS script:

This file is a white-file signed by Vmware:

Inject the following content into explorer.exe:

Its function is to create a VMwareCplLauncher.exe process:

The subsequent process is mentioned in Qihoo 360's report. This article does not cover that analysis part for now:

Interested readers can further read that report.
Note: The exploit samples for this vulnerability are basically similar; the difference lies in the final MSBuild.exe payload, which is stored in the payload_32 variable of the EPS script. It can be dumped directly, and after filling in the DOS header, it can be dragged into IDA for analysis.