Talos Vulnerability Report

TALOS-2016-0229

McAfee ePolicy Orchestrator DataChannel Blind SQL Injection Vulnerability

February 1, 2017
CVE Number

CVE-2016-8027

Summary

An exploitable blind sql injection vulnerability exists within McAfee’s ePolicy Orchestrator 5.3.0 that is accessible without authentication. A specially crafted HTTP post can allow an aggressor to alter a sql query which can result in disclosure of information within the database or impersonation of an agent. An attacker can use any HTTP client to trigger this vulnerability.

Tested Versions

McAfee’s ePolicy Orchestrator 5.3.0

Product URLs

http://www.mcafee.com/us/products/epolicy-orchestrator.aspx(http://www.mcafee.com/us/products/epolicy-orchestrator.aspx)

CVSSv3 Score

8.2 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:L

Details

McAfee’s ePolicy Orchestrator is a centralized security management suite that is used to manage antivirus and policies within the enterprise. These types of software are also known as an HBSS or Host-based Security System. It is used to manage a number of agents which are deployed in the enterprise and can be used to enforce defenses and software policies in a scalable manner.

McAfee’s ePolicy Orchestrator is composed of a number of components. This consists of a load-balancer which is driven by Apache and extended via custom modules. The Console itself which is based on Tomcat, and is used by staff to manage the entirety of the software. The last component are the agents themselves which are deployed on each client machine. The agents communicate over a proprietary protocol known as SPIPE to Apache which is then forwarded to the Console. This specific vulnerability is located within the application server and is reachable over SPIPE or via the Console directly.

Within the Tomcat server component, which may be reachable on port 8443 in most configurations, are a number of servlets which serve as the management component of the application. The DataChannel servlet is actually mapped to a POST request received against “https:///receiveDataChannelMsg.dcp" and is implement by the class "com.mcafee.epo.dataChannel.servlet.EPODataChannelServlet". This resource is normally not reachable without authentication, however there is another servlet that is responsible for forwarding requests to this servlet for an agent. This servlet is the "com.mcafee.epo.dataChannel.servlet.redirect.EPODataChannelRedirectServlet" class and is mapped to "https:///dcRedirect/dataChannelMsg.dc". This resource will receive a POST request without authentication and then forward it to the "/receiveDataChannelMsg.dcp" for processing without needing authentication.

Once the POST request is received by the EPODataChannelServlet class, the following code will be executed. When calling the ProcessIncomingMessage function, this will eventually call the EPOBaseDataChannelData.unpackLittleEndian_DataPacket method. This method will decode a structure from the POST’s content which contains the message type, it’s origin, and most importantly the Agent’s GUID as well as some other fields. Afterwards this object will then get passed to the EPODataChannelMessagingService.receiveAgentMessage method.

com.mcafee.epo.dataChannel.servlet.EPODataChannelServlet:49
  protected void doPost(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse)
    throws ServletException, IOException
  {
...
    try
    {
      BeanFactory localBeanFactory = (BeanFactory)getServletContext().getAttribute("beanFactory");
      EPODataChannelReceiveAction localEPODataChannelReceiveAction = (EPODataChannelReceiveAction)localBeanFactory.getBean("epo.dataChannel.action");
      localEPODataChannelReceiveAction.ProcessIncomingMessage(paramHttpServletRequest, paramHttpServletResponse);   // \
    }
\
    byte[] arrayOfByte = readBytesFromRequest(paramHttpServletRequest); // XXX: reads bytes from POST request
    try
    {
      EPOBaseDataChannelData localEPOBaseDataChannelData = EPOBaseDataChannelData.unpackLittleEndian_DataPacket(arrayOfByte);       // XXX: Decodes bytes into an object
      boolean bool = this.m_service.receiveAgentMessage(localEPOBaseDataChannelData);       // XXX
      if (!bool) {
        m_log.error("could not process incoming message");
      }
    }

The EPODataChannelMessagingService.receiveAgentMesage method will then forward the object containing the unpacked data to the DataChannelMessagingServiceInternal.receiveAgentMessage method. This method will check to see that the Agent GUID in the packet is defined, and then call the EPOComputerService.getByAgentGUIDNoUserCheck method using the AgentGUID as its only argument.

com.mcafee.epo.dataChannel.service.DataChannelMessageServiceInternal:325
  public boolean receiveAgentMessage(EPOBaseDataChannelData paramEPOBaseDataChannelData)
    throws EPODataChannelException
  {
    if (paramEPOBaseDataChannelData == null) {
      throw new EPODataChannelException("Empty message received");
    }
    String str = paramEPOBaseDataChannelData.getMessageType();
    if ((str == null) || (str.length() < 1)) {
      throw new EPODataChannelException("Invalid length of message type received");
    }
...
        localConnection = getDatabase().getConnection(localOrionUser);
        localOrionUser = getUserForMessage(localConnection, paramEPOBaseDataChannelData);   // \
\
  private OrionUser getUserForMessage(Connection paramConnection, EPOBaseDataChannelData paramEPOBaseDataChannelData)
  {
    OrionUser localOrionUser = getUserLoader().getDefaultTenantSystemUser();
    try
    {
      String str = paramEPOBaseDataChannelData.getAgentGUID();      // XXX: from POST request
      if ((str != null) && (str.length() > 0))
      {
        EPOComputer localEPOComputer = getComputerService().getByAgentGUIDNoUserCheck(paramConnection, str);    // XXX

In the EPOComputerService class, the getByAgentGUIDNoUserCheck method is contains the sql injection vulnerability. This method will simply take the provided Agent GUID and use it to build a query using the EPOComputerDataMapper class which will then be executed over the jdbc connection.

com.mcafee.epo.core.services.EPOComputerServiceInternal:159
 public EPOComputer getByAgentGUIDNoUserCheck(Connection paramConnection, String paramString)
    throws SQLException
  {
    if ("N/A".equals(paramString)) {
      return null;
    }
    String str = getComputerMapper().getSelectSql(false) + "where (AgentGUID = '" + paramString + "')";     // XXX
    List localList = getComputerMapper().getByQuery(paramConnection, str);
    if ((localList == null) || (localList.isEmpty())) {
      return null;
    }
    EPOComputer localEPOComputer = (EPOComputer)localList.get(0);
    loadLdapPropsForComputer(paramConnection, localEPOComputer);

    return localEPOComputer;
  }

This results in the EPOComputerDatabaseMapper class building a query that looks like the following.

select AutoID, Type, NodeName, ParentID, AgentPlatform, ResortEnabled, AgentGUID, SequenceErrorCount, SequenceErrorCountLastUpdate, SuperAgent, TenantId from [EPOLeafNode] where (AgentGUID = '%s')

Protocol Implementation

In some configurations, the console may not be directly reachable by an agent. The method by which an agent communicates to the console, however, is through the Apache based load balancer using a proprietary protocol known as SPIPE. This can be reached via the uri “https:///spipe". This request is implemented by an Apache extension module named mod_epo.dll which can perform a number of operations. To perform these operations, the module initially registers a couple handlers, one of which is responsible for forwarding the request to the library responsible for implementing the SPIPE protocol. When a request is made to the "/spipe" resource, the following code will be executed. Afterwards, the VERB will be checked if it's an HTTP POST which will then cause the module to initialize the EPO Handler if it hasn't been initialized already and then call a function responsible for handling the POST request.

.text:54609BBC                       loc_54609BBC:                           ; CODE XREF: epo_handler_9a60+D0j
.text:54609BBC 0B0 6A 06                             push    6               ; size_t
.text:54609BBE 0B4 68 0C DA 60 54                    push    offset str./spipe ; "/spipe"
.text:54609BC3 0B8 8B 86 E0 00 00 00                 mov     eax, [esi+0E0h]
.text:54609BC9 0B8 50                                push    eax             ; char *
.text:54609BCA 0BC FF 15 E8 C1 60 54                 call    ds:strncmp      ; [library] MSVCR100.dll
.text:54609BD0 0BC 83 C4 0C                          add     esp, 0Ch
...
.text:54609C1D                       loc_54609C1D:                           ; CODE XREF: epo_handler_9a60+17Aj
.text:54609C1D 0B0 8B 46 4C                          mov     eax, [esi+4Ch]
.text:54609C20 0B0 83 F8 02                          cmp     eax, 2
.text:54609C23 0B0 74 72                             jz      short loc_54609C97
...
.text:54609C97                       loc_54609C97:                           ; CODE XREF: epo_handler_9a60+1C3j
.text:54609C97 0B0 E8 E4 05 00 00                    call    epoStart_a280
.text:54609C9C 0B0 8B D8                             mov     ebx, eax
...
.text:54609F02 0B0 8D 8D 78 FF FF FF                 lea     ecx, [ebp+lv_apacheObject?_88] ; [object] gvtbl_f25c
.text:54609F08 0B0 E8 23 06 00 00                    call    mod_epo_ProcessPostRequest_a530 ; [completed] 0x0
.text:54609F0D 0B0 89 45 A8                          mov     [ebp+lv_result_58], eax

This function is simply responsible for checking that the Content-Length of the POST request is larger than 0xea bytes and will then proceed to call into the NAIMSERV.DLL library which contains the majority of the SPIPE implementation. This size represents the header of the SPIPE protocol.

.text:5460A74C                       loc_5460A74C:                           ; CODE XREF: mod_epo_ProcessPostRequest_a530+1D5j
.text:5460A74C 0C0 B9 EA 00 00 00                    mov     ecx, 0EAh
.text:5460A751 0C0 3B CB                             cmp     ecx, ebx
.text:5460A753 0C0 1B D2                             sbb     edx, edx
.text:5460A755 0C0 F7 DA                             neg     edx
.text:5460A757 0C0 89 55 8C                          mov     [ebp+var_74], edx
.text:5460A75A 0C0 75 47                             jnz     short loc_5460A7A3
...
.text:5460A7A3 0C0 A1 84 26 61 54                    mov     eax, gpf_naimserv(?)_12684 ; [external] naimserv.dll!ProcessPostRequest_fe40
.text:5460A7A8 0C0 85 C0                             test    eax, eax
.text:5460A7AA 0C0 74 10                             jz      short error(noProcessPostRequestEntryPoint)_a7bc
.text:5460A7AC
.text:5460A7AC 0C0 56                                push    esi
.text:5460A7AD 0C4 53                                push    ebx
.text:5460A7AE 0C8 57                                push    edi
.text:5460A7AF 0CC FF D0                             call    eax ; gpf_naimserv(?)_12684
.text:5460A7B1 0C0 89 45 94                          mov     [ebp+lv_httpStatus_6c], eax

NAIMSERV.DLL registers a couple of callbacks, one of which is responsible for processing the POST request. Eventually after some checks, the module will encounter the following code which will xor all the bytes in the POST’s content with the byte 0xAA. Immediately after decoding, the application will process the header and store it into an object which is used to determine the version of the SPIPE package as well as store extra data that’s interpreted as a list of key/value pairs. The version of the SPIPE package can be one of the following values: 0x40000001, 0x50000001, or 0x60000001.

.text:54C0FEE5 1110 8D 95 DC EF FF FF                lea     edx, [ebp+lv_supports2048bitRsa_1024]
.text:54C0FEEB 1110 52                               push    edx
.text:54C0FEEC 1114 8B 45 0C                         mov     eax, [ebp+av_contentLength_4]
.text:54C0FEEF 1114 50                               push    eax
.text:54C0FEF0 1118 E8 AB 0D 05 00                   call    sub_54C60CA0    ; [synopsis] xor's every byte in content with 0xaa
.text:54C0FEF5 1118 83 C4 08                         add     esp, 8
...
.text:54C0FF97 1110 8D 8D D0 FD FF FF                lea     ecx, [ebp+lvo_string_230]
.text:54C0FF9D 1110 51                               push    ecx
.text:54C0FF9E 1114 8B 55 0C                         mov     edx, [ebp+av_contentLength_4]
.text:54C0FFA1 1114 52                               push    edx
.text:54C0FFA2 1118 8D 95 B4 FD FF FF                lea     edx, [ebp+lvo_string_24c]
.text:54C0FFA8 1118 8B B5 C8 EF FF FF                mov     esi, [ebp+lp_content_1038]
.text:54C0FFAE 1118 8B CE                            mov     ecx, esi
.text:54C0FFB0 1118 E8 DB 8A 01 00                   call    sub_54C28A90    ; [input] %ecx=packetData?,%edx=v_string
.text:54C0FFB0                                                               ; [synopsis] checks the packet header for the minimum Spipe version and a few other things.
.text:54C0FFB0                                                               ; [output] %eax=v_headerResult

If a version 4 packet is used, then the application will use the DES3 algorithm to decrypt the extra data. Version 6, however relies on using SSL. Within this same function, the library will extract the SupportedSPIPEVersion string which could be “4.0”, “5.0”, or “6.0”.

.text:54C1024E 1110 8B 45 0C                         mov     eax, [ebp+av_contentLength_4]
.text:54C10251 1110 50                               push    eax
.text:54C10252 1114 8B D6                            mov     edx, esi
.text:54C10254 1114 8D 85 D0 F6 FF FF                lea     eax, [ebp+lvo_spipeExtraDataObject_930]
.text:54C1025A 1114 E8 C1 70 00 00                   call    sub_54C17320    ; [note] stored to 271(%eax) and 0x400 bytes in size
.text:54C1025A                                                               ; [input] %edx=postData,%eax=someObject
.text:54C1025A                                                               ; [synopsis] extracts from an epo packet the string for "SupportedSPIPEVersion"
...
.text:54C102D8 1110 8B 45 0C                         mov     eax, [ebp+av_contentLength_4]
.text:54C102DB 1110 50                               push    eax
.text:54C102DC 1114 8B D6                            mov     edx, esi
.text:54C102DE 1114 8D 85 D0 F6 FF FF                lea     eax, [ebp+lvo_spipeExtraDataObject_930]
.text:54C102E4 1114 E8 C7 6D 00 00                   call    sub_54C170B0    ; [note] stored to 1f0(%eax) and 0x81 bytes in size
.text:54C102E4                                                               ; [synopsis] extracts from an epo packet the string for "ServerKeyHash".
.text:54C102E9 1110 89 85 D4 EF FF FF                mov     [ebp+lp_extraPacketRequest_102c], eax
...
.text:54C10445 1110 8B 45 0C                         mov     eax, [ebp+av_contentLength_4]
.text:54C10448 1110 50                               push    eax
.text:54C10449 1114 8B D6                            mov     edx, esi
.text:54C1044B 1114 8D 85 D0 F6 FF FF                lea     eax, [ebp+lvo_spipeExtraDataObject_930]
.text:54C10451 1114 E8 2A 6C 00 00                   call    sub_54C17080    ; [note] stored to 671(%eax) and 0x40 bytes in size
.text:54C10451                                                               ; [synopsis] extracts from an epo packet the string for "TenantId"

Afterwards, the module will then use these values to locate the Agent’s public key. This public key will be used to verify that the SPIPE package has not been tampered with. Once this is done, then the application will check the Server’s key against the value of the “ServerKeyHash” field provided in the packet. A few more checks later and the module will finally encounter the function responsible for actually handling the Agent’s request.

.text:54C10683 1110 8B BD C8 EF FF FF                mov     edi, [ebp+lp_content_1038]
.text:54C10689 1110 8D 4F 16                         lea     ecx, [edi+16h]  ; lpString
.text:54C1068C 1110 89 8D D0 EF FF FF                mov     [ebp+lp_tenantIdString?_1030], ecx
.text:54C10692 1110 8D 95 A0 EF FF FF                lea     edx, [ebp+lv_databaseId_1060]
.text:54C10698 1110 52                               push    edx             ; int
.text:54C10699 1114 8D 85 A4 EF FF FF                lea     eax, [ebp+lv_105C]
.text:54C1069F 1114 50                               push    eax             ; int
.text:54C106A0 1118 8D 95 9C EF FF FF                lea     edx, [ebp+lv_size_1064]
.text:54C106A6 1118 52                               push    edx             ; int
.text:54C106A7 111C 8D 85 EC FD FF FF                lea     eax, [ebp+lv_buffer(512)?_214]
.text:54C106AD 111C 50                               push    eax             ; int
.text:54C106AE 1120 E8 3D 10 01 00                   call    GetAgentPublicKey_216f0
...
.text:54C10754 1110 8B 95 C4 EF FF FF                mov     edx, [ebp+var_103C]
.text:54C1075A 1110 52                               push    edx
.text:54C1075B 1114 8B 45 0C                         mov     eax, [ebp+av_contentLength_4]
.text:54C1075E 1114 50                               push    eax
.text:54C1075F 1118 8D 95 CC EF FF FF                lea     edx, [ebp+var_1034]
.text:54C10765 1118 8B CF                            mov     ecx, edi
.text:54C10767 1118 E8 34 06 05 00                   call    VerifySignedKeyPkgSignature_60da0
.text:54C1076C 1118 83 C4 08                         add     esp, 8
.text:54C1076F 1110 89 85 E8 EF FF FF                mov     [ebp+lv_flag?_1018], eax
...
.text:54C107E3 1110 8B 55 0C                         mov     edx, [ebp+av_contentLength_4]
.text:54C107E6 1110 52                               push    edx
.text:54C107E7 1114 8B D7                            mov     edx, edi
.text:54C107E9 1114 8D 8D D0 F6 FF FF                lea     ecx, [ebp+lvo_spipeExtraDataObject_930]
.text:54C107EF 1114 E8 7C 5F 00 00                   call    CEPODataPackage::Buffer2Data_16770
...
.text:54C10CE3 1110 8B 8D CC EF FF FF                mov     ecx, [ebp+var_1034]
.text:54C10CE9 1110 51                               push    ecx
.text:54C10CEA 1114 8B 95 A4 EF FF FF                mov     edx, [ebp+lv_105C]
.text:54C10CF0 1114 52                               push    edx
.text:54C10CF1 1118 8D 95 EC EF FF FF                lea     edx, [ebp+lv_someSPIPEObject_1014]
.text:54C10CF7 1118 8D 8D D0 F6 FF FF                lea     ecx, [ebp+lvo_spipeExtraDataObject_930]
.text:54C10CFD 1118 E8 0E 0F 01 00                   call    AgentRequestHandler_21c10 ; [input] %ecx=spipeExtraObject

Inside the AgentRequestHandler function, the application will then check the PackageType field from the packet in order to determine how to handle an Agent’s request. When encountering the following function, the library will populate the object at @ebp-0x17d8 with values from the packet. One of these is the Agent’s GUID.

.text:54C22107 187C 8B C1                            mov     eax, ecx
.text:54C22109 187C 8B 95 14 E8 FF FF                mov     edx, [ebp+var_17EC]
.text:54C2210F 187C 52                               push    edx
.text:54C22110 1880 50                               push    eax
.text:54C22111 1884 8D B5 28 E8 FF FF                lea     esi, [ebp+lv_agentInfoObject_17D8]
.text:54C22117 1884 8B CF                            mov     ecx, edi
.text:54C22119 1884 E8 32 E5 02 00                   call    server_GetAgentInfoFromSPIPE_50650 ; [input] %ecx=p_someObject,%esi=p_giantDestinationObject
.text:54C22119                                                               ; [synopsis] fetches a bunch of attributes from %ecx and writes them to the object at %esi
.text:54C2211E 1884 83 C4 08                         add     esp, 8
.text:54C22121 187C 8B F0                            mov     esi, eax
.text:54C22123 187C 89 B5 1C E8 FF FF                mov     [ebp+lp_agentInfo_17E4], esi ; [alias] v_flag?

After extracting the GUID from the package, the library will then execute the following code which ensures that the GUID isn’t contained in a blacklist. After this is done, the library will check the sequence number and then enter a series of loops that will check which “PackageType” is being requested. If the package type specified by the agent is “MsgUpload”, then the application will call a function, server_OnMsgUpload, which will forward the data to the application server.

.text:54C2222D 187C C6 45 FC 02                      mov     byte ptr [ebp+var_4], 2
.text:54C22231 187C 8D 85 50 FF FF FF                lea     eax, [ebp+lp_guidString?_B0]
.text:54C22237 187C 50                               push    eax
.text:54C22238 1880 B8 DC D2 E1 54                   mov     eax, offset dword_54E1D2DC
.text:54C2223D 1880 E8 BE AA 0F 00                   call    sub_54D1CD00
.text:54C22242 187C 8A D8                            mov     bl, al
...
.text:54C22334 187C 8D 9D 28 E8 FF FF                lea     ebx, [ebp+lv_agentInfoObject_17D8]
.text:54C2233A 187C E8 81 2A 02 00                   call    servdal_ProcessAgentReportedSequenceNumber_44dc0 ; [input] %ebx=p_agentInfoObject
.text:54C2233A                                                               ; [synopsis] validates the sequence number for an agentInfoObject and logs it
.text:54C2233F 187C 89 85 24 E8 FF FF                mov     [ebp+var_17DC], eax
...
.text:54C2278F                       compare(MsgUpload)_2278f:               ; CODE XREF: AgentRequestHandler_21c10+B62j
.text:54C2278F 187C B9 F8 55 DB 54                   mov     ecx, offset str.MsgUpload ; "MsgUpload"
.text:54C22794 187C 8D 85 6C FF FF FF                lea     eax, [ebp+lv_packageTypeString?_94]
.text:54C2279A 187C 8D 9B 00 00 00 00                lea     ebx, [ebx+0]
...
.text:54C227CF 187C 8D 95 28 E8 FF FF                lea     edx, [ebp+lv_agentInfoObject_17D8] ; [XXX] calls server_onMsgUpload
.text:54C227D5 187C 52                               push    edx
.text:54C227D6 1880 8B D3                            mov     edx, ebx
.text:54C227D8 1880 8B 8D 20 E8 FF FF                mov     ecx, [ebp+lv_spipeExtraObject_17E0]
.text:54C227DE 1880 E8 FD 01 03 00                   call    server_OnMsgUpload_529e0 ; [input] %ecx=packetData?

This function will tokenize data provided by the agent, and then enter the following loop which forwards the data directly to the application server using the ForwardDataChannelMessageToJava function.

.text:54C52ACE                       loop_tokens_52ace:                      ; CODE XREF: server_OnMsgUpload_529e0+3BAj
.text:54C52ACE 128 8B 44 24 28                       mov     eax, [esp+124h+lp_@ecx_fc]
.text:54C52AD2 128 50                                push    eax             ; int
.text:54C52AD3 12C 8D 7C 24 14                       lea     edi, [esp+128h+lv_size_114]
.text:54C52AD7 12C 33 D2                             xor     edx, edx
.text:54C52AD9 12C 8B CE                             mov     ecx, esi        ; void *
.text:54C52ADB 12C 89 5C 24 14                       mov     [esp+128h+lv_size_114], ebx
.text:54C52ADF 12C E8 3C 7D 00 00                    call    sub_54C5A820    ; [input] %edx=p_destBuffer
...
.text:54C52C99 128 56                                push    esi
.text:54C52C9A 12C 8B 74 24 1C                       mov     esi, [esp+128h+lp_dataChannelContent_10c]
.text:54C52C9E 12C E8 5D EB FF FF                    call    ForwardDataChannelMessageToJava_51800 ; [input] %esi=content
.text:54C52CA3 12C 83 C4 04                          add     esp, 4
...
.text:54C52D82                       continue_tokens_52d82:                  ; CODE XREF: server_OnMsgUpload_529e0+36Cj
.text:54C52D82 128 8D 44 24 2C                       lea     eax, [esp+124h+lv_Context_f8]
.text:54C52D86 128 50                                push    eax             ; Context
.text:54C52D87 12C 68 C4 9F DB 54                    push    offset str.,    ; ","
.text:54C52D8C 130 53                                push    ebx             ; Str
.text:54C52D8D 134 FF 15 3C 45 DA 54                 call    ds:strtok_s
.text:54C52D93 134 8B F0                             mov     esi, eax
.text:54C52D95 134 83 C4 0C                          add     esp, 0Ch
.text:54C52D98 128 3B F3                             cmp     esi, ebx
.text:54C52D9A 128 0F 85 2E FD FF FF                 jnz     loop_tokens_52ace

The ForwardDataChannelMessageToJava function will simply take the input that was extracted from the Agent’s request, and then forward it to the “/dcRedirect/dataChannelMsg.dc” resource that’s located directly on the application server component.

.text:54C51847 0F0 8D 8D 34 FF FF FF                 lea     ecx, [ebp+lvo_secureHttp_cc]
.text:54C5184D 0F0 FF 15 60 4B DA 54                 call    ds:CSecureHttp::CSecureHttp(void)
.text:54C51853
.text:54C51853 0F0 89 7D FC                          mov     [ebp+var_4], edi
.text:54C51856 0F0 8B 4D 08                          mov     ecx, [ebp+av_size_14]
.text:54C51859 0F0 8D 85 30 FF FF FF                 lea     eax, [ebp+ap_resultHeaders_18]
.text:54C5185F 0F0 50                                push    eax             ; ap_resultHeaders_18
.text:54C51860 0F4 A1 78 CB E1 54                    mov     eax, gp_dataChannelObject_21cb78
.text:54C51865 0F4 51                                push    ecx             ; av_optionalDataLength_14
.text:54C51866 0F8 56                                push    esi             ; ap_optionalData_10
.text:54C51867 0FC 68 64 9E DB 54                    push    offset gv_request(mimetype)_1b9e64 ; "application/octet-stream"
.text:54C5186C 100 89 BD 30 FF FF FF                 mov     [ebp+ap_resultHeaders_18], edi
.text:54C51872 100 8B 90 5C 02 00 00                 mov     edx, [eax+25Ch]
.text:54C51878 100 52                                push    edx             ; av_nServerPort_8
.text:54C51879 104 05 70 02 00 00                    add     eax, 270h
.text:54C5187E 104 50                                push    eax             ; ap_serverName_4
.text:54C5187F 108 68 98 9E DB 54                    push    offset gv_request(dataChannelMsg)_1b9e98 ; "/dcRedirect/dataChannelMsg.dc"
.text:54C51884 10C 8D 8D 34 FF FF FF                 lea     ecx, [ebp+lvo_secureHttp_cc] ; this
.text:54C5188A 10C FF 15 68 4B DA 54                 call    ds:CSecureHttp::PostSecureHttpRequest(wchar_t const *,wchar_t const *,int,wchar_t const *,void const *,uint,ulong &)

Protocol

The POST request that is made to the application server is made to the DataChannel servlet. The servlet implement’s the Data Channel which has the following format.

[0] <class pint.uint16_t> getHeaderSize ???
[2] <class pint.uint16_t> version ???
[4] <class pint.uint32_t> m_expiration ???
[8] <class pint.uint32_t> CorrelationID ???
[c] <class pint.uint8_t> getFlag ???
[0] <class StringPacket> m_originNameBytes ???
[0] <class StringPacket> m_messageTypeBytes ???
[0] <class StringPacket> m_agentGUIDBytes ???
[0] <class Data> m_data ???

A StringPacket structure is a byte which defines the number of bytes that define a string.

<class StringPacket>
[0] <class pint.uint8_t> size ???
[1] <class dyn.block(size)> data ???

A Data structure is similar to a StringPacket except it uses a uint32_t to define the length of the data.

<class Data>
[0] <class pint.uint32_t> size ???
[4] <class dyn.block(size)> data ???

In the proof-of-concept, the following packet is POSTed. The only requirements of this packet are that the version is set to 1, the m_messageTypeBytes is larger than one character in length, and the m_data field contains at least one byte of data. If all of these fields are valid, then the m_agentGUIDBytes can contain up to 256 characters used for the sql injection.

<class DataPacket>
[0] <instance pint.uint16_t 'getHeaderSize'> 0x004e (78)
[2] <instance pint.uint16_t 'version'> 0x0001 (1)
[4] <instance pint.uint32_t 'm_expiration'> 0x00000000 (0)
[8] <instance pint.uint32_t 'CorrelationID'> 0x00000000 (0)
[c] <instance pint.uint8_t 'getFlag'> 0x07 (7)
[d] <instance StringPacket 'm_originNameBytes'> "\x10\x58\x58\x58\x4f\x52\x49\x47\x49\x4e\x4e\x41\x4d\x45\x58\x58\x58"
[1e] <instance StringPacket 'm_messageTypeBytes'> "\x11\x58\x58\x58\x4d\x45\x53\x53\x41\x47\x45\x54\x59\x50\x45\x58\x58\x58"
[30] <instance StringPacket 'm_agentGUIDBytes'> "\x0f\x58\x58\x58\x41\x47\x45\x4e\x54\x47\x55\x49\x44\x58\x58\x58"
[40] <instance Data 'm_data'> "\x0a\x00\x00\x00\x58\x58\x58\x44\x41\x54\x41\x58\x58\x58"

Mitigation

To ensure that an attacker does not have direct access to the vulnerability and instead has to use just SPIPE as an agent, verify that port 8443 that the McAfee ePolicy Orchestrator Console is bound to is inaccessible by ePolicy Orchestrator’s agents and can only by accessed by Administrators.

Timeline

2016-11-04 - Vendor Disclosure
2017-02-01 - Public Release

Credit

Discovered by a member of the Talos Vulnerability Development Team.