Back to skill

Security audit

BT Download

Security checks for vulnerabilities and agentic risk

Overview

This BT download skill is mostly coherent, but it starts a persistent unauthenticated RPC service and contains unsafe shell execution paths that need review before installation.

Install only if you are comfortable with a Chinese-language BT tool that can install aria2, start a long-running download service, enable DHT/seeding, and read local .torrent files. Prefer a revised version that removes automatic sudo installation, starts aria2 without shell interpolation, binds RPC to localhost, uses an RPC secret, and provides explicit controls to stop the background service.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
plugin.ts:217
Finding
Shell Command Injection Through the Download Directory Parameter<![CDATA[ ## Vulnerability Details **File Location**: `plugin.ts`, lines 217-226 **Vulnerability Type**: OS command injection through unsafe shell interpolation **Risk Level**: Critical ### Vulnerable Code ```typescript const cmd = `nohup aria2c --enable-rpc --rpc-listen-all \ ${dhtArgs} \ --dir="${downloadDir}" \ --seed-ratio=${seedRatio} \ --seed-time=${seedTime} \ --bt-max-peers=50 \ --bt-seed-unverified=true \ > /tmp/aria2-rpc.log 2>&1 &`; exec(cmd, (err2) => { ``` ### Technical Analysis The `downloadDir` value can be supplied through the `bt_start_rpc` tool and is interpolated directly into a command string executed by `child_process.exec`. Although it is surrounded by double quotes, the value is not escaped or validated. Because `exec` invokes the command through a shell, a malicious value containing a double quote followed by shell metacharacters can terminate the `--dir` argument and append another command. Quoting the interpolated value is therefore insufficient to prevent command injection. The vulnerable operation runs with the same operating-system privileges as the plugin host. The input schema does not provide a security boundary and cannot replace contextual shell escaping or shell-free process execution. ### Attack Path 1. An attacker or untrusted agent invocation calls `bt_start_rpc`. 2. The attacker supplies a crafted `downloadDir` containing a closing quote and shell syntax. 3. The handler inserts the value into the `cmd` template without validation or escaping. 4. `child_process.exec` passes the resulting string to the operating-system shell. 5. The shell interprets the injected metacharacters and executes the appended command. 6. The injected command runs with the privileges of the OpenClaw/plugin process. ### Impact Assessment Successful exploitation permits arbitrary command execution under the plugin process's user account. Depending on that account's permissions, an attacker could: - Read, modify, or delete fi ...[truncated 554 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Replace `child_process.exec` with `spawn` or `execFile` and supply every aria2 option as a distinct argument, without invoking a shell. For example: ```typescript import { spawn } from "child_process"; const argumentsList = [ "--enable-rpc", "--no-rpc-listen-all", `--dir=${downloadDir}`, `--seed-ratio=${seedRatio}`, `--seed-time=${seedTime}`, "--bt-max-peers=50", "--bt-seed-unverified=true", ]; if (enableDht) { argumentsList.push("--enable-dht", "--enable-dht6"); } const child = spawn("aria2c", argumentsList, { detached: true, stdio: "ignore", }); child.unref(); ``` Additional hardening should include: 1. Require `downloadDir` to be an absolute filesystem path. 2. Resolve and normalize the path before use. 3. Restrict downloads to an explicitly approved base directory where appropriate. 4. Reject null bytes and paths that escape the approved directory. 5. Validate `seedRatio` and `seedTime` as finite numbers within safe ranges. 6. Do not implement shell redirection in a command string; configure `stdio` using Node.js process APIs. 7. Run aria2 and the plugin under a dedicated, unprivileged account with restricted filesystem access. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
plugin.ts:217
Finding
Unauthenticated aria2 RPC Service Exposed on All Network Interfaces<![CDATA[ ## Vulnerability Details **File Location**: `plugin.ts`, lines 217-224 **Vulnerability Type**: Unauthenticated network service exposure and broken access control **Risk Level**: High ### Vulnerable Code ```typescript const cmd = `nohup aria2c --enable-rpc --rpc-listen-all \ ${dhtArgs} \ --dir="${downloadDir}" \ --seed-ratio=${seedRatio} \ --seed-time=${seedTime} \ --bt-max-peers=50 \ --bt-seed-unverified=true \ > /tmp/aria2-rpc.log 2>&1 &`; ``` The RPC client is configured to use the local endpoint, but it supplies no authentication token: ```typescript const RPC_URL = "http://localhost:6800/jsonrpc"; async function rpcCall(method: string, params: any[] = []): Promise<any> { const { default: fetch } = await import("node-fetch"); const response = await fetch(RPC_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", method, params, id: Date.now(), }), }); const data = await response.json(); return data.result; } ``` ### Technical Analysis The aria2 process is started with `--rpc-listen-all`, which makes its JSON-RPC listener available through non-loopback network interfaces. The startup command does not configure `--rpc-secret`, and the RPC client does not send an aria2 `token:` authentication parameter. Consequently, hosts capable of reaching TCP port 6800 may invoke the exposed aria2 RPC API without authentication. Binding a service to all interfaces unnecessarily expands access beyond the local plugin and violates least privilege because the plugin itself only communicates with `localhost`. ### Attack Path 1. The user invokes `bt_start_rpc` on a host reachable by other systems. 2. The plugin starts aria2 with `--rpc-listen-all` and no RPC secret. 3. TCP port 6800 becomes reachable on the host's non-loopback interfaces unless an external firewall blocks it. 4. An attacker discovers or already knows the exposed port. 5. Th ...[truncated 1110 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Bind the RPC service to loopback unless remote access is an explicit, documented requirement: ```text --no-rpc-listen-all ``` The service should also require a cryptographically strong RPC secret: ```text --rpc-secret=<random-secret> ``` Apply the following hardening measures: 1. Generate the secret using a cryptographically secure random source. 2. Store the secret in a protected secret store or permission-restricted configuration file, not in source code or logs. 3. Include `token:<secret>` as the first parameter in every aria2 JSON-RPC request. 4. Avoid returning or logging the secret. 5. Restrict TCP port 6800 with host and network firewall rules. 6. If remote RPC access is necessary, place it behind an authenticated, encrypted proxy and allow only trusted clients. 7. Run aria2 under a dedicated, unprivileged account with access only to an approved download directory. 8. Verify service readiness through the authenticated RPC client rather than an unauthenticated `curl` command. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description frames the skill as a BT download helper, but the documented behavior includes installing software, invoking sudo apt-get, modifying aria2 configuration, reading local torrent files, fetching remote tracker lists, and starting a persistent RPC daemon. That mismatch is dangerous because users and policy systems may underestimate the privilege, persistence, and network effects of the skill, leading to consent bypass and execution of higher-risk actions than expected.

Description-Behavior Mismatch

High
Confidence
96% confidence
Finding
The skill’s stated purpose is download assistance, but it also performs system-level installation and service management. This expands its privilege and behavior far beyond a narrowly scoped downloader, creating unexpected host modification risk and increasing the chance an agent could make persistent system changes without informed user approval.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The tool runs 'sudo apt-get update && sudo apt-get install -y aria2', which performs privileged package management on the host. Allowing an agent skill to trigger root-level installation is dangerous because it changes the system state, may prompt for or rely on elevated privileges, and could be abused to install or chain additional software in environments where the skill is invoked automatically.

Chaining Abuse

High
Category
Tool Misuse
Content
const { exec } = await import("child_process");
        return new Promise((resolve) => {
          exec(
            "which aria2c || (sudo apt-get update && sudo apt-get install -y aria2)",
            (err, stdout, stderr) => {
              if (err) {
                resolve({ success: false, message: "安装失败: " + stderr });
Confidence
97% confidence
Finding
The chained shell expression 'which aria2c || (sudo apt-get update && sudo apt-get install -y aria2)' combines environment probing with privileged installation in one command. Chained admin actions reduce transparency, complicate auditing, and make it easier for an agent-triggered workflow to perform multiple sensitive operations automatically.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill advertises operational capabilities that require shell, network, and environment access, but it does not declare any explicit tool scope or permission boundaries. This is dangerous because a downloader that can install packages, start background services, and access local files expands the attack surface and makes unintended or unauthorized execution harder to govern.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The description and user-facing guidance are written entirely in Chinese, and the confirmation flow explicitly instructs the user to reply with Chinese text such as “回复「1」使用默认目录”. This creates a language/locale constraint without documenting user choice or opt-in, which matches the policy category for language policy violations.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The plugin's user-facing description, tool descriptions, prompts, and status messages are written in Chinese throughout the file. There is no indication that users can choose their language or that the Chinese-only behavior is a documented, justified locale constraint.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The plugin installs packages via sudo without a distinct user-facing warning or confirmation flow. Silent or implicit privileged changes are dangerous in agent settings because the user may believe they are only invoking a download helper, while the skill is actually modifying the operating system.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
const { exec } = await import("child_process");
        return new Promise((resolve) => {
          exec(
            "which aria2c || (sudo apt-get update && sudo apt-get install -y aria2)",
            (err, stdout, stderr) => {
              if (err) {
                resolve({ success: false, message: "安装失败: " + stderr });
Confidence
99% confidence
Finding
The presence of 'sudo' means the skill attempts privileged execution on the host. In an agent setting, privileged command paths are especially dangerous because they can alter the system broadly, may be combined with other behaviors, and exceed the expectations of a normal download assistant.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The tool starts a background RPC service, enables DHT, selects a download directory, and writes logs to /tmp without a strong disclosure/consent boundary. In an agent context, this can result in unintended persistence, network activity, and file writes that the user did not clearly authorize.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The code spawns shell commands and launches a background aria2 daemon with nohup, causing persistent local service execution outside the immediate tool invocation. This is risky because it creates a long-lived process, opens an RPC interface, and writes logs/files, all of which increase attack surface and can surprise users or be misused in shared environments.

External Transmission

Medium
Category
Data Exfiltration
Content
return new Promise((resolve) => {
          // 检查 RPC 是否已启动
          exec("curl -s http://localhost:6800/jsonrpc -d '{\"jsonrpc\":\"2.0\",\"method\":\"aria2.getVersion\",\"id\":1}'", async (err) => {
            if (!err) {
              // RPC 已启动,检测 DHT 状态
              if (enableDht) {
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Session Persistence

Medium
Category
Rogue Agent
Content
// 启动 RPC 服务,带 DHT 参数
            const dhtArgs = enableDht ? "--enable-dht --enable-dht6" : "";
            const cmd = `nohup aria2c --enable-rpc --rpc-listen-all \
              ${dhtArgs} \
              --dir="${downloadDir}" \
              --seed-ratio=${seedRatio} \
Confidence
95% confidence
Finding
Using nohup to launch aria2 creates session persistence beyond the lifetime of the immediate interaction. Persistent background processes are risky because they continue network activity and file operations after the user may believe the tool has finished, and they enlarge the local attack surface via the RPC service.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The plugin fetches tracker data from a third-party GitHub-hosted source without making that network disclosure obvious to the user. While not inherently malicious, it exposes user environments to external requests and introduces trust and integrity concerns around remote content used to influence BT behavior.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
plugin.ts:50

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
plugin.ts:183