إرسال #880100: liftoff-sr CIPster master branch (reproduced on 1802525be27d33e19a9a83c163e331a1d13b1892) Out-of-bounds Read/Writeالمعلومات

عنوانliftoff-sr CIPster master branch (reproduced on 1802525be27d33e19a9a83c163e331a1d13b1892) Out-of-bounds Read/Write
الوصفCIPster contains a remotely reachable memory-corruption vulnerability in its generic attribute logic when an application uses the documented object/attribute registration API to expose the same storage through both a writable kCipByteArray attribute and a writable kCipByteArrayLength companion attribute. The issue was reported against the GitHub master branch at commit 1802525. In the affected design, the underlying ByteBuf abstraction stores only start and limit pointers and derives the logical size from limit - start. It does not preserve an immutable physical capacity for the backing allocation. Because of that design, a remote explicit-message client can first send SetAttributeSingle to the writable length attribute, enlarge the shared ByteBuf metadata beyond the real backing allocation, and then trigger a second explicit operation on the byte-array attribute. The second operation enters CIPster core generic encode/decode code paths and causes an out-of-bounds read or out-of-bounds write. The vulnerable flow follows the EtherNet/IP explicit-message path: NetworkHandlerProcessOnce() -> HandleDataOnTcpSocket() -> Encapsulation::HandleReceivedExplicitTcpData() -> Cpf::NotifyCommonPacketFormat() -> CipMessageRouterClass::NotifyMR(...) -> CipAttribute::GetAttrData() / CipAttribute::SetAttrData() -> EncodeData() / DecodeData() -> BufWriter::append() / memcpy(). The memory violation is therefore triggered inside CIPster’s generic stack logic after normal session establishment and message routing, not in ad hoc application code. The root cause is a length/capacity inconsistency in shared ByteBuf metadata. In the affected implementation, ByteBuf stores only start and limit and computes size() from those fields. The problematic setter path for kCipByteArrayLength reconstructs the ByteBuf directly from attacker-controlled network input: a ByteBuf pointer is cast from the generic attribute storage and replaced with ByteBuf(bb->data(), aBuf.get16()). That operation rewrites the logical limit using a wire-supplied 16-bit value without validating that the new size is still within the real backing-store allocation. After this metadata poisoning step, later generic operations continue to trust the modified ByteBuf. GetAttrData() passes the storage to EncodeData(), which in the kCipByteArray case appends size() bytes to the response. SetAttrData() passes the same storage to DecodeData(), which in the kCipByteArray case constructs a writable view from the already-poisoned metadata and appends request data into it. Once the length attribute has inflated the logical size, the subsequent read or write operates according to attacker-influenced metadata rather than the true capacity of the original allocation. Two independently triggerable variants arise from the same root cause. Variant one is an out-of-bounds read. The attacker first sends SetAttributeSingle to the writable kCipByteArrayLength attribute and sets the shared logical size to a value larger than the real backing buffer. A later GetAttributeSingle on the associated kCipByteArray attribute reaches EncodeData(kCipByteArray), which appends the full poisoned size into the outgoing response buffer. If the physical storage is 64 bytes and the logical size is changed to 512, the generic encode path attempts to read 512 bytes from a 64-byte backing region. Under AddressSanitizer this manifests as a global-buffer-overflow read in BufWriter::append() with the stack passing through EncodeData() and CipAttribute::GetAttrData(). Variant two is an out-of-bounds write. The attacker again poisons the shared metadata by writing an oversized value to the kCipByteArrayLength companion attribute. A subsequent SetAttributeSingle on the kCipByteArray attribute reaches DecodeData(kCipByteArray), which creates a BufWriter from the poisoned ByteBuf and appends the incoming request payload into that writer. The write-side bound is checked only against the poisoned logical end pointer, not the true backing-store capacity, so the append operation can write beyond the physical buffer. In the reproduced case, a 64-byte backing array is first resized logically to 512 bytes and then a 512-byte payload is written through the generic setter path, producing a real out-of-bounds write into adjacent memory. Under AddressSanitizer this manifests as a global-buffer-overflow write in BufWriter::append() with the call chain DecodeData() -> CipAttribute::SetAttrData(). The exposure needs to be described precisely. The report does not claim that the default examples/POSIX/sample program is exploitable as shipped. Instead, the demonstrated condition arises when an application uses CIPster’s public API to register one attribute as kCipByteArray and a writable companion attribute as kCipByteArrayLength while both aliases point to the same bare ByteBuf storage. That distinction should remain in the advisory text. The absence of a stock-sample trigger does not remove the security impact, because the library explicitly permits this registration pattern and the later corruption occurs inside core generic code that assumes the metadata remains trustworthy. The unsafe condition is therefore created using a supported API model, and the library itself fails to enforce the invariant that logical length must never exceed physical capacity. The proof of concept is intentionally minimal and does not change CIPster core behavior. A custom class exposes attribute 1 as kCipByteArray and attribute 2 as kCipByteArrayLength, both backed by the same ByteBuf pointer. The shared backing array is only 64 bytes long. The PoC then uses ordinary explicit-message services over a registered session. Stage one uses SetAttributeSingle with service code 0x10 to write a new length to attribute 2. Stage two either issues GetAttributeSingle with service code 0x0E against attribute 1 to trigger the read variant, or issues SetAttributeSingle against attribute 1 with a large payload to trigger the write variant. The first request succeeds with general_status 0, confirming that the length change is accepted as a normal attribute write before the second request enters the vulnerable generic path. The resulting crashes are reported by AddressSanitizer as out-of-bounds read and write against the 64-byte backing object, and the stack traces land in CIPster’s own byte-buffer and attribute-encoding code rather than in custom application logic. This issue should not be dismissed as a false positive caused purely by an unusual embedding application. The application layer in the reproducer does not implement its own explicit-message parser, does not perform its own dangerous copy, and does not corrupt metadata directly. Its role is only to create an attribute layout that the public API permits. The actual memory-safety failure occurs because CIPster’s generic setter for kCipByteArrayLength rewrites ByteBuf metadata using attacker-controlled input while generic getter/setter logic for kCipByteArray subsequently trusts that rewritten metadata for read/write boundaries. As a result, the trusted boundary is inside the library. The maintainer’s response materially strengthens the assessment. According to the maintainer, the issue is confirmed and reachable through the documented API, not misuse. The maintainer summarized the root cause as the fact that a kCipByteArray attribute and its kCipByteArrayLength companion were both backed by a bare ByteBuf that records only start and limit and no physical capacity; the length setter rebuilt the view to start + len without a bound, so a subsequent get over-read and a set wrote out of bounds. The fix also shows that the problem is in the library. The maintainer introduced a new capacity-bearing CipByteArray structure in ciptypes.h that carries data, capacity, and length, with capacity remaining immutable and traveling with the storage. EncodeData was changed to emit only length() bytes instead of trusting a raw ByteBuf size. DecodeData was changed to reject writes larger than capacity() and to reject oversized length updates rather than extending the writable window. The maintainer noted that a rejected set returns CIP general status 0x09 (Invalid Attribute Value). The fix was prepared on branch harden-attribute-registration-api in commit e745d9d, with an accompanying security regression test in commit ad12c38. From an impact perspective, the issue enables unauthenticated memory corruption in deployments that expose the vulnerable attribute pattern. The out-of-bounds write variant can crash the server process reliably and corrupt adjacent memory. The out-of-bounds read variant demonstrates that memory beyond the intended byte-array boundary can be consumed by the generic encode path. A conservative database description is therefore that remote attackers can trigger out-of-bounds read and write conditions in CIPster’s generic attribute handling in API-enabled deployments, leading to memory corruption and process termination.
المصدر⚠️ https://github.com/liftoff-sr/CIPster/issues/47
المستخدم
 Carnegie (UID 98671)
ارسال04/07/2026 03:01 PM (2 أشهر منذ)
الاعتدال20/08/2026 08:52 AM (2 months later)
الحالةتمت الموافقة
إدخال VulDB393610 [liftoff-sr CIPster 1802525be27d33e19a9a83c163e331a1d13b1892 Generic Attribute Logic ciptypes.h SetAttrData تلف الذاكرة]
النقاط20

Are you interested in using VulDB?

Download the whitepaper to learn more about our service!