T09 · Insecure Skill Coding Practices
- Location
scripts/download.py:18- Finding
Shell Command Injection Through Unsanitized Task Parameters
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill has a coherent background-download purpose, but its implementation can run attacker-controlled shell commands and write downloads to arbitrary local paths.
Review carefully before installing. Use only in a tightly controlled environment, with trusted inputs, and preferably after replacing shell=True command construction with argument lists and restricting downloads to approved HTTPS URLs and a dedicated download directory.
scripts/download.py:18Shell Command Injection Through Unsanitized Task Parameters
scripts/download.py:88Unrestricted URL Retrieval and Arbitrary Writable Destination
This is a classic tool-parameter abuse case: user-influenced parameters are funneled into a shell command that invokes the ontology tool. Since the skill stores attacker-controlled properties and later rebuilds shell commands around them, exploitation can occur during create, get, update, or query operations.
def run_ontology_cmd(cmd):
"""Run ontology command and return parsed JSON"""
full_cmd = f"python3 {ONTOLOGY_SCRIPT} {cmd}"
result = subprocess.run(full_cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print(f"Ontology command failed: {full_cmd}\n{result.stderr}", file=sys.stderr)
return None
The messaging tool is exposed to parameter abuse because untrusted task properties are inserted into a shell command line. An attacker controlling the stored channel or message can pivot from a notification action into arbitrary OS command execution under the agent's privileges.
channel_type = parts[0]
target = ":".join(parts[1:])
cmd = f"openclaw message send --channel {channel_type} --target {target} --message '{message}'"
result = subprocess.run(cmd, shell=True)
return result.returncode == 0
def do_download(task):
The curl invocation gives an attacker control over both the URL and output path inside a shell command, enabling parameter abuse and shell injection. In this skill context, which is explicitly designed to fetch attacker-chosen remote content in the background, the risk is amplified because exploitation can be triggered asynchronously and may be less visible to the user.
# Use curl with resume
cmd = f"curl -C - -L -o '{path}' '{url}'"
print(f"Running: {cmd}")
result = subprocess.run(cmd, shell=True)
return result.returncode == 0
def background_download(task_id):
The skill clearly relies on shell-capable execution (python3, curl/wget, and message-sending commands) but does not declare any tool scope or permission boundary. That omission weakens reviewability and least-privilege controls, making it easier for a caller or downstream agent to invoke filesystem and network-affecting behavior without an explicit security contract.
The skill advertises downloading arbitrary URLs to arbitrary local paths in the background and sending notifications to an original channel, yet it does not prominently warn about persistent filesystem writes or outbound network activity. In practice this can enable silent data ingress, overwriting sensitive files, storage abuse, or use as a delivery mechanism for untrusted binaries while reducing user visibility because the work continues asynchronously.
This constructs a shell command by concatenating untrusted data into full_cmd and executes it with shell=True. Inputs such as task fields later embedded into ontology subcommands can break quoting and trigger arbitrary command execution in the shell, making this a real command-injection risk.
def run_ontology_cmd(cmd):
"""Run ontology command and return parsed JSON"""
full_cmd = f"python3 {ONTOLOGY_SCRIPT} {cmd}"
result = subprocess.run(full_cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print(f"Ontology command failed: {full_cmd}\n{result.stderr}", file=sys.stderr)
return None
The code invokes openclaw message send to transmit notifications containing download details such as title, path, and on failure the URL, but there is no explicit warning that this metadata will be sent to an external channel. This is a privacy-relevant network operation that lacks clear disclosure in the code.
The notification command interpolates channel_type, target, and message directly into a shell command and runs it with shell=True. Because these values come from task data and may include quotes or shell metacharacters, an attacker can inject additional commands when a notification is sent.
channel_type = parts[0]
target = ":".join(parts[1:])
cmd = f"openclaw message send --channel {channel_type} --target {target} --message '{message}'"
result = subprocess.run(cmd, shell=True)
return result.returncode == 0
def do_download(task):
The skill will write downloaded content to any caller-supplied path and automatically creates parent directories, with no restriction to a workspace or safe base directory. In an agent setting, this can overwrite sensitive files, drop executables or config, or stage persistence through arbitrary file placement.
The download command embeds untrusted path and url values into a shell string and executes curl with shell=True. Even though values are wrapped in single quotes, an attacker can inject a single quote and shell payload, leading to arbitrary command execution or file-system abuse.
# Use curl with resume
cmd = f"curl -C - -L -o '{path}' '{url}'"
print(f"Running: {cmd}")
result = subprocess.run(cmd, shell=True)
return result.returncode == 0
def background_download(task_id):
The file includes a language-specific instruction line in Chinese, but there is no indication that the skill is region-specific or that users may choose their preferred language. Under the stated policy, forcing a specific language without opt-in can be a natural-language policy violation.
No suspicious patterns detected.