T09 · Insecure Skill Coding Practices
- Location
fritzbox.py:99- Finding
Authentication and Session Material Transmitted over Plaintext HTTP
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
fritzbox.py:99Authentication and Session Material Transmitted over Plaintext HTTP
fritzbox.py:436Router Password Accepted through Command-Line Arguments
requirements.txt:1Python Runtime Dependency Is Not Reproducibly Pinned
tmp/publish.sh:37Release Script Downloads and Executes an Unpinned Latest Package
SKILL.md:134Documented Credential File Is Not Protected by the Claimed Git Ignore Rule
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Copy the example environment file and fill in your credentials:
cp .env.example .env
# Edit .env with your FRITZ!Box credentials
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Copy the example environment file and fill in your credentials:
cp .env.example .env
# Edit .env with your FRITZ!Box credentials
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
cp .env.example .env
# Edit .env with your FRITZ!Box credentials
Example .env:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
cp .env.example .env
# Edit .env with your FRITZ!Box credentials
Example .env:
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.
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.
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.
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.
## Authentication
### Option 1: .env File (Recommended)
Create a `.env` file:
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.
import re
import os
# Try to load .env file
def load_env_file(path='.env'):
"""Load environment variables from .env file."""
env_paths = [
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.
import os
# Try to load .env file
def load_env_file(path='.env'):
"""Load environment variables from .env file."""
env_paths = [
path,
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.
# 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'),
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.
"""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')
]
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.
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:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
exclude = {
'node_modules', '.git', '__pycache__', '.DS_Store',
'coverage', '.vscode', '.idea', '*.pyc', '*.skill',
'.env',
}
def should_exclude(rel_path: str) -> bool:
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.
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.
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.
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.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
## 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.
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.
"""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
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.
"""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
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
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
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
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
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.
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.
requests>=2.28.0