T09 · Insecure Skill Coding Practices
- Location
scripts/restore.mjs:16- Finding
Persisted Restart Command Is Executed Through an Unrestricted Shell
- Content
View full analysis
Vulnerability Details
File Location:
scripts/restore.mjs:16-19, 66-73;references/TESTING.md:108-112
Vulnerability Type: OS command injection through persisted configuration
Risk Level: HighVulnerable Code
js const SLOT = parseInt(process.argv[2] || '1', 10); const config = getConfig(); const manifest = getManifest(); const RESTART_CMD = config.restartCommand || 'kill -USR1 1';js // Run restart command let restartExit = 0; try { execSync(RESTART_CMD, { stdio: 'inherit', shell: '/bin/bash' }); } catch (e) { restartExit = e.status || 1; }The destructive testing instructions also execute the persisted value using
eval:bash RESTART_CMD=$(node -e "console.log(require('$HOME/.openclaw/rollback/rollback-config.json').restartCommand)") eval "$RESTART_CMD"Technical Analysis
The
restartCommandproperty is loaded fromrollback-config.jsonas unrestricted text and passed directly to Bash. The implementation does not validate the executable, separate arguments from the command, reject shell metacharacters, or restrict the command to supported restart mechanisms.Because Bash interprets operators such as semicolons, substitutions, redirections, and pipelines, a malicious value is not limited to restarting OpenClaw. For example, a value conceptually equivalent to a legitimate restart command followed by another shell command would execute both operations.
Setup documentation allows an alternative restart command to be supplied and persisted. The same persisted value can subsequently be invoked automatically after watchdog restoration, making exploitation possible without further confirmation at execution time.
Attack Path
- An attacker or unsafe setup workflow places a shell payload in the
restartCommandfield of~/.openclaw/rollback/rollback-config.json. - A config restore is initiated manually, or the watchdog expires and invokes ` ...[truncated 1078 chars]
- An attacker or unsafe setup workflow places a shell payload in the
- Remediation
View remediation
Remediation Suggestions
- Replace the free-form command string with a structured restart configuration containing a fixed method and validated arguments.
- Define an allowlist of supported methods, such as
systemd-user,systemd-system,docker-compose, andpid1-signal. - Invoke the selected executable using
spawnSyncorexecFileSyncwith an argument array andshell: false. - Validate service and container names against a strict pattern such as
^[A-Za-z0-9_.-]+$. - Reject shell operators, substitutions, newlines, redirections, and unrecognized executables during setup.
- Remove
evalfromreferences/TESTING.md; use the same structured restart helper used by the restore implementation. - Protect
rollback-config.jsonwith mode0600and verify that it is owned by the expected gateway account before use. - Log the selected restart method and argument array rather than an executable shell string.
