Introduction

A local privilege escalation vulnerability exists in one of the RPC endpoints exposed by a Symantec Endpoint Protection (SEP) userland service. This can be exploited by unprivileged local users with the ability to execute arbitrary code. An arbitrary file move allows users to move files from one controlled location to another, which can be exploited to obtain arbitrary code execution under the SYSTEM context. This bug was disclosed via ZDI as ZDI-20-228 and CVE-2020-5825, fixed in version 14.2.5569.2100 of SEP. Most systems should have automatically patched by now but users are urged to patch immediately.

This bug was discovered independently by our FusionX R&D team and disclosed to ZDI by z0mb1e in early 2020.

Analysis

At a high level, SEP’s architecture can be described by the following diagram:

All analysis was performed using version 14.2.5323.2000. While there have been some architectural changes since this minor version, they are not substantive enough to change much in the above diagram.

SymCorpUI is the userland GUI used to interface with SEP, inspect policy, start scans, and make changes to the local instance. Nearly all requests are proxied back to ccSvcHst through COM objects for processing. This privileged service, ccSvcHst, is SEP’s local hivemind and handles the execution of all scanning, configuration, isolation, etc.

The ccSvcHst service has a variety of input surfaces, including COM objects (both LocalServer and InprocServer), RPC endpoints, TCP ports, extended service control codes, and others. It additionally interfaces with the approximately 13 different kernel drivers loaded by the application via IO control codes.

Our vulnerability begins with ccSvcHst’s exposed RPC server, which can be connected to by any authenticated local user. It registers six functions, as described by the following IDL:

In order to call these, we first must obtain a context handle. This allows ccSvcHost to track state information in between RPC calls, whether they’re direct to the RPC server, COM, or through some other transport. You can read more about RPC context handles on MSDN here. In our case, Proc0 is the function used to obtain a context handle. We first need to generate and send a unique GUID to the service which will then use it to track our session.

To get a handle, we send an appropriately crafted buffer to the Proc0 function:

lpHandle will now contain a context handle. This must be passed to each RPC function for a call to succeed. We’ll return to these in a moment.

In addition to these function calls, the RPC server registers five different endpoints. These are represented by GUIDs that are dynamically generated each time the service is started. The current endpoints can be extracted from the registry:

Each endpoint has channels registered to it, which can also be extracted from the registry:

The above was truncated for brevity, but there are approximately 37 channels registered across the five RPC endpoints. Connecting to an RPC endpoint can be done via the standard win32 RPCRT4 functions, details of which can be found in the CreateBindingHandle function of the accompanying source code.

A channel registers itself at runtime with ccIPC.dll, which acts as the RPC server loaded by the system service. Each channel then registers a command handler which allows it to serve requests dynamically during invocation. A channel can register as many command handlers as it needs. This architecture is summarized below:

One channel, identified by the string {BFADC982-75E5-497A-A114-95856CCE9A33}, hosted by AVHostPlugin.dll, registers a handler called CRTVScanIPCCommandHandler and handles four different commands: HandleStillInfectedOpState, HandleOpState, KickOffSMRScan, and DoCOM. This final command, DoCOM, is serviced by a sub-handler, ProcessOneRequest, which provides support for approximately 40 additional functions. One of these, COM_ACT_ON_FILE, sub command type 17, supports five sub-sub-commands (!): move file, rename file, remove directory, unlink, and some sort of file encryption/decryption capability. The following is the relevant portion of the sub-sub-command handler:

If we can reach this handler, we can abuse the first case to control a MoveFileExA source and destination, thereby an arbitrary file move as the SYSTEM user.

In order to reach this handler, we need to return to our RPC functions. Once we retrieve our client context handle from Proc0, we need to use Proc6. This is the function that handles parsing requests and handing them off to the appropriate channel; its prototype is as follows:

Briefly, lpInputBuf is the request buffer containing all necessary information for routing the request and lpInputBuf2 is the buffer sent to the command handler for processing (though it is partially processed by ccIPC).

The request buffer takes the following form:

The first string is the channel that we’re requesting, the second is the GUID of the handler registered to the plugin, and the final string is handed off to the channel to execute whatever function the GUID is tied to.

The second buffer consists of a preamble and an embedded payload buffer. This preamble contains the protocol version (always 0x1), the argument type (of the following buffer, 2 in our case), and the payload length. There are some bytes between the preamble and the payload used primarily for routing our request.

The embedded payload contains all information necessary for reaching our destination; this includes the sub command (64), the client PID, and the source and destination filenames each prefixed with their lengths. Both strings are expected to be ANSI. This results in the following buffer:

Here we can see that we’re moving the file C:\Users\user\setup.dll into C:\Windows\Sysnative\setup.dll. Sysnative is a virtual folder that allows us to access the 64-bit System32 folder instead of SysWOW64. Since SEP is a 32-bit application, any paths to System32 will instead map into SysWOW64, which we do not want to do.

Exploitation

Exploiting a file move is straightforward. Prior to Windows 10 1903, James Forshaw’s DiagHub strategy is our preferred strategy. Briefly, the Microsoft Diagnostics Hub Standard Collector Service (DiagHub) is a service that collects trace information and is programmatically exposed via DCOM. This DCOM object can be used to load a DLL into a SYSTEM process, provided that this DLL exists in the C:\Windows\System32 directory. This is perfect for exploiting an arbitrary file move as we can simply invoke this service and safely execute code within a non-essential privileged system process.

Starting with version 1903 and above, DiagHub can no longer be used to load arbitrary DLLs. A newer strategy, UsoLoader, can be used. This is similar to DiagHub but uses the Update Session Orchestrator to load a DLL into a privileged process out of System32. Other options certainly exist here as well; since we fully control the destination of the write, we could DLL hijack into any number of system services, both native to Windows and within SEP itself.

Finally, sometime between versions 14.0 and 14.2, Symantec identified that this plugin was particularly sensitive and included a verification check. Once a request hits the AVHostPlugin, SEP will validate that the request came from a process that is signed by Symantec. Here’s the relevant portion:

Using RpcServerInqCallAttributesA it pulls the process ID of the RPC client sending the request. It then uses another library, ccSvrTrst, to validate the signature of the requesting process. This is easy to bypass, however; there are no checks on the token of the running process, simply that it is signed by Symantec. By spawning a Symantec binary and injecting into it, via some form of code injection or DLL hijacking, we can make all our RPC requests from this hosting process and pass the check.

Our proof of concept is implemented as a standard DLL with the expectation that the user will abuse DLL load paths to obtain code execution in a Symantec signed binary. One example, DevViewer.exe, can be used to load the DLL. By copying the executable to a user-controlled location, our exploit DLL can be dropped in the same path and renamed to TextInputFramework.dll. This will be loaded into the DevViewer process on launch and allow us to send valid requests to the AVHostPlugin interface.

Patch

The file move vulnerability was patched in version 14.2.5569.2100 of SEP. The patch is simple; in AVHostPlugin, they’ve added a new check:

 

If our DO_COM sub-code is less than 125, we call CheckAdminMember, which does the following:

If our sub-code is one of 5 values, it will check if the requesting process token has local administrator group membership. If it does the request is allowed, otherwise it’s rejected. This is an adequate patch for this specific vulnerability as it can only be executed by system admins, but still exposes a variety of sub-code functions in the channel, as well as all other channels registered to the endpoints. Exploring these other channels is left as an exercise to the reader.

Public Code

Along with this post we’re releasing our proof of concept exploit. Due to the finnicky nature of RPC interfaces, you may need to extract your specific version’s IDL GUID and compile it before using this code. Though we’ve included IDLs for several versions, every new minor build has a different interface GUID and it needs to match in the IDL compiled into the payload. Thankfully it’s a pretty easy process. You’ll first need to grab a GUID out of the registry, which is a key:

Our IDL GUID will thus be {D8E0573B-6B4C-4DC0-8F5C-4764B8E079F9}. Check out the included IDLs for where to switch out the GUID.

Additional usage details can be found in the project’s README file.

 

About the FusionX Team

FusionX is Accenture Security’s elite red team focused on executing sophisticated attacks against our clients in order to simulate the actions of an advanced adversary. Our FusionX team is backed up our Advanced Research and Development unit whose mission is to develop custom capabilities and tools used by the FusionX Adversary Team.

Accenture Security

Accenture Security is a leading provider of end-to-end cybersecurity services, including advanced cyber defense, applied cybersecurity solutions and managed security operations. We bring security innovation, coupled with global scale and a worldwide delivery capability through our network of Advanced Technology and Intelligent Operations centers. Helped by our team of highly skilled professionals, we enable clients to innovate safely, build cyber resilience and grow with confidence.  Follow us @AccentureSecure on Twitter or visit us at www.accenture.com/security.

Accenture, the Accenture logo, and other trademarks, service marks, and designs are registered or unregistered trademarks of Accenture and its subsidiaries in the United States and in foreign countries. All trademarks are properties of their respective owners. All materials are intended for the original recipient only. The reproduction and distribution of this material is forbidden without express written permission from Accenture. The opinions, statements, and assessments in this report are solely those of the individual author(s) and do not constitute legal advice, nor do they necessarily reflect the views of Accenture, its subsidiaries, or affiliates. Given the inherent nature of threat intelligence, the content contained in this report is based on information gathered and understood at the time of its creation. It is subject to change. Accenture provides the information on an “as-is” basis without representation or warranty and accepts no liability for any action or failure to act taken in response to the information contained or referenced in this report

Copyright © 2020 Accenture. All rights reserved. Accenture, its logo, and High Performance Delivered are trademarks

 

 

Subscribe to Accenture's Cyber Defense Blog Subscribe to Accenture's Cyber Defense Blog