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.
- 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.
- 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
- 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.
- 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
- The target device is running OpENer and listening on the default ENIP TCP port (44818).
- The attacker establishes a standard TCP connection and registers a session (
RegisterSession).
- The attacker establishes a CIP connection via
ForwardOpen to obtain a valid Connection ID.
- 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:
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.
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
DecodePaddedEPathvia 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_sizethat causesDecodePaddedEPathto 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.
source/src/enet_encap/cpf.c,NotifyConnectedCommonPacketFormatprocesses a connected data item and callsNotifyMessageRouter:SendUnitDatapayload wherelength = 1,1 - 2results in-1.-1is passed down toCreateMessageRouterRequestStructureinsource/src/cip/cipmessagerouter.casdata_length, where it is further decremented:DecodePaddedEPathreturns, there is a check:number_of_decoded_bytesis asize_t(unsigned), the negativedata_length(-2) is implicitly cast to a massive unsigned value (e.g.,4294967294), permanently bypassing the check.path_sizeto the payload.DecodePaddedEPathwill trust this size and continuously read from the stack, overrunning theincoming_messagebuffer.Trigger Conditions
RegisterSession).ForwardOpento obtain a valid Connection ID.SendUnitDatarequest using the obtained Connection ID, deliberately settingConnectedDataItem.length = 1to trigger the integer underflow, and appending a forged largepath_sizeto force an immediate ASan crash.Reproduction (Validated)
1) Build:
Compile the OpENer project on a POSIX environment (e.g., ubuntu 20.04). Enable
OpENer_TRACESand ASan to observe the crash.Edit
bin/posix/setup_posix.shto 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 make2) Run target:
Start the OpENer service, listening on the loopback interface:
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:4) Observe Phenomena:
The OpENer server will immediately crash with a massive ASan
stack-buffer-overflowtrace originating fromDecodePaddedEPath.Server Log Output:
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.