Back to skill

Security audit

Telegram Cloud Storage

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to be a coherent Teldrive wrapper, but it installs an unverified external executable and handles powerful storage credentials with weak safeguards.

Install only if you are comfortable running an unverified prebuilt Teldrive binary from GitHub and giving the skill access to Telegram, database, JWT, and session credentials. Use a dedicated database user, strong new JWT secret, restrictive permissions on config files, avoid running as root, review the binary source/release yourself, and be careful with agent-triggered delete, upload, and download commands.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/install_binary.sh:2
Finding
External Teldrive binary is downloaded and installed without integrity verification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install_binary.sh:2-17` **Vulnerability Type**: Unverified remote payload retrieval and execution **Risk Level**: High ```bash # install_binary.sh - Portable Teldrive Downloader VERSION="1.8.0" SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" BIN_DIR="$SKILL_DIR/bin" TEMP_DIR="$SKILL_DIR/temp_install" mkdir -p "$BIN_DIR" mkdir -p "$TEMP_DIR" echo "Downloading Teldrive $VERSION..." URL="https://github.com/tgdrive/teldrive/releases/download/$VERSION/teldrive-$VERSION-linux-amd64.tar.gz" curl -L -o "$TEMP_DIR/teldrive.tar.gz" "$URL" tar -xzf "$TEMP_DIR/teldrive.tar.gz" -C "$TEMP_DIR" mv "$TEMP_DIR/teldrive" "$BIN_DIR/teldrive" chmod +x "$BIN_DIR/teldrive" ``` ### Technical Analysis The installer retrieves a precompiled executable archive from an external GitHub release and marks the extracted binary as executable without verifying a cryptographic checksum or signature. Although the URL belongs to the Teldrive repository identified in the Skill documentation and the version is pinned to `1.8.0`, version pinning alone does not establish artifact integrity. The effective payload can change if the release artifact, repository, maintainer account, redirect destination, or distribution infrastructure is compromised. The use of `curl -L` permits redirects, while the absence of `--fail` and shell fail-fast options means HTTP and extraction failures are not handled robustly. Archive entries are also not inspected before extraction. The installed payload is subsequently executed by `scripts/manage.sh`: ```bash nohup "$BIN" run --config "$CONFIG" > "$LOG_DIR/stdout.log" 2>&1 & ``` This behavior is necessary to install Teldrive, but downloading executable code without independent integrity verification exceeds the minimum safe trust model for that functionality. ### Attack Path 1. An attacker compromises the upstream release, maintainer account, artifact storage, or another trusted distrib ...[truncated 1234 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Publish a trusted SHA-256 digest for the exact release artifact and verify it before extraction: ```bash echo "$EXPECTED_SHA256 $TEMP_DIR/teldrive.tar.gz" | sha256sum --check - ``` 2. Prefer verification of a maintainer-signed checksum or release artifact using Sigstore, GPG, or another authenticated signing mechanism. 3. Add strict shell error handling: ```bash set -euo pipefail ``` 4. Harden the download command: ```bash curl --fail --show-error --location \ --proto '=https' --tlsv1.2 \ --output "$TEMP_DIR/teldrive.tar.gz" "$URL" ``` 5. Inspect archive entry names before extraction, reject absolute paths and `..` traversal components, and confirm that the expected executable is a regular file. 6. Use `mktemp -d` for the installation directory and remove it with an `EXIT` trap. 7. Install and run the binary as a dedicated unprivileged account with access only to the required configuration, database, network endpoints, and storage paths. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.sh:19
Finding
Telegram, database, and JWT secrets are collected visibly and stored without enforced restrictive permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:19-38` **Vulnerability Type**: Insecure secret entry and plaintext secret storage **Risk Level**: Medium ```bash echo "Enter your Telegram credentials (from my.telegram.org):" read -p "App ID: " APP_ID read -p "App Hash: " APP_HASH echo "Enter Database Connection String (PostgreSQL):" echo "Example: host=localhost user=postgres password=secret dbname=teldrive port=5432 sslmode=disable" read -p "DB Source: " DB_SOURCE read -p "Enter a random string for JWT Secret: " JWT_SECRET # Update config file # Using sed for simple replacements. Note: This is fragile if inputs contain special chars. # Escaping forward slashes in inputs for sed ESCAPED_DB_SOURCE=$(echo "$DB_SOURCE" | sed 's/\//\\\//g') sed -i "s/app-id = 0/app-id = $APP_ID/" "$CONFIG_FILE" sed -i "s/app-hash = \"\"/app-hash = \"$APP_HASH\"/" "$CONFIG_FILE" sed -i "s/data-source = \"\"/data-source = \"$ESCAPED_DB_SOURCE\"/" "$CONFIG_FILE" sed -i "s/secret = \"change-me-to-something-random\"/secret = \"$JWT_SECRET\"/" "$CONFIG_FILE" ``` ### Technical Analysis Sensitive values are read using ordinary `read -p`, which echoes input to the terminal. This affects the Telegram app hash, database connection string—which may contain a database password—and JWT signing secret. The values are then written in plaintext to `config/config.toml`. The script neither sets a restrictive `umask` nor explicitly applies mode `0600`. The resulting file permissions therefore depend on the caller's inherited umask. Under permissive settings, other local users or group members may be able to read the credentials. Plaintext configuration may be operationally required by Teldrive, but visible secret entry and failure to enforce least-privilege file permissions are unnecessary. ### Attack Path 1. A user runs `scripts/setup.sh` in a shared terminal, recorded shell session, support session, or environment observable by another party. 2. The app hash, datab ...[truncated 1109 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable terminal echo for sensitive inputs: ```bash read -r -s -p "App Hash: " APP_HASH echo read -r -s -p "DB Source: " DB_SOURCE echo read -r -s -p "JWT Secret: " JWT_SECRET echo ``` 2. Set restrictive permissions before creating the configuration: ```bash umask 077 cp "$TEMPLATE_FILE" "$CONFIG_FILE" chmod 600 "$CONFIG_FILE" ``` 3. Prefer environment variables, operating-system credential facilities, or a dedicated secret manager rather than storing long-lived credentials directly in the project directory. 4. If plaintext configuration is required, ensure the file owner is the dedicated Teldrive account and prevent group or world access. 5. Avoid placing secrets in command-line arguments, logs, error messages, or shell tracing output. 6. Document credential rotation procedures and recommend a dedicated, least-privileged PostgreSQL account for Teldrive. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.sh:29
Finding
Unescaped user input is interpolated into sed programs and TOML configuration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:29-38` **Vulnerability Type**: Configuration injection and unsafe input handling **Risk Level**: Medium ```bash # Update config file # Using sed for simple replacements. Note: This is fragile if inputs contain special chars. # Escaping forward slashes in inputs for sed ESCAPED_DB_SOURCE=$(echo "$DB_SOURCE" | sed 's/\//\\\//g') sed -i "s/app-id = 0/app-id = $APP_ID/" "$CONFIG_FILE" sed -i "s/app-hash = \"\"/app-hash = \"$APP_HASH\"/" "$CONFIG_FILE" sed -i "s/data-source = \"\"/data-source = \"$ESCAPED_DB_SOURCE\"/" "$CONFIG_FILE" sed -i "s/secret = \"change-me-to-something-random\"/secret = \"$JWT_SECRET\"/" "$CONFIG_FILE" ``` ### Technical Analysis The setup script interpolates user-controlled values directly into double-quoted `sed` expressions. Only forward slashes in `DB_SOURCE` are escaped. Other significant characters remain untreated: - `&` in a `sed` replacement expands to the complete matched text. - Backslashes can alter replacement semantics. - Delimiters in fields other than `DB_SOURCE` can terminate the intended expression. - Quotes and newlines can corrupt or extend the generated TOML structure. - `APP_ID` is inserted without validation as a numeric value. This is not a confirmed shell-command injection because data produced by parameter expansion is not automatically reparsed by the shell as shell syntax. It is, however, a configuration-generation vulnerability that may permit `sed` expression manipulation, malformed output, or injection of additional TOML settings when setup input is attacker-controlled. ### Attack Path 1. An attacker supplies a crafted database source, Telegram app hash, JWT secret, or app ID through an automated deployment process, copied setup instructions, or another input channel. 2. The victim enters or passes that value to `setup.sh`. 3. Unescaped replacement metacharacters, quotes, or newline characters alter the `sed` operation or resulting ...[truncated 1006 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace text substitution with a TOML-aware configuration generator that serializes strings and numeric values safely. 2. Strictly validate `APP_ID` as an integer: ```bash [[ "$APP_ID" =~ ^[0-9]+$ ]] || { echo "Invalid App ID" >&2 exit 1 } ``` 3. Reject newline, carriage-return, and other control characters in interactive values. 4. If `sed` must be retained, escape every replacement metacharacter—including backslashes, ampersands, and the selected delimiter—and separately escape values according to TOML string rules. 5. Write the generated configuration to a restrictive temporary file, parse or validate it, and atomically move it into place only after validation succeeds. 6. Run Teldrive's configuration validation mode, if available, before starting the server. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (16)

Missing User Warnings

High
Confidence
97% confidence
Finding
The delete command performs an irreversible remote deletion immediately based on a provided file ID, with no confirmation or dry-run option. In an agent skill context, where commands may be triggered indirectly or composed automatically, this raises the chance of accidental or unauthorized destructive actions.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill declares executable installation and operational scripts and clearly requires capabilities such as shell execution, environment access, file access, and likely network access, but it does not declare any explicit tool scope or permissions boundary. This increases the chance that an agent or platform will run the skill with broader privileges than necessary, making misuse or unintended side effects harder to constrain or review.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill instructs users to use a JWT token and a Telegram session hash, and even points to local storage locations where those secrets can be retrieved, but it does not prominently warn that these are sensitive authentication artifacts. Because these credentials can enable access to stored files and Telegram-backed sessions, inadequate disclosure raises the risk of accidental exposure, unsafe handling, or over-trusting downstream tools and logs.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The template ships with a JWT secret set to a known placeholder value, and there is no accompanying hard-fail or strong warning in the file itself to prevent deployments from using it unchanged. If an operator leaves this value in place, attackers who know or guess the default can forge valid JWTs, impersonate users, and potentially gain unauthorized access to the Telegram-backed storage service.

Tainted flow: 'url' from requests.get (line 83, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
def list_files(token, path="/"):
    url = f"{BASE_URL}/files"
    params = {"path": path, "page": 1, "order": "asc", "sort": "name"}
    res = requests.get(url, headers=headers(token), params=params)
    res.raise_for_status()
    return res.json()
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'url' from requests.get (line 83, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
def mkdir(token, path):
    url = f"{BASE_URL}/files/mkdir"
    payload = {"path": path}
    res = requests.post(url, headers=headers(token), json=payload)
    res.raise_for_status()
    return True
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'url' from requests.get (line 83, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
def mkdir(token, path):
    url = f"{BASE_URL}/files/mkdir"
    payload = {"path": path}
    res = requests.post(url, headers=headers(token), json=payload)
    res.raise_for_status()
    return True
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'url' from requests.get (line 83, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
def mkdir(token, path):
    url = f"{BASE_URL}/files/mkdir"
    payload = {"path": path}
    res = requests.post(url, headers=headers(token), json=payload)
    res.raise_for_status()
    return True
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The upload_file function reads a local file and sends its contents over HTTP via requests.post, but there is no confirmation prompt, warning print, or explanatory comment disclosing that local data will be transmitted. Because this operation moves user data off the local system, it should be explicitly disclosed to the user.

Tainted flow: 'url' from requests.get (line 83, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
h["Content-Type"] = "application/octet-stream"
    
    with open(local_path, "rb") as f:
        res = requests.post(url, headers=h, params=params, data=f)
    res.raise_for_status()
    
    url = f"{BASE_URL}/files"
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The download function writes server-supplied content to an arbitrary caller-provided local path with no validation, overwrite protection, or safety prompt. In an agent or automation context, this can overwrite sensitive files or place untrusted content in dangerous locations, increasing the risk beyond a simple CLI convenience issue.

Tainted flow: 'url' from requests.get (line 83, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
url = f"{BASE_URL}/files/{file_id}/{name}"
    params = {"download": "1", "hash": session_hash}
    
    with requests.get(url, params=params, stream=True) as r:
        r.raise_for_status()
        with open(local_path, "wb") as f:
            for chunk in r.iter_content(chunk_size=8192):
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'url' from requests.get (line 83, network input) → requests.patch (network output)

Medium
Category
Data Flow
Content
def rename_file(token, file_id, new_name):
    url = f"{BASE_URL}/files/{file_id}"
    payload = {"name": new_name}
    res = requests.patch(url, headers=headers(token), json=payload)
    res.raise_for_status()
    return res.json()
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The script downloads a prebuilt executable from the network and installs it locally without any integrity verification, signature check, checksum validation, or user confirmation. If the release asset, GitHub account, DNS/TLS path, or distribution channel is compromised, users may install and execute a malicious binary with the permissions of the invoking user.

Session Persistence

Medium
Category
Rogue Agent
Content
fi

        echo "Starting Teldrive..."
        nohup "$BIN" run --config "$CONFIG" > "$LOG_DIR/stdout.log" 2>&1 &
        echo $! > "$PID_FILE"
        echo "Started. PID: $(cat "$PID_FILE")"
        echo "Logs: $LOG_DIR/stdout.log"
Confidence
65% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The setup script prompts for sensitive values including the Telegram app hash, database connection string, and JWT secret using normal terminal input, which causes secrets to be echoed on screen and potentially exposed to shoulder-surfing, terminal logging, or shell session recording. It also writes those secrets directly into a local config file without warning about file permissions or storage risk, increasing the chance of credential disclosure on multi-user systems or poorly secured hosts.

Static analysis

No suspicious patterns detected.