Skip to content

[Bug]: /elevated allowlist can be bypassed by matching recipient (ctx.To) instead of sender #11022

@coygeek

Description

@coygeek

CVSS Assessment

Metric Value
Score 9.9 / 10.0
Severity Critical
Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

CVSS v3.1 Calculator

Summary

/elevated authorization accepts ctx.To as an identity token, so recipient identifiers can satisfy sender allowlists. In WhatsApp inbound DM flows, To is set to the bot's own number for each message, allowing non-owner senders to pass tools.elevated.allowFrom.whatsapp checks when that allowlist contains the owner/self number.

This elevates the session to full when /elevated full is accepted, and embedded-agent exec calls then bypass approval prompts (ask=off).

Affected Code

File: source_code/src/auto-reply/reply/reply-elevated.ts:103

addToken(params.ctx.SenderName);
addToken(params.ctx.SenderUsername);
addToken(params.ctx.SenderTag);
addToken(params.ctx.SenderE164);
addToken(params.ctx.From);
addToken(stripSenderPrefix(params.ctx.From));
addToken(params.ctx.To);
addToken(stripSenderPrefix(params.ctx.To));

File: source_code/src/web/inbound/monitor.ts:303

const inboundMessage: WebInboundMessage = {
  id,
  from,
  conversationId: from,
  to: selfE164 ?? "me",
  // ...
};

File: source_code/src/web/auto-reply/monitor/process-message.ts:279

const ctxPayload = finalizeInboundContext({
  // ...
  From: params.msg.from,
  To: params.msg.to,
  // ...
});

File: source_code/src/auto-reply/reply/get-reply-run.ts:395

bashElevated: {
  enabled: elevatedEnabled,
  allowed: elevatedAllowed,
  defaultLevel: resolvedElevatedLevel ?? "off",
},

File: source_code/src/agents/pi-embedded-runner/run/attempt.ts:210

createOpenClawCodingTools({
  exec: {
    ...params.execOverrides,
    elevated: params.bashElevated,
  },
  // ...
});

File: source_code/src/agents/bash-tools.exec.ts:946

const bypassApprovals = elevatedRequested && elevatedMode === "full";
if (bypassApprovals) {
  ask = "off";
}

Attack Surface

How is this reached?

  • Network (HTTP/WebSocket endpoint, API call)
  • Adjacent Network (same LAN, requires network proximity)
  • Local (local file, CLI argument, environment variable)
  • Physical (requires physical access to machine)

Authentication required?

  • None (unauthenticated/public access)
  • Low (any sender already authorized to issue commands, e.g. allowlisted/paired DM sender)
  • High (admin/privileged user only)

Entry point: Inbound channel message with /elevated on|ask|full directive (for example WhatsApp DM).

Exploit Conditions

Complexity:

  • Low (no race/timing required once configuration and sender access are present)
  • High (requires race condition, specific config, or timing)

User interaction:

  • None (automatic, no victim action needed)
  • Required (victim must click, visit, or perform action)

Prerequisites:

  • tools.elevated.enabled=true
  • tools.elevated.allowFrom.whatsapp includes the owner/self E.164
  • Attacker is already allowed to send command-capable inbound messages (for example paired/allowlisted sender)

Impact Assessment

Scope:

  • Unchanged (impact limited to vulnerable component)
  • Changed (can affect other components, escape sandbox)

What can an attacker do?

Impact Type Level Description
Confidentiality High Read arbitrary host data by running privileged exec flows after /elevated full.
Integrity High Execute host commands that modify files/config/state outside intended per-sender controls.
Availability High Disrupt service by running destructive or terminating commands without approval prompts.

Steps to Reproduce

  1. Configure WhatsApp inbound with at least two command-capable senders (owner + another sender), and set tools.elevated.allowFrom.whatsapp to the owner/self E.164.
  2. From the non-owner sender, send /elevated full.
  3. Observe the directive is accepted (Elevated mode set to full (auto-approve).).
  4. Send a normal message that causes the embedded agent to invoke exec (for example, ask it to run id or list files on host).
  5. Observe host exec runs without approval prompts because exec defaults now resolve to elevated full (ask=off).

Notes

!id / !ls go through the /bash command path, which currently creates a separate exec tool with defaultLevel: "on" (ask-level), so that specific command form is not the approval-bypass path for this bug.

Recommended Fix

Use only sender-authenticated identities for elevated authorization. Remove ctx.To (and its stripped variant) from the token set. Prefer stable sender IDs (SenderId, provider-native immutable IDs, E.164 where applicable) over mutable display names. Treat recipient/conversation fields as routing metadata, never authorization principals.

References

  • CWE: CWE-285 - Improper Authorization

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions