T09 · Insecure Skill Coding Practices
- Location
SKILL.md:11- Finding
Arbitrary Shell Command Execution Through Sourced Configuration File
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 11–14
Vulnerability Type: Executable configuration file / unsafe shell sourcing
Risk Level: MediumVulnerable Code
bash CONTEXT_FILE="/opt/ocana/openclaw/workspace/skills/usage-costs/.context" [ -f "$CONTEXT_FILE" ] && source "$CONTEXT_FILE" # Provides: $OWNER_PHONE, $PRICING_INPUT, $PRICING_OUTPUT, $PRICING_CACHE_READTechnical Analysis
The skill instructs the agent to load configuration by passing
.contextto the shell built-insource. Unlike a data-only configuration parser,sourceinterprets every statement in the file as shell code in the current process. The file is outside the audited project artifact, and its contents, ownership, permissions, and integrity are not validated before execution.Consequently, any party that can modify
/opt/ocana/openclaw/workspace/skills/usage-costs/.contextcan place arbitrary shell commands in it. Those commands would execute when the skill performs its local-context initialization, with the same operating-system identity and privileges as the invoking OpenClaw process.The file is also expected to expose
OWNER_PHONE, although that personal value is not required by the documented cost-reporting workflow. Loading unnecessary sensitive data increases its accidental disclosure surface.Attack Path
- An attacker obtains write access to the external
.contextfile or its parent directory through a compromised account, another vulnerable component, or insecure filesystem permissions. - The attacker inserts shell commands into the file, for example:
bash PRICING_INPUT=3 arbitrary_attacker_command - A user invokes the usage-cost skill.
- The agent follows the initialization instructions and executes:
bash source "$CONTEXT_FILE" - The attacker's command runs in the agent's shell context without content validation or an allowlist.
- The command can access resources available to ...[truncated 1113 chars]
- An attacker obtains write access to the external
- Remediation
View remediation
Remediation Suggestions
- Do not execute configuration files with
source,.,eval, or equivalent shell mechanisms. - Store configuration in a data-only format such as JSON and parse only explicitly permitted fields.
- Allowlist the required pricing keys:
PRICING_INPUTPRICING_OUTPUTPRICING_CACHE_READ
- Validate each pricing value as a finite, non-negative numeric value before use.
- Remove
OWNER_PHONEfrom this skill's context because it is not needed for cost calculation. - Place the configuration in a directory that is not writable by untrusted users or unrelated services.
- Require restrictive ownership and permissions, and reject the file if they are unsafe.
- If shell-compatible configuration is unavoidable, parse simple
KEY=VALUErecords as text without executing them; reject substitutions, command delimiters, functions, redirections, and unknown keys. - Consider pinning and verifying the configuration file's expected path, owner, and integrity before parsing it.
A safer implementation should resemble:
python import json import math import os import stat path = "/opt/ocana/openclaw/workspace/skills/usage-costs/context.json" allowed = {"PRICING_INPUT", "PRICING_OUTPUT", "PRICING_CACHE_READ"} info = os.stat(path, follow_symlinks=False) if not stat.S_ISREG(info.st_mode): raise ValueError("Context path must be a regular file") if info.st_mode & (stat.S_IWGRP | stat.S_IWOTH): raise PermissionError("Context file has unsafe write permissions") with open(path, encoding="utf-8") as fh: raw = json.load(fh) if set(raw) - allowed: raise ValueError("Unknown context fields") pricing = {} for key in allowed: value = float(raw[key]) if not math.isfinite(value) or value < 0: raise ValueError(f"Invalid value for {key}") pricing[key] = value- Do not execute configuration files with
