Talos Vulnerability Report

TALOS-2022-1662

EIP Stack Group OpENer SetAttributeList attribute_count_request out-of-bounds write vulnerability

February 23, 2023
CVE Number

CVE-2022-43605

SUMMARY

An out-of-bounds write vulnerability exists in the SetAttributeList attribute_count_request functionality of EIP Stack Group OpENer development commit 58ee13c. A specially crafted EtherNet/IP request can lead to an out of bounds write, potentially causing the server to crash or allow for remote code execution. An attacker can send a series of EtherNet/IP requests to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

EIP Stack Group OpENer development commit 58ee13c

PRODUCT URLS

OpENer - https://github.com/EIPStackGroup/OpENer

CVSSv3 SCORE

10.0 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

CWE

CWE-787 - Out-of-bounds Write

DETAILS

OpENer is an EtherNet/IP stack for I/O adapter devices. It supports multiple I/O and explicit connections and includes objects and services for making EtherNet/IP-compliant products as defined in the ODVA specification.

When a SetAttributeList request is received, the number of attributes is extracted into attribute_count_request. This value is then used as the upper bound for a loop intended to iterate over each requested attribute and add the associated information to the CipMessageRouterResponse. This can be seen in the following snippet from cipcommon.c:SetAttributeList.

...

  CipUint attribute_count_request = GetUintFromMessage(
    &message_router_request->data);

  if(0 != attribute_count_request) {

    EipUint16 attribute_number = 0;
    CipAttributeStruct *attribute = NULL;

    AddIntToMessage(attribute_count_request, &message_router_response->message); // number of attributes in the response

    for(size_t j = 0; j < attribute_count_request; j++) {

      attribute_number = GetUintFromMessage(&message_router_request->data);
      attribute = GetCipAttribute(instance, attribute_number);

      AddIntToMessage(attribute_number, &message_router_response->message); // Attribute-ID
      
...

Within this loop the various fields associated with the attribute currently being processed are added to the response message buffer through use of the AddIntToMessage and AddSintToMessage functions. Both of these functions are essentially helpers to convert the value in question from the host endianess to little-endian, and then write the converted data to the response buffer.

The AddIntToMessage and AddSintToMessage functions use the current_message_position and used_message_length values contained within the EnipMessage to determine where within the response message buffer to write the converted data, as shown in the snippet below from endianconv.c:

/**
 * @brief converts UINT16 data from host to little endian an writes it to buffer.
 * @param data value to be written
 * @param buffer pointer where data should be written.
 */
void AddIntToMessage(const EipUint16 data,
                     ENIPMessage *const outgoing_message) {

  outgoing_message->current_message_position[0] = (unsigned char) data;
  outgoing_message->current_message_position[1] = (unsigned char) (data >> 8);
  outgoing_message->current_message_position += 2;
  outgoing_message->used_message_length += 2;
}

When a SetAttributeList request containing an attribute_count_request of size greater than 0x80 is received, the outgoing_message->current_message_position value gets incremented to an address outside of the EnipMessage buffer. Subsequent calls to AddIntToMessage then write user-supplied data outside of the EnipMessage buffer. When specially crafted, this can be used to corrupt the stack and cause the process to crash, resulting in loss of communications with the server and, potentially, code execution.

Crash Information

Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".                                                                                                           
                                                                                                                                                                                      
Breakpoint 1, 0x0000aaaaaaaab820 in HandleDataOnTcpSocket ()                                                                                                                          
(gdb) i r                                                                                                                                                                             
x0             0xfffff7ffd6b0      281474842482352                                                                                                                                    
x1             0x0                 0                                                                                                                                                  
x2             0x55e84b9de151c14   386892551830182932                                                                                                                                 
x3             0x0                 0                                                                                                                                                  
x4             0x0                 0                                                                                                                                                  
x5             0x0                 0                                                                                                                                                  
x6             0x0                 0
x7             0x0                 0
x8             0xce                206
x9             0x14414100144141    5701246964220225
x10            0x14414100144141    5701246964220225
x11            0x14414100144141    5701246964220225
x12            0x41410014414100ff  4702039572945633535
x13            0x4141001441410014  4702039572945633300
x14            0x14414100144141    5701246964220225
x15            0x14414100144141    5701246964220225
x16            0xaaaaaaad7d50      187649984658768
x17            0xfffff7f97a28      281474842065448
x18            0x0                 0
x19            0xaaaaaaac0150      187649984561488
x20            0x0                 0
x21            0xaaaaaaaa9e20      187649984470560
x22            0x0                 0
x23            0x0                 0
x24            0x0                 0
x25            0x0                 0
x26            0x0                 0
x27            0x0                 0
x28            0x0                 0
x29            0xffffffffe900      281474976704768
x30            0xaaaaaaaab7f4      187649984477172
sp             0xffffffffe900      0xffffffffe900
pc             0xaaaaaaaab820      0xaaaaaaaab820 <HandleDataOnTcpSocket+1044>
cpsr           0x20001000          [ EL=0 SSBS C ]
fpsr           0x0                 0
fpcr           0x0                 0
pauth_dmask    0x7f000000000000    35747322042253312
pauth_cmask    0x7f000000000000    35747322042253312
(gdb)
(gdb)
(gdb) x/i $pc
=> 0xaaaaaaaab820 <HandleDataOnTcpSocket+1044>:
    bl  0xaaaaaaaa9c60 <__stack_chk_fail@plt>
(gdb)
(gdb)
(gdb) bt
#0  0x0000aaaaaaaab820 in HandleDataOnTcpSocket ()
#1  0x0000aaaaaaaaae6c in NetworkHandlerProcessCyclic ()
#2  0x4141001441410014 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb)
(gdb)
(gdb) c
Continuing.
*** stack smashing detected ***: terminated

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) 
TIMELINE

2022-12-06 - Vendor Disclosure
2022-12-14 - Vendor Patch Release
2023-02-23 - Public Release

Credit

Discovered by Jared Rittle of Cisco Talos.