Skip to content

Integer Underflow Leading to Stack Buffer Overflow in CIP Connected Message Path #568

@MrAlaskan

Description

@MrAlaskan

Vulnerability Description

OpENer 2.3.0 is vulnerable to a severe memory corruption issue caused by an integer underflow in the processing of connected explicit messages (SendUnitData).

Although a previously reported vulnerability identified an out-of-bounds read in DecodePaddedEPath via Unconnected messages (SendRRData), I have discovered a distinct Integer Underflow vulnerability in the Connected message path. When parsing a connected data item, the length is blindly decremented by 2 (length - 2) without checking if the length is at least 2. An attacker can intentionally provide a length of 1, resulting in an integer underflow that produces a negative length.

This negative length bypasses downstream size validations due to implicit unsigned conversions, allowing an attacker to supply a forged path_size that causes DecodePaddedEPath to read wildly out of bounds, resulting in a Stack Buffer Overflow.

Root Cause

The root cause is an unsafe subtraction operation without bounds checking, which leads to an integer underflow.

  1. Integer Underflow: In source/src/enet_encap/cpf.c, NotifyConnectedCommonPacketFormat processes a connected data item and calls NotifyMessageRouter:
    return_value = NotifyMessageRouter(buffer,
                                       g_common_packet_format_data_item.data_item.length - 2, // <--- Underflow here if length < 2
                                       &message_router_response, ...);
    If the attacker sends a SendUnitData payload where length = 1, 1 - 2 results in -1.
  2. Propagating the Negative Length: The -1 is passed down to CreateMessageRouterRequestStructure in source/src/cip/cipmessagerouter.c as data_length, where it is further decremented:
    data_length--; // -1 becomes -2
  3. Bypassed Bounds Check: After DecodePaddedEPath returns, there is a check:
    if(number_of_decoded_bytes > data_length)
    Since number_of_decoded_bytes is a size_t (unsigned), the negative data_length (-2) is implicitly cast to a massive unsigned value (e.g., 4294967294), permanently bypassing the check.
  4. Triggering OOB Read: Because the size check is nullified, an attacker can append a forged, extremely large path_size to the payload. DecodePaddedEPath will trust this size and continuously read from the stack, overrunning the incoming_message buffer.

Trigger Conditions

  1. The target device is running OpENer and listening on the default ENIP TCP port (44818).
  2. The attacker establishes a standard TCP connection and registers a session (RegisterSession).
  3. The attacker establishes a CIP connection via ForwardOpen to obtain a valid Connection ID.
  4. The attacker sends a SendUnitData request using the obtained Connection ID, deliberately setting ConnectedDataItem.length = 1 to trigger the integer underflow, and appending a forged large path_size to force an immediate ASan crash.

Reproduction (Validated)

1) Build:
Compile the OpENer project on a POSIX environment (e.g., ubuntu 20.04). Enable OpENer_TRACES and ASan to observe the crash.

Edit bin/posix/setup_posix.sh to ensure the following flags are set:

  -DOpENer_TRACES:BOOL=ON \
  -DOpENer_TRACE_LEVEL_ERROR:BOOL=ON \
  -DCMAKE_C_FLAGS:STRING="-fsanitize=address -fno-omit-frame-pointer -DOPENER_TCPIP_IFACE_CFG_SETTABLE=1" \

Then build the project:

cd bin/posix
./setup_posix.sh
make

2) Run target:
Start the OpENer service, listening on the loopback interface:

./src/ports/POSIX/OpENer lo

3) Send attack traffic:

PoC.zip

In a new terminal, execute the provided Python PoC script to establish a connection, send the ForwardOpen, and then fire the underflow payload:

python3 PoC.py

4) Observe Phenomena:
The OpENer server will immediately crash with a massive ASan stack-buffer-overflow trace originating from DecodePaddedEPath.

Server Log Output:

networkhandler: opened new TCP connection on fd 21
/home/user/OpENer/source/src/enet_encap/endianconv.c:101:76: runtime error: left shift of 128 by 24 places cannot be represented in type 'int'
something is wrong with the length in Message Router @ CreateCommonPacketFormatStructure
=================================================================
==75107==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd53493c90 at pc 0x561e52df66ae bp 0x7ffd53492fe0 sp 0x7ffd53492fd0
READ of size 1 at 0x7ffd53493c90 thread T0
    #0 0x561e52df66ad in DecodePaddedEPath /home/user/OpENer/source/src/cip/cipcommon.c:1403
    #1 0x561e52e14bbe in CreateMessageRouterRequestStructure /home/user/OpENer/source/src/cip/cipmessagerouter.c:254
    #2 0x561e52e1417c in NotifyMessageRouter /home/user/OpENer/source/src/cip/cipmessagerouter.c:188
    #3 0x561e52e27142 in NotifyConnectedCommonPacketFormat /home/user/OpENer/source/src/enet_encap/cpf.c:173
    #4 0x561e52e2e555 in HandleReceivedSendUnitDataCommand /home/user/OpENer/source/src/enet_encap/encap.c:526
    #5 0x561e52e2b865 in HandleReceivedExplictTcpData /home/user/OpENer/source/src/enet_encap/encap.c:191
    #6 0x561e52de2be6 in HandleDataOnTcpSocket /home/user/OpENer/source/src/ports/generic_networkhandler.c:864
    #7 0x561e52de0d8a in NetworkHandlerProcessCyclic /home/user/OpENer/source/src/ports/generic_networkhandler.c:497
    #8 0x561e52dde513 in executeEventLoop /home/user/OpENer/source/src/ports/POSIX/main.c:261
    #9 0x561e52dde3bd in main /home/user/OpENer/source/src/ports/POSIX/main.c:229
    #10 0x7febaef02082 in __libc_start_main ../csu/libc-start.c:308
    #11 0x561e52dddbed in _start (/home/user/OpENer/bin/posix/src/ports/POSIX/OpENer+0x89bed)

Address 0x7ffd53493c90 is located in stack of thread T0 at offset 1312 in frame
    #0 0x561e52de227b in HandleDataOnTcpSocket /home/user/OpENer/source/src/ports/generic_networkhandler.c:720

  This frame has 6 object(s):
    [48, 52) 'remaining_bytes' (line 722)
    [64, 68) 'fromlen' (line 852)
    [80, 88) 'read_buffer' (line 760)
    [112, 128) 'sender_address' (line 850)
    [144, 672) 'outgoing_message' (line 862)
    [800, 1312) 'incoming_message' (line 732) <== Memory access at offset 1312 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/user/OpENer/source/src/cip/cipcommon.c:1403 in DecodePaddedEPath
Shadow bytes around the buggy address:
  0x10002a68a740: 00 00 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2
  0x10002a68a750: f2 f2 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10002a68a760: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10002a68a770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10002a68a780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10002a68a790: 00 00[f3]f3 f3 f3 f3 f3 f3 f3 00 00 00 00 00 00
  0x10002a68a7a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10002a68a7b0: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 06 f3 f3 f3
  0x10002a68a7c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10002a68a7d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10002a68a7e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==75107==ABORTING

Impact Assessment

This is a High severity memory corruption vulnerability. Because OpENer processes connected network packets without validating the length before performing a subtraction, an unauthenticated attacker can remotely trigger an integer underflow. This flaw completely bypasses downstream length validations and leads to a stack buffer overflow. While this PoC demonstrates a reliable DoS by crashing the service via an out-of-bounds read, integer underflows leading to broken length checks can potentially be leveraged to corrupt memory in more subtle ways, posing a significant risk to Industrial Control Systems (ICS) relying on this stack.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions