Skip to content

Integer Overflow and Truncation Leading to Stack Buffer Overflow in CIP Message Router #563

@MrAlaskan

Description

@MrAlaskan

Vulnerability Description

OpENer is vulnerable to a severe memory corruption issue caused by integer overflow and signedness truncation. When parsing incoming CIP (Common Industrial Protocol) network packets, the length parameter is inconsistently typed across the call stack. Specifically, an upstream length calculated as an int is passed to a downstream function that expects an EipInt16 (a 16-bit signed integer).

If a maliciously crafted packet with specific length fields is processed, the length parameter can overflow or be truncated into a negative value. This negative length bypasses subsequent bounds checking (due to signed/unsigned comparison issues) and is ultimately used in memory operations, leading to a Stack Buffer Overflow when reading data in DecodePaddedEPath.

Root Cause

The root cause lies in inconsistent integer types and missing bounds checks in source/src/cip/cipmessagerouter.c:

  1. Type Truncation: NotifyMessageRouter(EipUint8 *data, int data_length, ...) receives data_length as a 32-bit int. It then calls CreateMessageRouterRequestStructure(const EipUint8 *data, EipInt16 data_length, ...). If data_length is greater than 32767, it is truncated to a negative EipInt16 value.
  2. Integer Overflow: In endianconv.c, there are unsafe shift operations (e.g., left shift of 128 by 24 places) that cause Undefined Behavior (UBSan trigger) when processing malicious length fields, contributing to the corrupted length value.
  3. Bypassed Bounds Check: Inside CreateMessageRouterRequestStructure(), 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 is implicitly cast to a massive unsigned value, bypassing the check.
  4. Underflow to Massive Size: The remaining data size is calculated as request_data_size = data_length - number_of_decoded_bytes. Since request_data_size is size_t, subtracting from a negative value results in an enormous unsigned size. This corrupted size is passed to DecodePaddedEPath, causing a stack buffer overflow when it reads past the bounds of 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.
  3. The attacker establishes a CIP connection (e.g., via ForwardOpen).
  4. The attacker sends a SendUnitData request where the ConnectedDataItem.length is intentionally set to 1 (which becomes negative after subtracting header lengths) and appends a large amount of forged path words.

Reproduction (Validated)

PoC.zip

1) Build:
Compile the OpENer project on a POSIX environment (e.g., Ubuntu). Enable OpENer_TRACES and ASan/UBSan 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,undefined -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:
In a new terminal, execute the attacker script to establish a connection and send the payload that triggers the integer truncation and overflow:

python3 attacker.py

4) Observe Phenomena:
The OpENer server will immediately crash. The logs will first show a UBSan runtime error regarding an invalid left shift, followed immediately by a massive ASan stack-buffer-overflow trace originating from DecodePaddedEPath.

Server Log Output:

networkhandler: opened new TCP connection on fd 22
/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
=================================================================
==522093==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc44fc2890 at pc 0x55b1462f26ae bp 0x7ffc44fc1be0 sp 0x7ffc44fc1bd0
READ of size 1 at 0x7ffc44fc2890 thread T0
    #0 0x55b1462f26ad in DecodePaddedEPath /home/user/OpENer/source/src/cip/cipcommon.c:1403
    #1 0x55b146310bbe in CreateMessageRouterRequestStructure /home/user/OpENer/source/src/cip/cipmessagerouter.c:254
    #2 0x55b14631017c in NotifyMessageRouter /home/user/OpENer/source/src/cip/cipmessagerouter.c:188
    #3 0x55b146323142 in NotifyConnectedCommonPacketFormat /home/user/OpENer/source/src/enet_encap/cpf.c:173
    #4 0x55b14632a555 in HandleReceivedSendUnitDataCommand /home/user/OpENer/source/src/enet_encap/encap.c:526
    #5 0x55b146327865 in HandleReceivedExplictTcpData /home/user/OpENer/source/src/enet_encap/encap.c:191
    #6 0x55b1462debe6 in HandleDataOnTcpSocket /home/user/OpENer/source/src/ports/generic_networkhandler.c:864
    #7 0x55b1462dcd8a in NetworkHandlerProcessCyclic /home/user/OpENer/source/src/ports/generic_networkhandler.c:497
    #8 0x55b1462da513 in executeEventLoop /home/user/OpENer/source/src/ports/POSIX/main.c:261
    #9 0x55b1462da3bd in main /home/user/OpENer/source/src/ports/POSIX/main.c:229
    #10 0x7f4ba2672082 in __libc_start_main ../csu/libc-start.c:308
    #11 0x55b1462d9bed in _start (/home/user/OpENer/bin/posix/src/ports/POSIX/OpENer+0x89bed)

Address 0x7ffc44fc2890 is located in stack of thread T0 at offset 1312 in frame
    #0 0x55b1462de27b 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:
  0x1000089f04c0: 00 00 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2
  0x1000089f04d0: f2 f2 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000089f04e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000089f04f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000089f0500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1000089f0510: 00 00[f3]f3 f3 f3 f3 f3 f3 f3 00 00 00 00 00 00
  0x1000089f0520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000089f0530: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 06 f3 f3 f3
  0x1000089f0540: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000089f0550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000089f0560: 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
==522093==ABORTING

Impact Assessment

This is a High severity memory corruption vulnerability. Because OpENer processes network packets without proper length validation and type safety, an unauthenticated attacker can remotely trigger a stack buffer overflow. While this PoC demonstrates a reliable DoS by crashing the service, stack buffer overflows can potentially be leveraged for Remote Code Execution (RCE) depending on the target architecture's memory protections (like Stack Canaries and ASLR). Given its use in Industrial Control Systems, a crash leads to an immediate loss of communication and control capability.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions