Back to skill

Security audit

Ipcam

Security checks for vulnerabilities and agentic risk

Overview

This camera-control skill appears purpose-aligned, but it handles camera passwords and surveillance media in ways users should review carefully before installing.

Install only if you are comfortable giving this skill access to camera credentials, live feeds, PTZ controls, local network discovery, and local storage of snapshots or recordings. Use a dedicated low-privilege camera account, avoid passing passwords with --pass, do not share stream-url output, restrict permissions on the config file, and review the unpinned dependency before running install.sh.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (5)

T08 · Insecure Dependencies

Warning
Location
install.sh:39
Finding

Unpinned Third-Party Package Installed from a Mutable Package Index

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
install.sh:47
Finding

Camera Credentials Stored Without Enforced Restrictive File Permissions

Content
View full analysis
"$CONFIG_FILE" <<'EOF' { "default": "cam1", "cameras": { "cam1": { "ip": "192.168.1.100", "username": "admin", "password": "YOUR_PASSWORD", "rtsp_port": 554, "onvif_port": 2020 } } } EOF echo " ✓ Config created: $CONFIG_FILE" echo " ⚠ Edit config to set your camera IP and password." fi ``` Discovery-created or updated configuration: ```python # Write config config_path.parent.mkdir(parents=True, exist_ok=True) with open(config_path, "w") as f: json.dump(config, f, indent=2) print(f"\n✓ Config saved: {config_path}") ``` Neither path explicitly applies owner-only permissions such as mode `0600`. ### Technical Analysis The configuration contains camera usernames and passwords in plaintext. Both the shell installer and Python discovery workflow rely on the process umask to determine the resulting file permissions. With a permissive umask, a newly created configuration may be readable by other local users. The code also does not inspect or correct permissions on an existing configuration. Because these credentials authorize RTSP viewing and ONVIF control, disclosure provides access to a sensitive physical-surveillance system. Plaintext storage may be functionally necessary for unattended operation, but it must be protected with strict filesystem permissions or an operating-system credential service. ### Attack Path 1. A user runs the installer or `ptz.py discover --add` in an environment with a permissive umask, or an existing configuration already has broad permissions. 2. The user enters or later adds valid camera credentials to `~/.config/ipcam/config.json`. 3. Another local account ...[truncated 715 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ptz.py:757
Finding

Camera Password Accepted Through Process Command-Line Arguments

Content
View full analysis
`. 2. The plaintext password becomes part of the shell command and the Python process argument vector. 3. A local observer, process-monitoring service, shell-history collector, or log collection system records the arguments. 4. The attacker retrieves the password from the recorded command or process metadata. 5. The attacker authenticates to the camera's RTSP or ONVIF service. ### Impact Assessment Disclosure grants the attacker the privileges of the affected camera account. Depending on camera configuration, this can include live-feed access, recordings, PTZ movement, pre ...[truncated 205 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/camera.sh:82
Finding

Credential-Bearing RTSP URLs Exposed Through Output and FFmpeg Arguments

Content
View full analysis
&1) || true ``` ```bash url="$(rtsp_url main)" echo "[$CAM_NAME] Capturing snapshot -> $output" if ffmpeg -y -rtsp_transport tcp -timeout "$RTSP_TIMEOUT_US" \ -i "$url" -frames:v 1 -q:v 2 \ ``` ```bash url="$(rtsp_url main)" echo "[$CAM_NAME] Recording ${duration}s -> $output" if ffmpeg -y -rtsp_transport tcp -timeout "$RTSP_TIMEOUT_US" \ -i "$url" -t "$duration" -c copy \ ``` The `stream-url` command prints the complete URL directly: ```bash stream-url|url) rtsp_url "${1:-main}" ;; ``` URL encoding protects syntax but does not redact or encrypt the password. Anyone who obtains the URL can decode and reuse it. ### Technical Analysis The `stream-url` command intentionally writes a reusable credential-bearing URL to standard output. That output may be copied into terminal logs, CI logs, chat transcripts, or other command pipelines. The `info`, `snapshot`, and `record` commands place the same URL in FFmpeg's argum ...[truncated 1362 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/ptz.py:545
Finding

ONVIF Stream URI Printed Without Redacting Embedded Credentials

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The code supports part of the description: RTSP capture/recording and basic camera info/connectivity checks. However, there is no ONVIF interaction, no PTZ commands, and no discovery mechanism such as network scanning or WS-Discovery. 'list-cameras' only lists cameras already present in a local config file, which is not camera discovery. The script primarily manages configured RTSP streams with ffmpeg, so the declared description materially overstates the implemented capabilities.

Content

No source excerpt is available for this finding.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
85% confidence
Finding

os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Content

Scanner excerpt · scripts/ptz.py (reported line 22)May include surrounding context.

python
VENV_PYTHON = VENV_DIR / "bin" / "python"

if VENV_PYTHON.exists() and not sys.prefix.startswith(str(VENV_DIR)):
    os.execv(str(VENV_PYTHON), [str(VENV_PYTHON)] + sys.argv)

# ── Config Loading ───────────────────────────────────────────────────────────

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill advertises installation and usage of shell scripts and Python tools that interact with the network, environment variables, and local config files, but it declares no explicit tool scope or permissions. That omission weakens user consent and sandboxing because the skill can scan the local network, access credentials from env/config, and write files without transparent capability declaration.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The documented discover --add workflow implies active network scanning and automatic modification of the user's camera config, yet the skill provides no warning or consent prompt. In this context, silent LAN scanning and persistent config changes are sensitive operations because they can expose device inventory, alter local state, and surprise users in managed or monitored environments.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill advertises snapshot and recording features without warning that it may capture, store, or expose surveillance footage. In the context of IP cameras, this is especially sensitive because recordings may contain private spaces, people, or security-relevant information, and users need clear notice about storage location, retention, and access implications.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · install.sh (reported line 22)May include surrounding context.

sh
if command -v brew &>/dev/null; then
    brew install ffmpeg
  elif command -v apt-get &>/dev/null; then
    sudo apt-get install -y ffmpeg
  else
    echo "  ✗ Please install ffmpeg manually." >&2; exit 1
  fi

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The stream-url command prints a full RTSP URL that embeds the camera username and password directly in stdout. This is dangerous because credentials can be exposed through terminal history, logs, screenshots, shell piping, or other tooling that captures command output; in the context of an IP camera skill, those credentials grant access to live video streams and potentially other camera functions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The discover --add path stores supplied camera usernames and passwords in plaintext JSON on disk, and the code does not set restrictive permissions or clearly warn the user about local secret storage. In the context of an IP camera control skill, these credentials grant access to surveillance devices, so local disclosure can directly compromise camera feeds and control functions.

Content

No source excerpt is available for this finding.

Tainted flow: 'config_path' from os.environ.get (line 579, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
86% confidence
Finding

The script allows IPCAM_CONFIG from the environment to fully control the path written during discover --add, then writes camera configuration and credentials to that location. If this script is launched by another tool, agent, or privileged context, an attacker who can influence the environment could redirect writes to arbitrary files the process can access, causing unauthorized file overwrite or credential leakage.

Content

Scanner excerpt · scripts/ptz.py (reported line 371)May include surrounding context.

python
# Write config
    config_path.parent.mkdir(parents=True, exist_ok=True)
    with open(config_path, "w") as f:
        json.dump(config, f, indent=2)
    print(f"\n✓ Config saved: {config_path}")

Tainted flow: 'config_path' from os.environ.get (line 579, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
86% confidence
Finding

This is the same issue in the stream-uri --save flow: config_path is derived from an environment variable and then opened for writing without validation. An attacker able to control the process environment could redirect the write to an unintended file, leading to file clobbering or exposure of camera-related configuration in sensitive locations.

Content

Scanner excerpt · scripts/ptz.py (reported line 595)May include surrounding context.

python
if len(uris) >= 2:
                cam_cfg["rtsp_sub_path"] = uris[1]["path"]

            with open(config_path, "w") as f:
                json.dump(config, f, indent=2)

            print(f"✓ Saved to config [{cam_name}]:")

Static analysis

No suspicious patterns detected.