T09 · Insecure Skill Coding Practices
- 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-18andscripts/qbt_download.py:65-68
Vulnerability Type: Plaintext credential transmission and insecure default credentials
Risk Level: HighThe 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_URLcan 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
-
Require secure transport for remote endpoints
- Parse
QBT_URLwithurllib.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.
- Parse
-
Remove embedded credential defaults
- Do not default to
adminoradminadmin. - Require
QBT_USERandQBT_PASSto be explicitly configured. - Terminate with a clear error when either value is absent.
- Avoid printing credentials in exceptions, logs, command output, or diagnostics.
- Do not default to
-
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.
-
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.
-
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.
-
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.
-
