NB: This blog post will be updated in the upcoming days with more information
Introduction
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, Malwr.com, Cryptam.com |
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 greyhathacker.net. 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.
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:
- Padding containing 0x7C342404 dwords functioning as a RET-sled.
- A ROP chain and first stage shellcode after every 0x20000 bytes. (16 repetitions in total)
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.
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 Mona.py. 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 onlinedisassembler.com, 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.
Commented assembly and pseudocode of both shellcodes will be added at a later point in time.
Lure
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……..”
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 | 1.0.0.0 |
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, Malwr.com |
c&c
When the backdoor is being analyzed in a sandbox one can observe DNS request for the “login.loginto.me” domain. Passive DNS indicates that this domain has been pointing to the IP address: 23.249.225.140.
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
Appendix
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") { break; } } } } lpvFileView += 0x10000; // Locate the encrypted 2nd stage shellcode preceded by the fefefefe fefefefe ffffffff tag while (true) { do { lpvFileView += 4; } while (&lpvFileView != 0xFEFEFEFE); do { lpvFileView++; } while ((char*)lpvFileView == 0xFE); if (&lpvFileView == 0xFFFFFFFF) { scAddr = lpvBuffer + 0x1000; memcpy(lpvBuffer, lpvFileView + 4, 0x1000); scAddr(kernel32Base, lpvFileView, hFile, dFileSize, lpvBuffer); } }