जमा करें #865309: open62541 Project open62541 1.5.4 Memory Corruption (Stack Overflow)जानकारी

शीर्षकopen62541 Project open62541 1.5.4 Memory Corruption (Stack Overflow)
विवरणThe vulnerability affects the master branch of open62541 and is reproducible against the official example server examples/ci_server.c. The issue is a server-side uncontrolled recursion / stack exhaustion condition in the NodeManagement instantiation path. An attacker who can reach the OPC UA TCP endpoint and use NodeManagement services can remotely create a self-referential modelling graph in which an ObjectType contains a mandatory Object child whose TypeDefinition points back to the same ObjectType. When the server later instantiates that type, the implementation recursively expands the same mandatory child again and again, causing unbounded recursion in the core library and eventually terminating the server process with AddressSanitizer stack-overflow. The demonstrated security impact is remote denial of service. The vulnerable behavior is not caused by a malformed packet parser, a local API misuse pattern, or a custom harness. It occurs in the normal AddNodes / AddReferences processing logic of the core server implementation. In the tested setup the target is the project’s own official example server, build-asan/bin/examples/ci_server, compiled from examples/ci_server.c. The client reaches the server over a real network path using OPC UA TCP, creates and activates a SecureChannel and Session, and then sends a sequence of NodeManagement requests. The uploaded runtime logs show that the server starts with anonymous login enabled, exposing the NodeManagement surface needed for exploitation. The client log further shows that the first three operations succeed with status Good and that the final instantiation request fails only after the server side has already crashed, returning BadSecureChannelClosed to the client. The triggering sequence is straightforward. First, the attacker creates a new ObjectType, for example ns=1;s=wc-loop-type. Second, the attacker creates an Object child below that type, for example ns=1;s=wc-loop-child. Third, the attacker sets the child’s TypeDefinition to the same ObjectType wc-loop-type instead of a benign base type. Fourth, the attacker adds a HasModellingRule -> Mandatory reference to that child. Fifth, the attacker creates an instance of wc-loop-type. At that point the server enters normal type-instantiation logic and recursively tries to instantiate the same mandatory child for every newly created child object, with no effective cycle detection or recursion limit. From a code perspective, the problem is rooted in src/server/ua_services_nodemanagement.c. The first enabling condition is that addNode_addRefs() does not reject this semantic modelling loop. The function contains a check that prevents a node from using itself directly as its parent. However, that check only covers a structural parent-child self loop. It does not reject the semantic loop used here, where an ObjectType T has a mandatory Object child C whose TypeDefinition again refers to T. In the same function, type validation for Object nodes only verifies that the supplied TypeDefinition resolves to an ObjectType. Therefore, a malicious Object child whose TypeDefinition is the same ObjectType that owns it is accepted by the existing consistency checks. The second enabling condition is isMandatoryChild(). That helper only determines whether the child has a forward HasModellingRule reference to the Mandatory modelling rule. Once the attacker adds that reference, the child is treated as mandatory during instantiation. The function does not evaluate whether instantiating that child would re-enter the same type expansion graph, nor does it inspect ancestor types, visited nodes, or any recursion budget. The third and most important part is the interaction between copyChild(), copyObjectVariableChild(), copyAllChildren(), addTypeChildren(), and addNode_finish(). copyAllChildren() browses the source node’s Aggregates children and explicitly requests the TypeDefinition in the browse result mask. For each returned ReferenceDescription, copyChild() is called. If the destination already has a child with the same BrowseName, the code deep-copies missing descendants. If the child does not yet exist and is mandatory, copyChild() dispatches Object and Variable children into copyObjectVariableChild(). Inside copyObjectVariableChild(), the source child is copied and inserted as a new node. Critically, the new node is then linked with addNode_addRefs() using rd->typeDefinition.nodeId from the browse result. This means the attacker-controlled TypeDefinition is not normalized or rewritten; it is propagated as-is into the newly cloned child. If the original child was typed as wc-loop-type, every cloned child is also typed as wc-loop-type. The function then recursively copies the original child’s own children and finally calls addNode_finish() on the newly created child. That final step closes the recursion loop. addNode_finish() proceeds with normal post-insertion processing and invokes type/interface child instantiation for the new node. addTypeChildren() obtains the type hierarchy and iterates over it. For each type in the hierarchy it calls copyAllChildren() in order to copy and instantiate members of the type and its supertypes. Because the newly created child still has TypeDefinition = wc-loop-type, addTypeChildren() re-enters copyAllChildren() on the same malicious type. copyAllChildren() again enumerates the mandatory child wc-loop-child, copyChild() again identifies it as mandatory, and copyObjectVariableChild() again clones a new child whose TypeDefinition is still wc-loop-type. The recursion therefore becomes: addNode_finish(instance) -> addTypeChildren(type = wc-loop-type) -> copyAllChildren(source = wc-loop-type) -> copyChild(child = wc-loop-child) -> copyObjectVariableChild(new child, typeDefinition = wc-loop-type) -> addNode_finish(new child) -> addTypeChildren(type = wc-loop-type) -> repeat There is no visited-set, no graph cycle detection, and no recursion depth guard in this instantiation path. The implementation assumes that the modelling graph is safe to expand recursively, but the attacker can construct a graph that invalidates that assumption using only standard NodeManagement operations. The runtime evidence is consistent with this analysis. The supplied client log shows successful connection establishment, endpoint selection, session creation, and session activation. It then shows addObjectType rc=Good, addChildObject rc=Good, and addMandatoryRule rc=Good, proving that the malicious model was accepted by the server. The final step, instantiate rc=BadSecureChannelClosed, indicates that the connection collapsed while the instantiation request was being processed. The corresponding server log shows that the server accepted the connection, created the SecureChannel and Session, and then terminated with AddressSanitizer: stack-overflow. The backtrace repeatedly alternates through copyAllChildren, addTypeChildren, addNode_finish, copyObjectVariableChild, and copyChild, which is exactly the recursive expansion loop described above. Although the top frame finally lands in __asan_memcpy, zipNsGetNode, and browse, those are only the last observable frames after the recursion has already exhausted the thread stack. The root cause remains the uncontrolled recursive type expansion in NodeManagement. The security impact is a reliable remote denial of service against affected open62541 servers that expose NodeManagement to untrusted clients. In the demonstrated configuration, the official ci_server example exposes the required surface because anonymous login is enabled and permissive access control allows the modelling operations. The attacker only needs to send a small number of valid AddNodes and AddReferences requests that define the self-referential mandatory type graph and then trigger one instantiation. On ASan builds the process aborts with stack-overflow. On production-style builds without sanitizers, the likely effect is still server crash or forced termination due to stack exhaustion, resulting in loss of availability. The proper weakness classification is CWE-674: Uncontrolled Recursion. A robust fix should be placed in the core instantiation path rather than relying on applications to avoid constructing dangerous type graphs. The server should reject modelling configurations that introduce a recursive instantiation cycle, or at minimum detect and stop the cycle before recursive expansion begins. Practical remediation options include maintaining a visited set of expansion state, rejecting a mandatory Object or Variable child whose TypeDefinition resolves back into the type currently being instantiated, and enforcing a strict recursion depth limit as a final safety barrier. Without such protection, the same malicious type graph can repeatedly drive the server into infinite recursive expansion and crash the process.
स्रोत⚠️ https://github.com/open62541/open62541/issues/8133
उपयोगकर्ता
 SCU_1CP (UID 99172)
सबमिशन22/06/2026 01:25 PM (4 महीनों पहले)
संयम08/08/2026 01:35 PM (2 months later)
स्थितिप्रतिलिपि
VulDB प्रविष्टि386429 [open62541 तक 1.5.5 NodeManagement Type-Instantiation Logic सेवा अस्वीकार]
अंक0

Do you need the next level of professionalism?

Upgrade your account now!