EIPStackGroup / EIPStackGroup/OpENer
Stack Out-of-Bounds Read in ForwardOpen Parsing
- Dominant language
- C
- Stars
- 857
- Forks
- 314
- Avg merge
- 18d 2h
- Merged PRs (30d)
- 1
Description
## Vulnerability Description
OpENer (commit 76b95cf) has an out-of-bounds read issue in Connection Manager handling of `ForwardOpen` requests when processing short malformed packets. An attacker can send a valid ENIP outer frame carrying a malformed CIP `ForwardOpen`/`LargeForwardOpen` request, causing the parser to continue reading fields even when request data is insufficient. This issue is remotely triggerable via network traffic and does not require authentication.
## Root Cause
The root cause is the combination of parse-before-validate logic and missing remaining-length boundary checks in low-level read helpers:
1. In `ForwardOpenRoutine()` in `source/src/cip/cipconnectionmanager.c`, the code calls `ConnectionObjectInitializeFromMessage(&(message_router_request->data), ...)` before completing strict minimal-length validation.
2. `ConnectionObjectInitializeFromMessage()` in `source/src/cip/cipconnectionobject.c` repeatedly calls `GetByteFromMessage()` / `GetUintFromMessage()` / `GetUdintFromMessage()` to parse fields, but this call chain does not carry a "remaining readable length" parameter for per-read boundary checks.
3. `GetByteFromMessage()` in `source/src/enet_encap/endianconv.c` directly reads `buffer[0]` and advances the pointer; when the input pointer is already at the end of the receive buffer, it performs an out-of-bounds read.
## Trigger Conditions
1. The target runs OpENer (POSIX) and listens on the ENIP TCP port (default `44818`).
2. The attacker can establish a TCP connection and complete `RegisterSession`.
3. The attack traffic uses `SendRRData` carrying a malformed `ForwardOpen` or `LargeForwardOpen` request.
4. The malicious packet pads the route path close to the end of the stack receive buffer and keeps request data extremely short (`request_data_size == 0`), leading to out-of-bounds field parsing.
## Reproduction (Validated)
[PoC.zip](https://github.com/user-attachments/files/27010332/PoC.zip)
### 1) Build
Build OpENer in a POSIX environment (e.g., Ubuntu 22.04) with `OpENer_TRACES` and ASan enabled.
```bash
cd /bin/posix
./setup_posix.sh
make
```
### 2) Run target
Start the target (slave) process:
```bash
./src/ports/POSIX/OpENer lo
```
### 3) Send attack traffic
In another terminal, send PoC attack traffic:
```bash
python3 attacker.py
```
### 4) Observe behavior
After receiving the attack traffic, the target triggers an ASan `stack-buffer-overflow`. The stack trace points to `GetByteFromMessage -> ConnectionObjectInitializeFromMessage -> ForwardOpenRoutine`, and the process exits with `ABORTING`. Log output:
```text
networkhandler: opened new TCP connection on fd 22
=================================================================
==576651==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe15b181d0 at pc 0x55a88b39f67e bp 0x7ffe15b17570 sp 0x7ffe15b17560
READ of size 1 at 0x7ffe15b181d0 thread T0
#0 0x55a88b39f67d in GetByteFromMessage /home/user/OpENer/source/src/enet_encap/endianconv.c:40
#1 0x55a88b36715f in ConnectionObjectInitializeFromMessage /home/user/OpENer/source/src/cip/cipconnectionobject.c:124
#2 0x55a88b36da2e in ForwardOpenRoutine /home/user/OpENer/source/src/cip/cipconnectionmanager.c:596
#3 0x55a88b36d9a2 in ForwardOpen /home/user/OpENer/source/src/cip/cipconnectionmanager.c:579
#4 0x55a88b356665 in NotifyClass /home/user/OpENer/source/src/cip/cipcommon.c:127
#5 0x55a88b382854 in NotifyMessageRouter /home/user/OpENer/source/src/cip/cipmessagerouter.c:217
#6 0x55a88b393f8f in NotifyCommonPacketFormat /home/user/OpENer/source/src/enet_encap/cpf.c:60
#7 0x55a88b39c886 in HandleReceivedSendRequestResponseDataCommand /home/user/OpENer/source/src/enet_encap/encap.c:558
#8 0x55a88b399840 in HandleReceivedExplictTcpData /home/user/OpENer/source/src/enet_encap/encap.c:186
#9 0x55a88b350be6 in HandleDataOnTcpSocket /home/user/OpENer/source/src/ports/generic_networkhandler.c:864
#10 0x55a88b34ed8a in NetworkHandlerProcessCyclic /home/user/OpENer/source/src/ports/generic_networkhandler.c:497
#11 0x55a88b34c513 in executeEventLoop /home/user/OpENer/source/src/ports/POSIX/main.c:261
#12 0x55a88b34c3bd in main /home/user/OpENer/source/src/ports/POSIX/main.c:229
#13 0x7f77783e9082 in __libc_start_main ../csu/libc-start.c:308
#14 0x55a88b34bbed in _start (/home/user/OpENer/bin/posix/src/ports/POSIX/OpENer+0x89bed)
Address 0x7ffe15b181d0 is located in stack of thread T0 at offset 1312 in frame
#0 0x55a88b35027b 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/enet_encap/endianconv.c:40 in GetByteFromMessage
Shadow bytes around the buggy address:
0x100042b5afe0: 00 00 00 00 00 00 00 00 00 00 f2 f2 f2 f2 f2 f2
0x100042b5aff0: f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 00 00 00 00 00 00
0x100042b5b000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100042b5b010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100042b5b020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100042b5b030: 00 00 00 00 00 00 00 00 00 00[f3]f3 f3 f3 f3 f3
0x100042b5b040: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100042b5b050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100042b5b060: f1 f1 f1 f1 06 f3 f3 f3 00 00 00 00 00 00 00 00
0x100042b5b070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100042b5b080: 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
==576651==ABORTING
```
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.