Back to skill

Security audit

Torrent Downloader

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent torrent-search and qBittorrent downloader, but it can automatically start downloads and uses under-scoped credentialed network access.

Install only if you are comfortable with torrent searching and qBittorrent automation. Before use, require explicit approval before any download, inspect candidate results, set dedicated qBittorrent credentials, keep QBT_URL on loopback or HTTPS, and avoid using the default admin/adminadmin values.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/qbt_download.py:10
Finding

qBittorrent Credentials Can Be Sent Over Unencrypted or Untrusted Connections

Content
View full analysis

Vulnerability Details

File Location: scripts/qbt_download.py:10-18 and scripts/qbt_download.py:65-68
Vulnerability Type: Plaintext credential transmission and insecure default credentials
Risk Level: High

The qBittorrent endpoint is configurable, defaults to plain HTTP, and is not validated before the script submits authentication credentials. Publicly known default credentials are also embedded in the code.

python
# Configuration via environment variables with defaults
QBT_URL = os.environ.get("QBT_URL", "http://localhost:8080")
QBT_USER = os.environ.get("QBT_USER", "admin")
QBT_PASS = os.environ.get("QBT_PASS", "adminadmin")


def login() -> str:
    """Login and return SID cookie."""
    data = urllib.parse.urlencode({"username": QBT_USER, "password": QBT_PASS}).encode()
    req = urllib.request.Request(f"{QBT_URL}/api/v2/auth/login", data=data)
    resp = urllib.request.urlopen(req, timeout=10)

The fallback authentication path repeats the same unsafe behavior:

python
except Exception:
    # Fallback: try with cookie processor
    data = urllib.parse.urlencode({"username": QBT_USER, "password": QBT_PASS}).encode()
    req = urllib.request.Request(f"{QBT_URL}/api/v2/auth/login", data=data)
    opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor())
    opener.open(req, timeout=10)

Technical Analysis

Authentication is necessary for the declared qBittorrent integration, but the implementation does not constrain where credentials may be sent. QBT_URL can identify an arbitrary HTTP or HTTPS host, and the script does not validate its scheme, hostname, port, or trust boundary.

When a non-loopback qBittorrent endpoint uses HTTP, the username and password are transmitted as URL-encoded POST data without transport encryption. Any attacker able to observe or modify traffic between the host and the endpoint may recover these credentials. If an attacker can influence the environment or deployment configurat ...[truncated 2475 chars]

Remediation
View remediation

Remediation Suggestions

  1. Require secure transport for remote endpoints

    • Parse QBT_URL with urllib.parse.urlparse.
    • Permit plain HTTP only when the normalized hostname is strictly loopback, such as localhost, 127.0.0.1, or ::1.
    • Require https:// for all non-loopback destinations.
    • Reject unsupported schemes, missing hostnames, embedded credentials, and malformed URLs.
  2. Remove embedded credential defaults

    • Do not default to admin or adminadmin.
    • Require QBT_USER and QBT_PASS to be explicitly configured.
    • Terminate with a clear error when either value is absent.
    • Avoid printing credentials in exceptions, logs, command output, or diagnostics.
  3. Use dedicated credentials

    • Create a dedicated qBittorrent account or equivalent restricted authentication context where supported.
    • Assign only the capabilities needed to inspect status and add torrents.
    • Do not reuse the qBittorrent password for other services.
    • Rotate credentials that may previously have crossed an unencrypted connection.
  4. Harden TLS

    • Retain Python's default certificate and hostname validation.
    • Do not introduce an unverified SSL context as a workaround for certificate errors.
    • Use a certificate trusted by the host running the Skill.
  5. Constrain endpoint configuration

    • Where remote qBittorrent access is unnecessary, reject all non-loopback hosts.
    • If remote access is required, optionally enforce an administrator-defined hostname allowlist.
    • Protect the process environment and Skill configuration from modification by untrusted users.
  6. Improve authentication validation

    • Confirm that the login response indicates success and that a valid session cookie was received before issuing API requests.
    • Make the fallback path apply the same URL validation and transport restrictions as the primary path.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (14)

Tainted flow: 'req' from os.environ.get (line 51, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The request URL is built from the untrusted QBT_URL environment variable and then used for an authenticated login request. Because the script also includes qBittorrent credentials in that request, a malicious or misconfigured environment can redirect the login to an attacker-controlled host, causing credential exfiltration or SSRF; the default use of plain HTTP increases this risk.

Content

Scanner excerpt · scripts/qbt_download.py (reported line 20)May include surrounding context.

python
"""Login and return SID cookie."""
    data = urllib.parse.urlencode({"username": QBT_USER, "password": QBT_PASS}).encode()
    req = urllib.request.Request(f"{QBT_URL}/api/v2/auth/login", data=data)
    resp = urllib.request.urlopen(req, timeout=10)
    for header in resp.headers.get_all("Set-Cookie") or []:
        if "SID=" in header:
            return header.split("SID=")[1].split(";")[0]

Tainted flow: 'req' from os.environ.get (line 51, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The add-torrent request is sent to a URL derived from QBT_URL without validation. While this call uses a session cookie rather than raw credentials, a malicious QBT_URL can still redirect requests and session state to an unintended server, enabling SSRF and possible token leakage after login.

Content

Scanner excerpt · scripts/qbt_download.py (reported line 44)May include surrounding context.

python
data=data,
        headers={"Cookie": f"SID={sid}"},
    )
    resp = urllib.request.urlopen(req, timeout=10)
    body = resp.read().decode()
    return {"status": "ok" if body.strip() == "Ok." else "error", "response": body.strip()}

Tainted flow: 'req' from os.environ.get (line 51, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The status/info request uses the same environment-controlled QBT_URL and authenticated cookie, allowing a hostile environment value to force outbound requests to arbitrary destinations. This can expose session information and makes the tool usable as an SSRF primitive inside the agent environment.

Content

Scanner excerpt · scripts/qbt_download.py (reported line 55)May include surrounding context.

python
f"{QBT_URL}/api/v2/torrents/info?sort={sort}&reverse=true&limit={limit}",
        headers={"Cookie": f"SID={sid}"},
    )
    resp = urllib.request.urlopen(req, timeout=10)
    return json.loads(resp.read())

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The implemented script is a thin qBittorrent client. It accepts an existing magnet URL from the command line, authenticates to qBittorrent, submits the magnet via the Web API, and optionally prints recent torrent status. There is no code for searching torrent indexes, discovering movie/TV resources, parsing metadata, evaluating subtitle availability, or ranking by resolution. The download portion is represented accurately, but the major claimed capabilities around search and automatic scoring/ranking are absent, making the description materially broader than the actual behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code does match the search-and-ranking portion of the description: it searches torrent metadata, builds magnet links, and scores results based on subtitle-related keywords and resolution preferences. However, the declared purpose explicitly says it will 'download via qBittorrent,' which is not implemented at all—there is no qBittorrent API call, subprocess launch, RPC interaction, or any download behavior. The script only returns ranked search results as JSON. Additionally, the implementation uses a single source (SolidTorrents), so any implication of broader multi-source behavior is unsupported. The primary mismatch is the missing download capability.

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The trigger list includes broad phrases like 'find resources' and colloquial Chinese phrases that can match ordinary conversation, causing the skill to activate in contexts where the user did not clearly request torrent acquisition. In this skill's context, accidental invocation is more dangerous because activation can lead directly to network searches and download initiation for potentially unlawful or unwanted content.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The workflow instructs automatic selection and download initiation without a confirmation step, which removes user control over a high-impact action involving network activity and local download infrastructure. In a torrenting context, this can trigger unauthorized downloads, legal exposure, bandwidth abuse, or retrieval of harmful or inappropriate content.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The agent behavior explicitly says to pick the highest-scored result, hide the result list, and start the download automatically. Hiding options and bypassing consent is dangerous because it prevents the user from validating content legitimacy, relevance, safety, or legality before the skill causes a real external side effect.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill invokes Python scripts that rely on environment variables and network access to search external torrent sources and interact with qBittorrent, but it declares no explicit tool scope or permissions. That omission weakens reviewability and can cause the agent to run network-capable actions without clear operator awareness or policy gating.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The workflow states that built-in Chinese subtitles are prioritized over other options, which imposes a language preference by default. There is no indication that the user can opt into or override this locale/language preference before ranking results.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The scoring policy explicitly boosts built-in Chinese subtitles above other subtitle options, reflecting a fixed language preference. The file does not offer a user language selection or explain a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script silently authenticates to qBittorrent using environment variables and insecure defaults of admin/adminadmin, with no user-facing warning that credentials will be transmitted over the network. In this skill context, that is more dangerous because the tool is designed to be triggered automatically for downloads, so it may send privileged local-service credentials without meaningful user awareness; combined with plain HTTP defaults, interception or redirection can expose them.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The top-level docstring states that the tool 'prefers Chinese built-in subtitles,' which imposes a language/locale preference by default. The file does not present this as an optional user choice or a clearly justified region-specific constraint.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script sends the user's raw search query to a third-party torrent index over the network without any explicit consent prompt, disclosure, or privacy notice. Because torrent searches can reveal sensitive user interests and may involve legally risky content, this creates a real privacy issue even though the request uses HTTPS and appears functionally necessary for the skill.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.