OpENer DecodePaddedEPath EPATH path_size OOB read (unauthenticated ENIP) — OpENer @ 36943e6
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 52/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- c
- Domain
- networking, security
Research direction
Start in source/src/cip/cipcommon.c at DecodePaddedEPath and trace its MessageRouter callers to determine the received-buffer length available at each call. Build and run poc/opener_epath_oob_read_repro.c with AddressSanitizer, then exercise the generated ENIP packets; done means a short malformed EPATH is rejected without an out-of-bounds read and the sanitizer reproduction no longer reports an overflow.
Written by the indexing model from the issue text.
Description
Summary
OpENer decodes a CIP EPATH (electronic path) by reading a path_size count byte from the request and then walking that many path words in DecodePaddedEPath — without a remaining-buffer-length argument or bound. A request whose EPATH path_size is larger than the actual data (e.g. path_size = 0xFF with a couple of non-terminating segment bytes) walks the segment loop off the end of the fixed incoming_message receive buffer (~512 bytes), reading adjacent memory. Reachable unauthenticated over ENIP (TCP/44818) via RegisterSession → SendRRData → MessageRouter request. Confirmed via a faithful ASan model: heap-buffer-overflow READ past the buffer end.
- Affected Version & Scope
Repository / product: OpENer — https://github.com/EIPStackGroup/OpENer
Pinned commit / firmware build: 36943e6 (HEAD at analysis)
Affected range (if known): commit 36943e6 and earlier; present at HEAD.
Build/config preconditions: default build; the EPATH decode path is on the standard MessageRouter request-handling flow.
Discovery of the bug
Manual source-to-sink trace of the EPATH decoder. The tell: number_of_decoded_elements = message (:1397, the wire path_size) drives while (number_of_decoded_elements < path_size) (:1404), and the function signature takes only the message pointer — no remaining-length argument. Each iteration consumes 2+ bytes ((message+1), message += 2) with no end check, so path_size = 0xFF plus non-terminating logical-segment bytes (0x20) keeps the loop consuming past the buffer. A faithful ASan model (a 300-byte redzoned buffer modelling the tail of incoming_message; buf[0] = 255; segment bytes 0x20 driving the walk) — a full OpENer build was avoided — reproduces heap-buffer-overflow READ of size 1 past the region.
Technical Details & Root Cause
source/src/cip/cipcommon.c, DecodePaddedEPath:
number_of_decoded_elements = message; / :1397 path_size, one wire byte, 0..255 words — attacker-controlled /
++message;
while (number_of_decoded_elements < path_size) { / :1404 no check vs the received buffer end */
if ((message & kSegmentTypeReserved) == kSegmentTypeReserved) { ... } / :1405 /
/ e.g. class/instance/attribute segments: read *(message+1), message += 2 */
}
Reproduction — numbered steps
clang -fsanitize=address -g -O1 poc/opener_epath_oob_read_repro.c -o repro_OPENER-01.
./repro_OPENER-01.
Observed: AddressSanitizer: heap-buffer-overflow READ of size 1 — the EPATH walk driven by path_size=255 runs off the 300-byte buffer end.
On-wire: an unauthenticated ENIP client (TCP/44818) sends RegisterSession, then a SendRRData whose CPF data item is a MessageRouter request with an EPATH of path_size=0xFF and no matching data; poc/gen_opener_epath_oob.py prints both packets.
Sanitizer / crash evidence
OPENER-01-asan.txt:
[opener] path_size=255 drives the walk with no buffer-end check (buf=300B)
==319685==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61200000016c ...
READ of size 1 at 0x61200000016c thread T0
#0 main .../poc/opener_epath_oob_read_repro.c:24:17
0x61200000016c is located 0 bytes to the right of 300-byte region [0x612000000040,0x61200000016c)
allocated by thread T0 here:
#0 __interceptor_malloc
#1 main .../poc/opener_epath_oob_read_repro.c:14:20
SUMMARY: AddressSanitizer: heap-buffer-overflow .../opener_epath_oob_read_repro.c:24:17 in main
==319685==ABORTING
Note: if you could provide the CVE once confirmed.
- Dominant language
- C
- Stars
- 858
- Forks
- 314
- Avg merge
- 18d 2h
- Merged PRs (30d)
- 1
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from EIPStackGroup/OpENer
-
Memory leak in TCP/IP Interface Object attribute 5 and 6 setters on the validation-failure path Open
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
EIPStackGroup/OpENer#605 ·
-
Difficulty 4/5 3-5 days Newbie friendliness 42/100
EIPStackGroup/OpENer#610 ·
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
EIPStackGroup/OpENer#593 ·
-
Difficulty 4/5 3-5 days Newbie friendliness 58/100
EIPStackGroup/OpENer#592 ·
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
EIPStackGroup/OpENer#582 ·
All issues in EIPStackGroup/OpENer
Similar issues
-
level/task module/gcp type/bug
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
-
Difficulty 1/5 Under an hour Newbie friendliness 86/100
hapostgres/pg_auto_failover#1190 ·
-
docs
Difficulty 1/5 Under an hour Newbie friendliness 85/100
-
P3 sonic-vpp
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
sonic-net/sonic-buildimage#29662 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 94/100
spack/spack-packages#6586 ·