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 file

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