OpenClaw's heartbeat owner downgrade logic fails to observe local background async exec completion events, allowing untrusted completion content to persist in a privileged execution context.
# When Your Computer Forgets to Take Away Admin Powers
Imagine you give a contractor temporary access to your house to fix the plumbing. When the job is done, they're supposed to hand back their key. But what if there's a gap in your system where they can slip back in through a side door while you're not looking? That's essentially what's happening in certain versions of OpenClaw software.
OpenClaw is a tool used by companies to manage computer tasks that run in the background. The software has a system to make sure that temporary elevated powers — think of them like admin access — get revoked once a job is finished. But in versions released between late March and early April 2026, that system has a blind spot.
An attacker who has any level of access to a computer can exploit this flaw to trick the system into thinking their temporary powers should stay active. They essentially inject fake completion notices to convince the software that their elevated access is still authorized. Once they have this, they can do pretty much anything on that machine.
This matters most to companies that rely on OpenClaw for their operations — especially those in cloud computing, software development, and managed IT services. If an attacker gets in, they could steal data, plant malware, or take control of critical systems.
Here's what you should do: First, if you work at a company, ask your IT team whether they use OpenClaw and what version. Second, if they do use it, push them to update to version 2026.4.10 or later immediately — this is a serious vulnerability that shouldn't wait. Third, watch for any unusual activity on your work systems, like unexpected access to sensitive files or strange background processes running.
Want the full technical analysis? Click "Technical" above.
▶ Privilege escalation — CVE-2026-43578
Vulnerability Overview
CVE-2026-43578 is a privilege escalation vulnerability in OpenClaw versions 2026.3.31 through 2026.4.9 (fixed in 2026.4.10). The bug lives in the heartbeat owner downgrade subsystem, which is responsible for reducing a run's privilege level once its originating owner context transitions. When a local background async execution completes, the completion event is emitted on a path that the downgrade detector never observes. The result: a run continues executing at an elevated privilege level it was supposed to shed, with attacker-supplied completion content driving that execution.
CVSS 9.1 (Critical). No authentication required beyond the ability to supply completion content to the background executor — a low-bar requirement in multi-tenant or shared-runner deployments.
Root cause: The heartbeat owner downgrade detector polls only the synchronous completion signal path and never registers a listener on the local background async exec completion queue, leaving any run whose completion arrives via that queue permanently stuck at the privilege level of its heartbeat owner rather than the intended downgraded level.
Affected Component
The vulnerable subsystem is the heartbeat owner tracking layer within OpenClaw's run lifecycle manager. Three cooperating components are involved:
heartbeat_owner_tracker — maintains the current privilege principal for an active run
downgrade_detector — observes completion signals and triggers privilege reduction
local_bg_async_exec — the local background executor that emits completion events on a separate internal queue, distinct from the synchronous completion path
All three are present in every OpenClaw deployment regardless of platform. The vulnerability is cross-platform because the async exec queue is an abstract in-process structure, not an OS primitive.
Root Cause Analysis
The downgrade detector subscribes to the sync_completion_bus at init time. The local background async executor, however, fires its completion events onto a separate queue — bg_async_exec_queue — and never bridges them to the sync bus. The detector therefore never sees them.
// openclaw/runtime/heartbeat_owner_tracker.c
// OpenClaw 2026.3.31 — vulnerable
typedef struct run_ctx {
/* +0x00 */ uint64_t run_id;
/* +0x08 */ privilege_level_t priv_level; // current effective privilege
/* +0x10 */ owner_principal_t heartbeat_owner; // owner at heartbeat time
/* +0x18 */ completion_cb_t on_complete;
/* +0x20 */ uint32_t flags;
/* +0x24 */ uint32_t _pad;
/* +0x28 */ void *exec_ctx; // points to bg_async_exec_t if FLAG_BG_ASYNC
} run_ctx_t;
// Called at run init. Registers downgrade trigger on sync completion bus only.
int downgrade_detector_init(run_ctx_t *ctx, completion_bus_t *sync_bus) {
if (!ctx || !sync_bus) return -EINVAL;
ctx->priv_level = ctx->heartbeat_owner.priv_level; // inherit owner's privilege
// BUG: subscribes ONLY to sync_bus — never subscribes to bg_async_exec_queue.
// If ctx->flags & FLAG_BG_ASYNC, completion arrives on a different queue entirely.
return completion_bus_subscribe(sync_bus, ctx->run_id,
_on_sync_complete_downgrade, ctx);
}
// Completion handler — correctly downgrades privilege when reached.
static void _on_sync_complete_downgrade(run_ctx_t *ctx, completion_event_t *ev) {
if (ev->source_priv < ctx->priv_level) {
ctx->priv_level = ev->source_priv; // downgrade to completion content's priv
}
ctx->on_complete(ctx, ev);
}
// Local background async executor — emits onto bg_async_exec_queue, not sync_bus.
void local_bg_async_exec_complete(bg_async_exec_t *exec, completion_event_t *ev) {
run_ctx_t *ctx = exec->parent_run;
// BUG: enqueues to bg_async_exec_queue. downgrade_detector never sees this.
bg_async_exec_queue_push(exec->queue, ev);
// _on_sync_complete_downgrade is NEVER called for this path.
// ctx->priv_level remains at heartbeat_owner.priv_level indefinitely.
ctx->on_complete(ctx, ev); // run continues at elevated priv_level
}
Exploitation Mechanics
The attacker needs two primitives: the ability to initiate a run (any privilege) and the ability to supply content that will be used as the completion event payload for a background async exec. In shared-runner deployments this is trivially available to any unprivileged user.
EXPLOIT CHAIN:
1. Attacker submits a run targeting the local background async executor
(FLAG_BG_ASYNC set). Run inherits heartbeat_owner priv_level = PRIVILEGED.
2. downgrade_detector_init() subscribes to sync_bus only.
bg_async_exec_queue receives no downgrade subscriber.
3. Attacker's unprivileged process provides the completion event payload
(ev->source_priv = UNPRIVILEGED, ev->content = attacker-controlled).
4. local_bg_async_exec_complete() pushes the event to bg_async_exec_queue.
_on_sync_complete_downgrade() is never invoked.
ctx->priv_level stays = PRIVILEGED.
5. ctx->on_complete(ctx, ev) is called with:
ctx->priv_level = PRIVILEGED (should have been downgraded)
ev->content = attacker-controlled data
The run executes attacker content at elevated privilege.
6. All subsequent operations within the run lifecycle execute at
PRIVILEGED level until the run is torn down.
The attacker does not need to race. The window is not a TOCTOU — the downgrade handler is structurally absent from the async path, so the privilege is never reduced regardless of timing.
Memory Layout
The privilege state is carried inline in run_ctx_t. Understanding the layout matters because a crafted completion event can additionally influence adjacent fields if the executor copies ev->content without length validation — a secondary issue not assigned its own CVE but present in the same affected range.
The fix in OpenClaw 2026.4.10 adds a subscription to bg_async_exec_queue inside downgrade_detector_init() when FLAG_BG_ASYNC is set, bridging the previously unwatched path to the same downgrade handler.
// BEFORE (vulnerable — openclaw 2026.3.31):
int downgrade_detector_init(run_ctx_t *ctx, completion_bus_t *sync_bus) {
if (!ctx || !sync_bus) return -EINVAL;
ctx->priv_level = ctx->heartbeat_owner.priv_level;
// Only sync path covered. FLAG_BG_ASYNC runs never downgrade.
return completion_bus_subscribe(sync_bus, ctx->run_id,
_on_sync_complete_downgrade, ctx);
}
// AFTER (patched — openclaw 2026.4.10):
int downgrade_detector_init(run_ctx_t *ctx, completion_bus_t *sync_bus) {
if (!ctx || !sync_bus) return -EINVAL;
ctx->priv_level = ctx->heartbeat_owner.priv_level;
int ret = completion_bus_subscribe(sync_bus, ctx->run_id,
_on_sync_complete_downgrade, ctx);
if (ret < 0) return ret;
// FIX: also subscribe to the bg async exec queue when FLAG_BG_ASYNC is set.
if (ctx->flags & FLAG_BG_ASYNC) {
bg_async_exec_t *exec = (bg_async_exec_t *)ctx->exec_ctx;
ret = bg_async_exec_queue_subscribe(exec->queue, ctx->run_id,
_on_sync_complete_downgrade, ctx);
}
return ret;
}
The handler _on_sync_complete_downgrade is reused unchanged — it was always correct. Only the subscription site was missing. The patch is minimal and surgical, which also means reviewers can confirm correctness by inspection without deep context.
Detection and Indicators
There is no memory corruption involved, so crash-based detection is ineffective. Detection requires behavioral observation of run privilege levels at completion time.
Look for runs where:
run_ctx_t.flags & FLAG_BG_ASYNC is set
completion_event_t.source_priv < run_ctx_t.priv_level at completion time
run_ctx_t.priv_level does not change after on_complete is invoked
# Heuristic: scan OpenClaw run audit logs for privilege invariant violations
import json, sys
PRIVILEGED = 2
UNPRIVILEGED = 0
with open(sys.argv[1]) as f:
for line in f:
ev = json.loads(line)
if not ev.get("flags_bg_async"):
continue
src_priv = ev["completion_source_priv"]
run_priv = ev["run_priv_at_completion"]
if src_priv < run_priv:
# Downgrade should have occurred but run_priv unchanged post-completion
print(f"[SUSPECT] run_id={ev['run_id']} "
f"priv_at_completion={run_priv} source_priv={src_priv} "
f"— downgrade missed, possible CVE-2026-43578 exploitation")
On patched deployments this condition should never occur. Any log entry matching the heuristic on a 2026.3.31 deployment is indicative of either exploitation or a misconfigured run that inadvertently benefits from the bug.
Remediation
Immediate: Upgrade to OpenClaw 2026.4.10 or later. No configuration workaround exists for the unpatched versions — the subscription gap is structural.
If upgrade is not immediately possible: Restrict the ability to submit FLAG_BG_ASYNC runs to trusted principals only, eliminating the attacker primitive at step 1 of the exploit chain. This is a containment measure, not a fix.
For operators running shared-runner deployments: Audit existing runs that completed between 2026.3.31 and 2026.4.10 for the privilege invariant violation described above. Any run that completed via the async path during this window should be treated as having executed at heartbeat owner privilege regardless of what the completion content's source privilege indicated.