Back to skill

Security audit

BTC Monitor TalentverseX

Security checks for vulnerabilities and agentic risk

Overview

This BTC/ETH monitor is mostly coherent, but its optional cron installer can create unsafe persistent scheduled execution from unvalidated config.

Review before installing. Running the one-shot monitor is comparatively low risk, but avoid scripts/setup_cron.sh unless you trust and inspect config.json, understand the exact cron entry being installed, and are prepared to remove it manually. Enable Discord only if you are comfortable sending the generated market report to a configured Discord channel using a bot token from your environment.

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/setup_cron.sh:17
Finding
Unvalidated Cron Schedule Enables Persistent Command Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup_cron.sh:17-31` **Vulnerability Type**: Cron command injection through unvalidated configuration **Risk Level**: High ### Vulnerable Code ```bash SCHEDULE=$(python3 - <<PY import json from pathlib import Path config = json.loads(Path(r"$CONFIG_FILE").read_text(encoding="utf-8")) print(config.get("schedule", "0 8 * * *")) PY ) mkdir -p "$LOG_DIR" CRON_MARKER="# btc-monitor-skill" CRON_JOB="$SCHEDULE cd $ROOT_DIR && /usr/bin/env python3 scripts/monitor.py >> $LOG_DIR/monitor.log 2>&1 $CRON_MARKER" (crontab -l 2>/dev/null | grep -v "$CRON_MARKER" || true; echo "$CRON_JOB") | crontab - ``` ### Technical Analysis The script reads the `schedule` property from `config.json` and inserts it directly at the beginning of a crontab entry. It does not verify that the value: - Is a string containing only one line. - Contains exactly the expected cron scheduling fields. - Does not contain an embedded command after the scheduling fields. - Does not contain carriage returns, newline characters, NUL bytes, or other control characters. - Does not use cron directives or special syntax outside the supported schedule format. A crontab line consists of scheduling fields followed by an arbitrary shell command. Consequently, an attacker-controlled schedule can place a command immediately after valid scheduling fields, causing the intended monitor command to become additional shell syntax. Newline characters can also create entirely separate cron entries. For example, a malicious configuration could use a value conceptually equivalent to: ```json { "schedule": "* * * * * attacker_command #" } ``` After concatenation, the resulting entry would execute `attacker_command`, while the comment character prevents the intended monitoring command from affecting execution. A multiline value could install additional independent scheduled jobs. The exploit requires the attacker to influence `config.json` before a u ...[truncated 1731 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `schedule` to be a string and reject empty or excessively long values. 2. Reject carriage returns, newline characters, NUL bytes, and all other control characters. 3. Parse the schedule into an explicitly supported format rather than concatenating arbitrary text into a crontab line. 4. If only conventional five-field cron expressions are supported, require exactly five validated fields before constructing the command. 5. Validate each field against an allowlist of supported cron tokens and numeric ranges. Prefer a maintained cron-expression parser over a custom regular expression. 6. Reject cron directives such as `@reboot`, environment assignments, comments, and embedded command text unless deliberately supported. 7. Shell-quote `ROOT_DIR` and `LOG_DIR` when generating the command so spaces and shell metacharacters in paths cannot alter execution. 8. Write the generated entry using controlled formatting rather than interpolating an unrestricted configuration value. 9. Display the exact proposed cron entry and request explicit confirmation before installation. 10. Document and provide an uninstall command that removes the marked entry. A safer validation flow should fail closed before calling `crontab`, for example: ```python schedule = config.get("schedule", "0 8 * * *") if not isinstance(schedule, str): raise SystemExit("schedule must be a string") if any(character in schedule for character in ("\r", "\n", "\0")): raise SystemExit("schedule must contain exactly one line") fields = schedule.split() if len(fields) != 5: raise SystemExit("schedule must be a five-field cron expression") # Validate every field with a trusted cron parser before printing it. ``` The shell script should only install the entry after the validated parser returns a canonical schedule. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (17)

Tainted flow: 'headers' from os.getenv (line 504, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
def post_json(url: str, *, headers: dict[str, str], payload: dict[str, Any], timeout: int = DEFAULT_TIMEOUT) -> Any:
    response = requests.post(url, headers=headers, json=payload, timeout=timeout)
    response.raise_for_status()
    return response.json()
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises executable behavior that reads configuration files, accesses environment variables, and makes network requests, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an authorization gap: a host or reviewer cannot easily determine from the manifest what capabilities the skill expects, increasing the risk of unintended data access or outbound communication when the skill is executed.

Session Persistence

Medium
Category
Rogue Agent
Content
## Cron job does not run

- Run `crontab -l` and verify the installed entry
- Check `logs/monitor.log`
- Make sure the machine timezone matches your expected schedule
Confidence
85% 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.

External Transmission

Medium
Category
Data Exfiltration
Content
def post_json(url: str, *, headers: dict[str, str], payload: dict[str, Any], timeout: int = DEFAULT_TIMEOUT) -> Any:
    response = requests.post(url, headers=headers, json=payload, timeout=timeout)
    response.raise_for_status()
    return response.json()
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_binance_klines(symbol: str, interval: str, limit: int, timeout: int) -> list[list[Any]]:
    return request_json(
        "https://api.binance.com/api/v3/klines",
        params={"symbol": symbol, "interval": interval, "limit": limit},
        timeout=timeout,
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_bybit_klines(symbol: str, limit: int, timeout: int) -> list[list[Any]]:
    data = request_json(
        "https://api.bybit.com/v5/market/kline",
        params={"category": "linear", "symbol": symbol, "interval": "W", "limit": limit},
        timeout=timeout,
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_coingecko_weekly_klines(coin_id: str, limit: int, timeout: int) -> list[list[Any]]:
    days = 365
    data = request_json(
        f"https://api.coingecko.com/api/v3/coins/{coin_id}/market_chart",
        params={"vs_currency": "usd", "days": days, "interval": "daily"},
        timeout=timeout,
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_coingecko_weekly_klines(coin_id: str, limit: int, timeout: int) -> list[list[Any]]:
    days = 365
    data = request_json(
        f"https://api.coingecko.com/api/v3/coins/{coin_id}/market_chart",
        params={"vs_currency": "usd", "days": days, "interval": "daily"},
        timeout=timeout,
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_coingecko_weekly_klines(coin_id: str, limit: int, timeout: int) -> list[list[Any]]:
    days = 365
    data = request_json(
        f"https://api.coingecko.com/api/v3/coins/{coin_id}/market_chart",
        params={"vs_currency": "usd", "days": days, "interval": "daily"},
        timeout=timeout,
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_coingecko_weekly_klines(coin_id: str, limit: int, timeout: int) -> list[list[Any]]:
    days = 365
    data = request_json(
        f"https://api.coingecko.com/api/v3/coins/{coin_id}/market_chart",
        params={"vs_currency": "usd", "days": days, "interval": "daily"},
        timeout=timeout,
    )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_fear_greed_index(timeout: int) -> int | None:
    try:
        data = request_json("https://api.alternative.me/fng/", timeout=timeout)
    except requests.RequestException:
        return None
    entries = data.get("data") or []
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest emphasizes market monitoring with public API data and only briefly notes optional Discord delivery. In code, the skill not only reads public market data but can also authenticate with a secret token and perform outbound writes to a Discord channel, which is a materially broader behavior than passive monitoring.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
When enabled, the skill sends generated report content to Discord, an external third-party service, without any in-file consent prompt or prominent disclosure at send time. Even if the current report only contains market data, future configuration or report content could include sensitive operational details, and users may not realize that enabling the feature transmits data off-platform.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
A monitor based on public BTC/ETH market APIs is expected to fetch and analyze public data. Reading a bot token from the environment and using it to post authenticated messages adds a credential-handling and remote-write capability that goes beyond the core monitoring purpose unless clearly declared as part of scope.

Session Persistence

Medium
Category
Rogue Agent
Content
exit 1
fi

if ! command -v crontab >/dev/null 2>&1; then
  echo "crontab is required."
  exit 1
fi
Confidence
85% 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.

Session Persistence

Medium
Category
Rogue Agent
Content
CRON_MARKER="# btc-monitor-skill"
CRON_JOB="$SCHEDULE cd $ROOT_DIR && /usr/bin/env python3 scripts/monitor.py >> $LOG_DIR/monitor.log 2>&1 $CRON_MARKER"

(crontab -l 2>/dev/null | grep -v "$CRON_MARKER" || true; echo "$CRON_JOB") | crontab -

echo "Cron installed: $SCHEDULE"
echo "Log file: $LOG_DIR/monitor.log"
Confidence
95% confidence
Finding
This line installs a persistent cron entry for the current user, causing `scripts/monitor.py` to run on a recurring schedule from `config.json`. Although likely intended as normal functionality for a market-monitoring skill, persistence mechanisms are security-relevant because they survive the current session and can be abused if the monitored script, working directory, or configuration is later modified by an attacker.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Static analysis

No suspicious patterns detected.