CVE-2022-43605
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.
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
OpENer - https://github.com/EIPStackGroup/OpENer
10.0 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
CWE-787 - Out-of-bounds Write
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.
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)
2022-12-06 - Vendor Disclosure
2022-12-14 - Vendor Patch Release
2023-02-23 - Public Release
Discovered by Jared Rittle of Cisco Talos.