| Название | open62541 Project open62541 master (commit ca356b088ada7dee824d1b4acd07c1ff07ce242b) out-of-bounds read |
|---|
| Описание | A client-side out-of-bounds read / invalid memory read vulnerability was identified in open62541 on master commit ca356b088ada7dee824d1b4acd07c1ff07ce242b. The issue is reachable through the normal OPC UA TCP connection workflow of the official client examples and affects the shared client library code rather than example-specific logic. A malicious OPC UA server can complete a valid handshake and session establishment sequence with the victim client and then return a crafted but protocol-level successful ReadResponse for the NamespaceArray read performed automatically by the client after session activation. This leads to a crash in the client-side response handler responseReadNamespacesArray() in src/client/ua_client_connect.c.
The vulnerable path is the standard client connection sequence used by the official examples/client_connect.c and examples/client_connect_loop.c. The observed protocol flow is HEL/ACK, OpenSecureChannel, FindServers, GetEndpoints, CreateSession, ActivateSession, and then Read(Server_NamespaceArray). The last step is not an artificial test action added by the proof of concept. It is part of the normal open62541 client state machine. After the session is activated, the library asynchronously reads the server NamespaceArray and processes the response in responseReadNamespacesArray(). Therefore, this is not a parser-only bug, not a local helper misuse, and not an issue limited to a custom harness. It is reachable through a real network path using the public client API and the official example applications.
The malicious server behavior required to trigger the issue is minimal. The attacker completes the channel and session setup successfully and returns a ReadResponse for the NamespaceArray request with responseHeader.serviceResult set to Good, resultsSize set to 0, and results encoded as an empty array rather than as a NULL pointer. In open62541, zero-length arrays are represented using UA_EMPTY_ARRAY_SENTINEL, which is a non-NULL sentinel pointer value ((void*)0x01). As a result, on the client side the decoded response contains resp->resultsSize = 0 and resp->results = 0x1. This state is central to the vulnerability.
The root cause is an incomplete validation in responseReadNamespacesArray(). The current code checks:
if(!resp->results || !resp->results[0].value.data) {
This condition assumes that either resp->results is NULL or resp->results[0] is a valid element that can be dereferenced. That assumption is incorrect for zero-length arrays represented with UA_EMPTY_ARRAY_SENTINEL. When resp->results is 0x1, the first condition is false because the pointer is non-NULL. Evaluation then proceeds to resp->results[0].value.data, which dereferences the sentinel as if it were a valid UA_DataValue pointer. At that point, the client performs an invalid read from an address derived from the sentinel value and crashes.
The crash mechanics are consistent with the structure layout observed during debugging. The malicious response causes the client to treat 0x1 as a UA_DataValue*. The code then accesses resp->results[0].value.data. According to the runtime-observed layout, UA_DataValue begins with UA_Variant value, and the data field of UA_Variant is located at offset 24 in the tested build. This means the client attempts to read from 0x1 + 0x18 = 0x19, which matches the sanitizer output precisely. UBSan reports misaligned member access on address 0x000000000001 and then a load of misaligned address 0x000000000019 for type void*. ASan subsequently terminates the process with a SEGV on unknown address 0x000000000019 caused by a READ access. This is strong evidence that the crash is a real memory-safety violation and not a benign logic error.
The issue can be reproduced with the official victim clients and a malicious server. In the verified setup, the victim binaries are build-asan/bin/examples/client_connect and build-asan/bin/examples/client_connect_loop, while the malicious server is tools/wc_server_client_nsread_zero_results.c compiled as /tmp/wc_server_client_nsread_zero_results_plain. A normal baseline can be established first by running the malicious server in a benign mode, after which client_connect connects successfully and prints the expected connection status. In the attack mode, the server returns the crafted zero-length NamespaceArray ReadResponse described above. The client then proceeds through SecureChannel creation and session activation successfully and only crashes after SessionState becomes Activated, when the NamespaceArray ReadResponse is processed. This timing is important because it confirms that the failure occurs after a valid protocol-level session setup and is not the result of an earlier connection error.
The client-side runtime logs confirm the full successful path before the crash: opening a TCP connection to localhost:4840, opening the SecureChannel, validating and selecting the endpoint, creating the session, and activating the session. After “Client Status: ChannelState: Open, SessionState: Activated, ConnectStatus: Good”, UBSan reports member access within misaligned address 0x000000000001 for type UA_DataValue in src/client/ua_client_connect.c:741, followed by a load of misaligned address 0x000000000019 for type void*, and ASan reports “SEGV on unknown address 0x000000000019” in responseReadNamespacesArray(). The backtrace shows the call chain responseReadNamespacesArray() -> processMSGResponse() -> processServiceResponse() -> __Client_networkCallback() -> connectSync() -> __UA_Client_connect() -> UA_Client_connect() -> main() in examples/client_connect.c. This backtrace demonstrates that the crash occurs in shared library code reached from the normal official example application.
The server-side runtime output aligns with the client observations. The malicious server receives HEL, OPN, and subsequent MSG requests corresponding to the normal discovery and session sequence. It responds successfully to OpenSecureChannel, FindServers, GetEndpoints, CreateSession, and ActivateSession. It then receives the final Read request and sends the crafted response with a zero-length results array. This proves the vulnerability is remotely triggerable by a malicious server over an actual OPC UA TCP session and is not dependent on local fuzzed buffers or synthetic API misuse.
From a security perspective, the currently demonstrated impact is a reliable client-side denial of service. Any application built on top of the affected open62541 client path and connecting to an attacker-controlled OPC UA server may be crashed remotely once the connection reaches the NamespaceArray read stage. The issue resides in the shared client library logic in src/client/ua_client_connect.c, so the official examples are only minimal reproducers and are not the root cause. There is currently no evidence from the verified data that the issue results in remote code execution or arbitrary memory write. The confirmed impact is a remotely reachable memory-safety bug causing process termination.
It is also worth noting that the immediate missing check is not limited to resultsSize == 0. The handler additionally assumes that a first result exists, that it contains a value, that the value is of the expected array type, and that the array length is sufficient for later accesses. However, the specific reproducible vulnerability described here is the absence of a resultsSize check in combination with the use of UA_EMPTY_ARRAY_SENTINEL for empty arrays. This combination makes a non-NULL pointer value represent a zero-length array, defeating the current guard and leading directly to the out-of-bounds read.
In summary, this is a genuine client-side memory-safety vulnerability in open62541 master ca356b088ada7dee824d1b4acd07c1ff07ce242b. A malicious OPC UA server can complete a valid handshake and session setup with the victim and then return a protocol-level successful NamespaceArray ReadResponse with resultsSize = 0 and a zero-length array encoded via UA_EMPTY_ARRAY_SENTINEL. Because responseReadNamespacesArray() does not validate resultsSize before dereferencing resp->results[0], the client interprets the empty-array sentinel 0x1 as a valid UA_DataValue pointer, performs an invalid read at 0x19, and crashes. The vulnerability is remotely reachable through the normal client state machine and has been reproduced against the official example clients with ASan/UBSan-confirmed results. |
|---|
| Источник | ⚠️ https://github.com/open62541/open62541/issues/8104 |
|---|
| Пользователь | VULL (UID 98817) |
|---|
| Представление | 11.06.2026 13:34 (1 месяц назад) |
|---|
| Модерация | 14.07.2026 06:55 (1 month later) |
|---|
| Статус | принято |
|---|
| Запись VulDB | 378237 [open62541 до 1.5.5 Shared Client Library ua_client_connect.c responseReadNamespacesArray Server_NamespaceArray отказ в обслуживании] |
|---|
| Баллы | 20 |
|---|