Vulnerability Description
OpENer 2.3.0 (commit 76b95cf) has an out-of-bounds read issue in CIP message parsing when handling malformed explicit requests with a forged EPath size. An attacker can send a valid ENIP SendRRData frame carrying a very short CIP payload whose path_size field claims that many more path words are present than are actually available. Because the parser trusts the attacker-controlled path_size and continues decoding path segments without a remaining-length boundary, it reads beyond the end of the stack receive buffer. This issue is remotely triggerable via network traffic and does not require authentication.
Root Cause
The root cause is the combination of attacker-controlled length trust and post-parse validation in the EPath decoder path:
- In
DecodePaddedEPath() in source/src/cip/cipcommon.c, the code reads epath->path_size = *message_runner and then enters while(number_of_decoded_elements < epath->path_size) without knowing how many bytes remain in the input buffer.
- Inside that loop, the function directly dereferences
*message_runner and also reads additional bytes for class / instance / attribute segments, but the function signature does not carry a "remaining readable length" argument to enforce per-read bounds checks.
CreateMessageRouterRequestStructure() in source/src/cip/cipmessagerouter.c calls DecodePaddedEPath() first and only afterwards checks if(number_of_decoded_bytes > data_length). That check can detect an invalid path length only after decoding has already happened, so it cannot prevent the out-of-bounds read during decoding.
- When a malformed packet places only
service + path_size inside the CIP item and relies on trailing CPF bytes to keep the decoder walking forward, DecodePaddedEPath() eventually advances beyond incoming_message in HandleDataOnTcpSocket(), which ASan reports as a stack-buffer-overflow.
Trigger Conditions
- The target runs OpENer (POSIX) and listens on the ENIP TCP port (default
44818).
- The attacker can establish a TCP connection and complete
RegisterSession.
- The attack traffic uses
SendRRData carrying a malformed unconnected CIP request with a forged large path_size.
- The CIP data item is intentionally truncated so that only the service byte and
path_size are inside the nominal request, while trailing CPF bytes are positioned to be misinterpreted as path segments until the decoder reads beyond the 512-byte stack receive buffer.
Reproduction (Validated)
1) Build
Build OpENer in a POSIX environment (e.g. Ubuntu 22.04) with OpENer_TRACES and ASan enabled.
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 /home/user/OpENer/bin/posix
./setup_posix.sh
make
2) Run target
Start the target process:
./src/ports/POSIX/OpENer lo
3) Send attack traffic
In another terminal, send the PoC request:
PoC.zip
4) Observe behavior
After receiving the malformed request, the target triggers an ASan stack-buffer-overflow. The stack trace points to DecodePaddedEPath -> CreateMessageRouterRequestStructure -> NotifyMessageRouter, and the process exits with ABORTING. Log output:
networkhandler: opened new TCP connection on fd 21
something is wrong with the length in Message Router @ CreateCommonPacketFormatStructure
=================================================================
==32626==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffec0669660 at pc 0x55a4bbe5e6ae bp 0x7ffec06689f0 sp 0x7ffec06689e0
READ of size 1 at 0x7ffec0669660 thread T0
#0 0x55a4bbe5e6ad in DecodePaddedEPath /home/user/OpENer/source/src/cip/cipcommon.c:1403
#1 0x55a4bbe7cbbe in CreateMessageRouterRequestStructure /home/user/OpENer/source/src/cip/cipmessagerouter.c:254
#2 0x55a4bbe7c17c in NotifyMessageRouter /home/user/OpENer/source/src/cip/cipmessagerouter.c:188
#3 0x55a4bbe8df8f in NotifyCommonPacketFormat /home/user/OpENer/source/src/enet_encap/cpf.c:60
#4 0x55a4bbe96886 in HandleReceivedSendRequestResponseDataCommand /home/user/OpENer/source/src/enet_encap/encap.c:558
#5 0x55a4bbe93840 in HandleReceivedExplictTcpData /home/user/OpENer/source/src/enet_encap/encap.c:186
#6 0x55a4bbe4abe6 in HandleDataOnTcpSocket /home/user/OpENer/source/src/ports/generic_networkhandler.c:864
#7 0x55a4bbe48d8a in NetworkHandlerProcessCyclic /home/user/OpENer/source/src/ports/generic_networkhandler.c:497
#8 0x55a4bbe46513 in executeEventLoop /home/user/OpENer/source/src/ports/POSIX/main.c:261
#9 0x55a4bbe463bd in main /home/user/OpENer/source/src/ports/POSIX/main.c:229
#10 0x7f3450841082 in __libc_start_main ../csu/libc-start.c:308
#11 0x55a4bbe45bed in _start (/home/user/OpENer/bin/posix/src/ports/POSIX/OpENer+0x89bed)
Address 0x7ffec0669660 is located in stack of thread T0 at offset 1312 in frame
#0 0x55a4bbe4a27b 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:
0x1000580c5270: 00 00 00 00 00 00 00 00 00 00 00 00 f2 f2 f2 f2
0x1000580c5280: f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 00 00 00 00
0x1000580c5290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000580c52a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000580c52b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1000580c52c0: 00 00 00 00 00 00 00 00 00 00 00 00[f3]f3 f3 f3
0x1000580c52d0: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
0x1000580c52e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000580c52f0: 00 00 f1 f1 f1 f1 06 f3 f3 f3 00 00 00 00 00 00
0x1000580c5300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000580c5310: 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
==32626==ABORTING
Impact Assessment
This issue is remotely reachable through the normal EtherNet/IP explicit-message path and results in a real out-of-bounds read in server-side packet parsing. The currently validated impact is a reliable process crash / denial of service: a network attacker can send a crafted RegisterSession + SendRRData sequence that drives DecodePaddedEPath() past the end of the stack-backed TCP receive buffer and causes the OpENer process to abort under ASan. Even without sanitizers, the decoder still reads beyond the intended request boundary and interprets adjacent stack bytes as CIP path data, which is undefined behavior and should be treated as a memory-safety bug with remote DoS impact.
Vulnerability Description
OpENer 2.3.0 (commit 76b95cf) has an out-of-bounds read issue in CIP message parsing when handling malformed explicit requests with a forged EPath size. An attacker can send a valid ENIP
SendRRDataframe carrying a very short CIP payload whosepath_sizefield claims that many more path words are present than are actually available. Because the parser trusts the attacker-controlledpath_sizeand continues decoding path segments without a remaining-length boundary, it reads beyond the end of the stack receive buffer. This issue is remotely triggerable via network traffic and does not require authentication.Root Cause
The root cause is the combination of attacker-controlled length trust and post-parse validation in the EPath decoder path:
DecodePaddedEPath()insource/src/cip/cipcommon.c, the code readsepath->path_size = *message_runnerand then enterswhile(number_of_decoded_elements < epath->path_size)without knowing how many bytes remain in the input buffer.*message_runnerand also reads additional bytes for class / instance / attribute segments, but the function signature does not carry a "remaining readable length" argument to enforce per-read bounds checks.CreateMessageRouterRequestStructure()insource/src/cip/cipmessagerouter.ccallsDecodePaddedEPath()first and only afterwards checksif(number_of_decoded_bytes > data_length). That check can detect an invalid path length only after decoding has already happened, so it cannot prevent the out-of-bounds read during decoding.service + path_sizeinside the CIP item and relies on trailing CPF bytes to keep the decoder walking forward,DecodePaddedEPath()eventually advances beyondincoming_messageinHandleDataOnTcpSocket(), which ASan reports as a stack-buffer-overflow.Trigger Conditions
44818).RegisterSession.SendRRDatacarrying a malformed unconnected CIP request with a forged largepath_size.path_sizeare inside the nominal request, while trailing CPF bytes are positioned to be misinterpreted as path segments until the decoder reads beyond the 512-byte stack receive buffer.Reproduction (Validated)
1) Build
Build OpENer in a POSIX environment (e.g. Ubuntu 22.04) with
OpENer_TRACESand ASan enabled.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 /home/user/OpENer/bin/posix ./setup_posix.sh make2) Run target
Start the target process:
3) Send attack traffic
In another terminal, send the PoC request:
PoC.zip
4) Observe behavior
After receiving the malformed request, the target triggers an ASan
stack-buffer-overflow. The stack trace points toDecodePaddedEPath -> CreateMessageRouterRequestStructure -> NotifyMessageRouter, and the process exits withABORTING. Log output:Impact Assessment
This issue is remotely reachable through the normal EtherNet/IP explicit-message path and results in a real out-of-bounds read in server-side packet parsing. The currently validated impact is a reliable process crash / denial of service: a network attacker can send a crafted
RegisterSession+SendRRDatasequence that drivesDecodePaddedEPath()past the end of the stack-backed TCP receive buffer and causes the OpENer process to abort under ASan. Even without sanitizers, the decoder still reads beyond the intended request boundary and interprets adjacent stack bytes as CIP path data, which is undefined behavior and should be treated as a memory-safety bug with remote DoS impact.