high · 7.1Jul 2, 2026

CVE-2026-53831: openclaw system.run Safe-Bin Allowlist Bypass via Shell Expansion

Rohit Hatagale
AI Security Researcher, SecureLayer7

OpenClaw's safe-bin allowlist for system.run checked command arguments before the shell expanded them, so a glob or environment variable that looked harmless at check time could silently become a…

Packageopenclaw
Ecosystemnpm
Affected< 2026.5.18
Fixed in2026.5.18

The problem

On POSIX nodes, system.run runs commands via sh -c, which performs glob expansion, tilde expansion, and environment-variable substitution at execution time. The safe-bin allowlist evaluated argv tokens before that expansion happened.

An authenticated operator could pass a token like $HOME/.openclaw/config.json or * as an argument to an allowlisted stdin-only binary such as head or grep. The token cleared the allowlist check, then the shell expanded it into a real file path. The result was an arbitrary local file read without triggering any approval prompt.

Proof of concept

A working proof-of-concept for this issue in openclaw, with the exact payload below.

javascript
# Assume 'head' is in the safe-bin allowlist (intended as stdin-only).
# Pass a shell-expandable token as its argument.
# The allowlist sees the literal string and approves it.
# sh -c then expands it to the actual config path.

system.run(["head", "$HOME/.openclaw/config.json"])

# Glob variant - expands to all files in cwd:
system.run(["grep", "-r", "token", "*"])

The root cause is a time-of-check/time-of-use (TOCTOU) gap: the policy boundary was enforced on the pre-expansion string representation, while the OS saw the post-expansion result. CWE-78 (OS Command Injection via shell metacharacters) and CWE-284 (Improper Access Control) both apply because the shell acts as a second interpreter between the allowlist and the OS.

The fix requires either stripping or rejecting any token that contains shell-expandable characters ($, *, ~, backticks, etc.) before the allowlist check, or replacing sh -c with a no-shell exec path (e.g. Node's execFile with shell: false) so expansion never occurs.

The patched version in 2026.5.18 addresses the pre-expansion validation gap.

The fix

Upgrade to openclaw@2026.5.18 or later (npm install -g openclaw@latest). Before upgrading, avoid broad safe-bin auto-approval for commands that can accept file operands (head, tail, grep, cat, etc.), and restrict approvals to commands with fully literal, non-expandable argument sets.

Reported by Ellahi (@Ellahinator).

References: [1][2]

Related research