Back to skill

Security audit

FRITZ!Box

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to control FRITZ!Box routers as advertised, but it needs review because it handles powerful router credentials and includes underprotected credential and release-script practices.

Install only if you are comfortable giving the skill credentials that can control your router and smarthome devices. Use a dedicated least-privilege FRITZ!Box account, avoid passing passwords on the command line, create and protect any .env file carefully, and treat WLAN/reconnect/smarthome actions as disruptive commands requiring explicit confirmation.

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)

T09 · Insecure Skill Coding Practices

Warning
Location
fritzbox.py:99
Finding
Authentication and Session Material Transmitted over Plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `fritzbox.py:99`, `fritzbox.py:166-167`, `fritzbox.py:207-213`, `fritzbox.py:302-303`, `fritzbox.py:320-321`, `fritzbox.py:370-371` **Vulnerability Type**: Plaintext transmission of authentication and session material **Risk Level**: Medium ### Vulnerable Code ```python self.base_url = f'http://{validated_ip}:49000' ``` ```python login_url = f'http://{self.host}/login_sid.lua?username={quote(self.user)}&response={response}' resp = requests.get(login_url, timeout=10) ``` ```python resp = requests.post( f'{self.base_url}{control_url}', data=soap, headers=headers, auth=self._auth, timeout=10 ) ``` ```python login_url = f'http://{self.host}/login_sid.lua?username={quote(self.user)}&response={response}' resp = requests.get(login_url, timeout=10) ``` ```python url = f'http://{self.host}/webservices/homeautoswitch.lua?switchcmd=getdevicelistinfos&sid={sid}' resp = requests.get(url, timeout=10) ``` ```python url = f'http://{self.host}/webservices/homeautoswitch.lua?switchcmd={cmd}&ain={quote(ain)}&sid={sid}' resp = requests.get(url, timeout=10) ``` ### Technical Analysis The implementation uses unencrypted HTTP for TR-064 authentication and Homeautoswitch API requests. The password is not directly submitted in the Web API login request; instead, a challenge response is generated. However, the resulting session ID is a bearer-like credential and is placed in subsequent request URLs. The private-address validation in `_validate_host()` is a meaningful control against sending credentials directly to a public Internet address. It does not provide confidentiality or server authenticity on the local network. A device with local traffic visibility, a compromised gateway, a malicious access point, or an attacker capable of ARP or DNS manipulation may observe or modify plaintext traffic. Putting the session ID in the query string also increases exposure through HTTP access logs, proxy logs, ne ...[truncated 1147 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use the FRITZ!Box HTTPS interface where supported and configure certificate validation appropriately. 2. Do not silently fall back from HTTPS to HTTP. 3. Avoid putting session identifiers in URLs where the router API permits another transport mechanism. 4. Explicitly disable use of environment-configured HTTP proxies for local router traffic, or validate proxy settings before transmitting authentication material. 5. Redact URLs, session IDs, challenge responses, usernames, and authentication headers from logs and error telemetry. 6. Use a dedicated FRITZ!Box account with only the permissions required for the selected operations. 7. Document the residual local-network interception risk if compatibility requires plaintext HTTP. 8. Consider binding the configured router to a known IP or validating its identity to reduce local spoofing risk. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
fritzbox.py:436
Finding
Router Password Accepted through Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `fritzbox.py:436`; documented usage in `SKILL.md:62` and `README.md:76-79` **Vulnerability Type**: Sensitive information exposed through process arguments and shell history **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument('--password', '-p', default=os.environ.get('FRITZBOX_PASSWORD', ''), help='Password') ``` The documented invocation encourages this behavior: ```bash python3 fritzbox.py --user admin --password YOURPASS wlan status ``` ```bash python3 fritzbox.py --user admin --password secret --host 192.168.178.1 info ``` ### Technical Analysis Command-line arguments are not an appropriate channel for long-lived router passwords. Depending on the operating system and process configuration, command arguments may be visible to other local users through process inspection utilities or process metadata. They may also be retained in shell history, terminal logging, automation logs, job definitions, monitoring systems, or support bundles. Because the documentation explicitly presents `--password` as a supported authentication method, users may disclose real router credentials even if a safer environment-file option is available. ### Attack Path 1. A user follows the documented example and supplies the router password with `--password`. 2. The command is recorded in shell history, terminal logs, automation output, or process metadata. 3. Another local user, administrator, monitoring agent, or later attacker obtains access to that record. 4. The attacker extracts the router username and password. 5. The attacker authenticates directly to the FRITZ!Box and exercises the account's assigned permissions. ### Impact Assessment The attacker obtains the same router privileges as the exposed account. This may include access to connected-device information, WLAN configuration, WAN reconnection, and smarthome device control. If an administrator account is used, the exposure could affect ...[truncated 158 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--password` option or mark it as deprecated and unsafe. 2. Add an interactive password prompt using Python's `getpass.getpass()` when no protected credential source is configured. 3. Prefer an operating-system credential store or a credential file outside the repository with permissions restricted to the owner. 4. Remove command-line password examples from `SKILL.md` and `README.md`. 5. If compatibility requires retaining the option, display a prominent warning and ensure automated logs redact its value. 6. Encourage a dedicated least-privilege FRITZ!Box account instead of an administrator account. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Python Runtime Dependency Is Not Reproducibly Pinned<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1` **Vulnerability Type**: Unbounded third-party dependency selection **Risk Level**: Low ### Vulnerable Code ```text requests>=2.28.0 ``` ### Technical Analysis The dependency declaration specifies only a minimum version. Every installation may therefore resolve to a different future release of `requests` and its transitive dependencies. The effective dependency set can change without a corresponding change to this repository or a review of the newly selected code. This does not establish that the current `requests` package is malicious. It is a supply-chain hardening deficiency because builds are not reproducible and no hashes are provided to verify downloaded artifacts. ### Attack Path 1. A maintainer or user runs `pip install -r requirements.txt`. 2. The package resolver selects the newest compatible package and transitive dependency versions available at that time. 3. A compromised, malicious, or unexpectedly incompatible future release is downloaded. 4. The package is imported by `fritzbox.py` and executes with the user's privileges. 5. The compromised dependency may access process environment variables, local files readable by the process, router credentials, and network resources. ### Impact Assessment A malicious dependency executes in the same Python process as the Skill. It could access `FRITZBOX_USER`, `FRITZBOX_PASSWORD`, `FRITZBOX_HOST`, loaded `.env` values, and router responses. It would also inherit the operating-system permissions of the user running the Skill. The practical likelihood is reduced by the use of a well-known package name from the normal Python ecosystem, but the lack of exact versions and integrity hashes prevents reproducible verification. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `requests` and relevant transitive dependencies to reviewed versions. 2. Generate a lockfile appropriate for the supported Python environments. 3. Use package hashes, such as a hash-locked requirements file generated with `pip-tools`. 4. Install from an explicitly trusted package index. 5. Add automated dependency vulnerability scanning and controlled update reviews. 6. Test dependency updates before modifying the lockfile rather than resolving unrestricted future versions during installation. ]]>

T08 · Insecure Dependencies

Warning
Location
tmp/publish.sh:37
Finding
Release Script Downloads and Executes an Unpinned Latest Package<![CDATA[ ## Vulnerability Details **File Location**: `tmp/publish.sh:37-42` **Vulnerability Type**: Dynamic execution of an unpinned package during publication **Risk Level**: Medium ### Vulnerable Code ```bash # Publish to clawhub npx clawhub@latest publish . \ --slug "$SLUG" \ --name "$NAME" \ --version "$VERSION" \ --changelog "$CHANGELOG" ``` ### Technical Analysis The release script invokes `npx clawhub@latest`. This may download and execute the latest package release at the time the script runs. Consequently, the code executed by the release process is not fixed by this repository and can change after audit without any local source modification. The command runs in a publication context where repository content, Git credentials, registry credentials, ClawHub tokens, environment variables, and developer filesystem permissions may be available. The preceding clean-working-tree check does not protect against a malicious or compromised package. ### Attack Path 1. An attacker compromises the package, its publisher account, or a dependency selected by `clawhub@latest`. 2. A maintainer invokes `tmp/publish.sh`. 3. `npx` downloads and executes the attacker-controlled or compromised release. 4. The package runs with the maintainer's privileges. 5. It may read source files and credentials, alter publication content, publish a malicious Skill version, or use available Git and registry authentication. ### Impact Assessment Exploitation affects the developer or release environment rather than ordinary runtime invocation of `fritzbox.py`. A compromised package could obtain all permissions available to the publishing user, including access to repository data, release tokens, Git credentials, and potentially unrelated files readable by that account. It could also compromise the integrity of the published Skill artifact. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `clawhub@latest` with an exact, reviewed version. 2. Record and verify the package lockfile and integrity hash. 3. Install the release tool in a controlled build environment before publication rather than dynamically resolving it in the release command. 4. Run publication in an isolated, least-privilege CI job with short-lived credentials. 5. Restrict release tokens to the specific package and required publication operation. 6. Review dependency updates separately before changing the pinned release-tool version. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:134
Finding
Documented Credential File Is Not Protected by the Claimed Git Ignore Rule<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:134-138`; related setup instructions in `INSTALL.md:20-31` **Vulnerability Type**: Risk of accidental credential disclosure through version control **Risk Level**: Medium ### Vulnerable Documentation ```markdown ## Security Guidance - **Least privilege:** Create a dedicated FRITZ!Box user with limited permissions for agent use rather than using the admin account. - **Protect credentials:** Keep `.env` outside version control (it is listed in `.gitignore`). Restrict file permissions: `chmod 600 .env`. - **Verify the host:** Ensure `FRITZBOX_HOST` points to your own local router. Do not route credentials through untrusted hosts. - **Scope of access:** The configured account can authorize all router and smarthome changes supported by this skill. Treat the credentials with the same care as router admin credentials. ``` The installation instructions direct the user to create the file inside the repository: ```bash cp .env.example .env # Edit .env with your FRITZ!Box credentials ``` ```bash FRITZBOX_USER=your_username FRITZBOX_PASSWORD=your_password FRITZBOX_HOST=fritz.box ``` ### Technical Analysis The project documentation claims that `.env` is listed in `.gitignore`, but the audited project structure contains no `.gitignore` file. Users are instructed to create `.env` in the repository and may reasonably rely on the stated ignore protection. The packaging script separately excludes `.env` from generated Skill archives, but that safeguard does not prevent `git add`, repository commits, source backups, or uploads performed by other tooling. ### Attack Path 1. A user follows the installation instructions and creates a repository-local `.env` containing real router credentials. 2. The user relies on the documentation's claim that the file is ignored. 3. Because the repository has no applicable `.gitignore`, the file appears as untracked content. 4. The user stages all files, commits the creden ...[truncated 685 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a repository-root `.gitignore` containing at least: ```gitignore .env *.env ``` Add explicit exceptions only for sanitized templates such as `.env.example`. 2. Add a test that fails if `.env` is not ignored by Git. 3. Store operational credentials outside the repository and reference them through a configurable path or an operating-system credential store. 4. Enforce owner-only file permissions such as mode `0600`. 5. Add pre-commit secret scanning and repository-level secret scanning. 6. If credentials have ever been committed, rotate them and remove them from repository history using an appropriate history-rewriting procedure. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (26)

Credential Access

High
Category
Privilege Escalation
Content
Copy the example environment file and fill in your credentials:

```bash
cp .env.example .env
# Edit .env with your FRITZ!Box credentials
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
Copy the example environment file and fill in your credentials:

```bash
cp .env.example .env
# Edit .env with your FRITZ!Box credentials
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
```bash
cp .env.example .env
# Edit .env with your FRITZ!Box credentials
```

Example `.env`:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
```bash
cp .env.example .env
# Edit .env with your FRITZ!Box credentials
```

Example `.env`:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Git inspection, tagging, pushing, and publishing are materially different from home router control and introduce supply-chain and source-code exposure risks. Hiding release automation inside a device-control skill makes the skill context more dangerous because users may authorize it expecting LAN-only device actions, not repository or publication operations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Git inspection, tagging, pushing, and publishing are materially different from home router control and introduce supply-chain and source-code exposure risks. Hiding release automation inside a device-control skill makes the skill context more dangerous because users may authorize it expecting LAN-only device actions, not repository or publication operations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Git inspection, tagging, pushing, and publishing are materially different from home router control and introduce supply-chain and source-code exposure risks. Hiding release automation inside a device-control skill makes the skill context more dangerous because users may authorize it expecting LAN-only device actions, not repository or publication operations.

Credential Access

High
Category
Privilege Escalation
Content
## Authentication

### Option 1: .env File (Recommended)

Create a `.env` file:
Confidence
76% confidence
Finding
The skill explicitly relies on credentials stored in environment variables or passed on the command line, which creates a real credential-handling risk. In this context the risk is heightened because FRITZ!Box credentials can authorize impactful router and smarthome changes, and command-line secrets may leak into shell history or process listings.

Credential Access

High
Category
Privilege Escalation
Content
import re
import os

# Try to load .env file
def load_env_file(path='.env'):
    """Load environment variables from .env file."""
    env_paths = [
Confidence
85% confidence
Finding
The skill automatically loads credentials from multiple .env locations at import time, including a hard-coded path in the user's home workspace. In an agent-skill context, implicit credential harvesting from local files is sensitive because it broadens access to secrets without explicit user action and could expose or misuse router credentials if the skill is invoked unexpectedly.

Credential Access

High
Category
Privilege Escalation
Content
import os

# Try to load .env file
def load_env_file(path='.env'):
    """Load environment variables from .env file."""
    env_paths = [
        path,
Confidence
85% confidence
Finding
The load_env_file helper is specifically designed to ingest .env content into process environment variables, which constitutes access to potentially sensitive credentials. In this skill, those credentials can control a router and smart-home devices, making implicit secret loading more dangerous than in a read-only utility.

Credential Access

High
Category
Privilege Escalation
Content
# Try to load .env file
def load_env_file(path='.env'):
    """Load environment variables from .env file."""
    env_paths = [
        path,
        os.path.join(os.path.dirname(__file__), '..', '.env'),
Confidence
84% confidence
Finding
Including a parent-directory .env path expands the trust boundary and may unintentionally pull credentials from unrelated project configuration. That increases the chance of accidental secret exposure or use of credentials the operator did not intend this skill to consume.

Credential Access

High
Category
Privilege Escalation
Content
"""Load environment variables from .env file."""
    env_paths = [
        path,
        os.path.join(os.path.dirname(__file__), '..', '.env'),
        os.path.expanduser('~/.openclaw/workspace-main/skills/fritzbox/.env')
    ]
Confidence
88% confidence
Finding
The hard-coded fallback to ~/.openclaw/workspace-main/skills/fritzbox/.env is especially concerning because it targets a specific user-local secrets location automatically. In an agent environment, hard-coded secret discovery paths are a red flag because they facilitate opportunistic credential access beyond what a user may expect from a single command invocation.

Credential Access

High
Category
Privilege Escalation
Content
env_paths = [
        path,
        os.path.join(os.path.dirname(__file__), '..', '.env'),
        os.path.expanduser('~/.openclaw/workspace-main/skills/fritzbox/.env')
    ]
    
    for env_path in env_paths:
Confidence
83% confidence
Finding
The loop over env_paths operationalizes secret discovery from multiple filesystem locations, increasing the attack surface for credential ingestion. While the apparent purpose is convenience, in a security review this is still a true secret-access concern because it silently imports credentials into runtime state.

Credential Access

High
Category
Privilege Escalation
Content
exclude = {
    'node_modules', '.git', '__pycache__', '.DS_Store',
    'coverage', '.vscode', '.idea', '*.pyc', '*.skill',
    '.env',
}

def should_exclude(rel_path: str) -> bool:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

Medium
Confidence
83% confidence
Finding
The documented commands include WAN reconnect and power/state-changing smarthome actions without warning that they can interrupt connectivity or alter physical device state. In the context of a router/smarthome control skill, this increases the chance of unintended denial of service, user lockout, or disruptive device behavior if commands are run mistakenly or automated unsafely.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README instructs users to pass credentials directly on the command line (`--user admin --password secret`), which can expose secrets through shell history, terminal logs, and process listings visible to other local users. For a router administration tool, disclosure of these credentials can enable unauthorized access to the FRITZ!Box and connected smarthome functions.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill declares access to sensitive environment variables and implies networked/router control, but it does not define an explicit permission or allowed-tools boundary. In an agent setting, missing scope declarations can lead to over-broad execution context and unintended access to credentials, files, or network functions beyond what users expect.

Vague Triggers

Medium
Confidence
96% confidence
Finding
Broad triggers like 'router', 'wlan', and 'wifi' can cause the skill to activate in many unrelated conversations. In this skill's context, accidental invocation is more dangerous than usual because the documented actions include disruptive network changes and smart-device power control.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
## Security Guidance

- **Least privilege:** Create a dedicated FRITZ!Box user with limited permissions for agent use rather than using the admin account.
- **Protect credentials:** Keep `.env` outside version control (it is listed in `.gitignore`). Restrict file permissions: `chmod 600 .env`.
- **Verify the host:** Ensure `FRITZBOX_HOST` points to your own local router. Do not route credentials through untrusted hosts.
- **Scope of access:** The configured account can authorize all router and smarthome changes supported by this skill. Treat the credentials with the same care as router admin credentials.
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Dynamic Request Target

Medium
Category
Server-Side Request Forgery
Content
"""Get authentication challenge from FRITZ!Box."""
        try:
            # Try to get challenge via login_sid.lua
            resp = requests.get(f'http://{self.host}/login_sid.lua', timeout=10)
            resp.raise_for_status()
            
            # Parse challenge from XML response
Confidence
60% confidence
Finding
Request target host is built from a dynamic or untrusted value. If the host is attacker-influenced, this enables SSRF to arbitrary internal or metadata endpoints.

Dynamic Request Target

Medium
Category
Server-Side Request Forgery
Content
"""Get authentication challenge from FRITZ!Box."""
        try:
            # Try to get challenge via login_sid.lua
            resp = requests.get(f'http://{self.host}/login_sid.lua', timeout=10)
            resp.raise_for_status()
            
            # Parse challenge from XML response
Confidence
60% confidence
Finding
Request target host is built from a dynamic or untrusted value. If the host is attacker-influenced, this enables SSRF to arbitrary internal or metadata endpoints.

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

Medium
Category
Data Flow
Content
try:
            login_url = f'http://{self.host}/login_sid.lua?username={quote(self.user)}&response={response}'
            resp = requests.get(login_url, timeout=10)
            resp.raise_for_status()
            
            # Parse SID from response
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: 'login_url' from requests.get (line 302, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
try:
            login_url = f'http://{self.host}/login_sid.lua?username={quote(self.user)}&response={response}'
            resp = requests.get(login_url, timeout=10)
            resp.raise_for_status()
            
            # Parse SID from response
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.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
The script executes `npx clawhub@latest publish`, which fetches and runs the latest package version at publish time rather than a pinned, reviewed version. This creates a supply-chain risk: a compromised upstream package, malicious new release, or unexpected breaking change could execute arbitrary code in the publisher's environment and potentially access repository credentials or modify release artifacts.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.28.0
Confidence
95% confidence
Finding
The dependency is specified as `requests>=2.28.0`, which allows any future version to be installed and makes builds non-reproducible. This can inadvertently introduce vulnerable or breaking releases into a router/smarthome control skill that likely performs network and authentication operations.

Static analysis

Detected: suspicious.env_credential_access

Python code POSTs credential environment variables to an environment-controlled URL.

Critical
Code
suspicious.env_credential_access
Location
fritzbox.py:207