Back to skill

Security audit

LAN Media Server

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real LAN file-sharing tool, but it installs a persistent unauthenticated server on all network interfaces with weak file-containment guarantees.

Install only if you intentionally want a persistent unauthenticated HTTP file share from this machine. Use a dedicated empty directory, do not place secrets there, avoid symlinks, restrict access with firewall or bind-address changes, and stop/disable the systemd user service when sharing is no longer needed.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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 (4)

T06 · System Persistence

Error
Location
scripts/setup.sh:23
Finding
Persistent Systemd User Service Installed and Enabled by Default<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:23-45` **Vulnerability Type**: Cross-session service persistence **Risk Level**: High ### Vulnerable Code ```bash # Create systemd user service mkdir -p "$SERVICE_DIR" cat > "$SERVICE_FILE" <<EOF [Unit] Description=LAN Media Server (port $PORT) After=network.target [Service] ExecStart=$NODE_BIN $SERVER_SCRIPT Restart=always RestartSec=3 Environment=NODE_ENV=production Environment="MEDIA_PORT=$PORT" Environment="MEDIA_ROOT=$MEDIA_ROOT" [Install] WantedBy=default.target EOF echo "📝 Created service: $SERVICE_FILE" # Enable and start systemctl --user daemon-reload systemctl --user enable media-server.service systemctl --user restart media-server.service ``` ### Technical Analysis The setup script creates a systemd user service under `~/.config/systemd/user`, configures it with `Restart=always`, and enables it under `default.target`. This causes the server to continue running after the original skill invocation and to restart following failures. If user lingering is enabled, it may also start and remain active without an interactive login. The behavior is disclosed in the documentation, but it is still a cross-session persistence mechanism. It also leaves a network-facing process active until the user explicitly disables it. Because `ExecStart` points directly to the skill's server script, later modification or replacement of that script changes what the persistent service executes. ### Attack Path 1. The user follows the documented instruction and runs `bash scripts/setup.sh`. 2. The script writes `media-server.service` into the user's systemd configuration. 3. It reloads systemd, enables the unit, and starts it immediately. 4. The service listens for network requests and is automatically restarted after termination or failure. 5. If the referenced script is subsequently altered, systemd executes the altered code when the service restarts. ### Impact Assessment The service execut ...[truncated 394 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Run the media server as an explicit, transient foreground process by default. - Do not enable the systemd service automatically during initial setup. - If persistent operation is necessary, require a separate opt-in command and clearly explain its lifetime and network exposure. - Consider using `systemd-run --user` with a limited runtime for temporary sharing sessions. - Add a documented uninstallation command that stops and disables the service, removes the unit file, and reloads systemd: ```bash systemctl --user disable --now media-server.service rm -f "$HOME/.config/systemd/user/media-server.service" systemctl --user daemon-reload ``` - Apply systemd sandboxing controls such as `NoNewPrivileges=yes`, `PrivateTmp=yes`, `ProtectSystem=strict`, and narrowly scoped `ReadOnlyPaths`/`ReadWritePaths`. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/server.js:37
Finding
Symbolic Link Escape Allows Files Outside MEDIA_ROOT to Be Served<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.js:37-53` **Vulnerability Type**: Path containment bypass through symbolic links **Risk Level**: High ### Vulnerable Code ```javascript const safePath = path.normalize(urlPath); const filePath = path.join(MEDIA_ROOT, safePath); // Block path traversal if (!filePath.startsWith(MEDIA_ROOT + path.sep) && filePath !== MEDIA_ROOT) { res.writeHead(403); res.end('Forbidden'); return; } // Block directory listing (root path returns simple status) if (filePath === MEDIA_ROOT || filePath === MEDIA_ROOT + '/') { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('LAN Media Server OK\n'); return; } fs.stat(filePath, (err, stats) => { if (err || !stats.isFile()) { res.writeHead(404); res.end('Not found'); return; } ``` The file is subsequently opened at `scripts/server.js:63`: ```javascript fs.createReadStream(filePath).pipe(res); ``` ### Technical Analysis The containment validation checks only the lexical form of `filePath`. It verifies that the normalized path string begins with `MEDIA_ROOT`, but it does not resolve symbolic links before performing the check. Both `fs.stat()` and `fs.createReadStream()` follow symbolic links. Consequently, a symbolic link located lexically inside `MEDIA_ROOT` can point to a regular file outside that directory. The prefix check accepts the link path, while the filesystem operations access and serve the external target. This contradicts the documented assertion that files must remain under `MEDIA_ROOT`. ### Attack Path 1. A symbolic link is created inside the shared directory, for example: ```bash ln -s "$HOME/.ssh/id_rsa" "$HOME/projects/shared-media/leak.txt" ``` 2. A network client sends `GET /leak.txt` to the server. 3. The normalized path remains lexically beneath `MEDIA_ROOT`, so the prefix check succeeds. 4. `fs.stat()` follows the symbolic link and observes a regular file. 5. `fs.createReadStream()` follows the same link ...[truncated 759 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Resolve `MEDIA_ROOT` to its canonical path once with `fs.realpath`. - Resolve each requested file with `fs.realpath` and perform the containment check against canonical paths. - Require the resolved target to begin with `canonicalRoot + path.sep`. - Reject symbolic links explicitly with `fs.lstat()` if symlink sharing is not required. - Where supported, open files using no-follow semantics to reduce time-of-check/time-of-use risks. - Validate the opened file descriptor rather than relying solely on a path checked before opening. - Return a generic denial response without disclosing filesystem details. A canonical containment check should follow this pattern: ```javascript fs.realpath(filePath, (err, resolvedFile) => { if ( err || (!resolvedFile.startsWith(resolvedRoot + path.sep) && resolvedFile !== resolvedRoot) ) { res.writeHead(403); res.end('Forbidden'); return; } // Open and serve the validated canonical target. }); ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/server.js:65
Finding
Unauthenticated File Server Listens on All Network Interfaces<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.js:65-68` **Vulnerability Type**: Unrestricted network exposure and missing authentication **Risk Level**: High ### Vulnerable Code ```javascript server.listen(PORT, '0.0.0.0', () => { console.log(`LAN Media Server running on http://0.0.0.0:${PORT}`); console.log(`Serving: ${MEDIA_ROOT}`); }); ``` The absence of authentication is explicitly documented in `SKILL.md:65-68`: ```markdown ## Security Notes - Serves files only on LAN (0.0.0.0 but typically behind NAT) - No authentication — don't put sensitive files in the shared directory - Path traversal is blocked (files must be under MEDIA_ROOT) - No directory listing — must know the exact filename ``` ### Technical Analysis Binding to `0.0.0.0` exposes the service through every IPv4 interface available to the host. It does not technically enforce LAN-only access. Depending on firewall rules, cloud security groups, router port forwarding, VPN routing, container networking, or host placement, the service may be reachable from untrusted networks or the public Internet. The server does not authenticate requests or authorize access to individual files. The absence of directory listing only obscures filenames; it is not an access-control mechanism. Filenames can be guessed, disclosed through logs or messages, or discovered through predictable naming conventions. The server also uses plaintext HTTP, so file contents and requested paths can be observed or modified by an on-path network attacker. ### Attack Path 1. The setup script starts the server on TCP port `18801` or a configured alternative. 2. The listener accepts connections through all host IPv4 interfaces. 3. An attacker reaches the port through a local network, VPN, forwarded port, permissive firewall, or public interface. 4. The attacker obtains or guesses a shared filename. 5. The attacker requests `http://host:18801/<filename>`. 6. The server returns the file without reques ...[truncated 568 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Bind to `127.0.0.1` by default. - Require an explicit, validated bind-address option before exposing the service to a LAN. - Add authentication, preferably using cryptographically random, short-lived, per-file share tokens. - Expire links and revoke them after use where practical. - Use HTTPS when files may traverse untrusted networks. - Enforce host firewall rules that restrict access to explicitly trusted subnets. - Generate unpredictable filenames rather than relying on descriptive or sequential names. - Apply request rate limits and access logging to support abuse detection. - Update documentation to clarify that `0.0.0.0` does not guarantee LAN-only reachability. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.sh:4
Finding
Unvalidated Environment Values Are Interpolated into a Systemd Unit<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:4-10,23-37` **Vulnerability Type**: Systemd unit configuration injection **Risk Level**: Medium ### Vulnerable Code ```bash PORT="${MEDIA_PORT:-18801}" MEDIA_ROOT="${MEDIA_ROOT:-$HOME/projects/shared-media}" SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)" SERVER_SCRIPT="$SKILL_DIR/scripts/server.js" SERVICE_DIR="$HOME/.config/systemd/user" SERVICE_FILE="$SERVICE_DIR/media-server.service" # Detect node NODE_BIN="$(command -v node 2>/dev/null || echo '/usr/bin/node')" ``` ```bash # Create systemd user service mkdir -p "$SERVICE_DIR" cat > "$SERVICE_FILE" <<EOF [Unit] Description=LAN Media Server (port $PORT) After=network.target [Service] ExecStart=$NODE_BIN $SERVER_SCRIPT Restart=always RestartSec=3 Environment=NODE_ENV=production Environment="MEDIA_PORT=$PORT" Environment="MEDIA_ROOT=$MEDIA_ROOT" [Install] WantedBy=default.target EOF ``` ### Technical Analysis `MEDIA_PORT` and `MEDIA_ROOT` are accepted from the inherited process environment and inserted directly into a systemd unit through an unquoted heredoc. No validation rejects newline characters, quotes, backslashes, or malformed values. A newline in either value can terminate the intended systemd directive and add another directive to the generated unit. Quotes and backslashes may also change systemd's parsing of `Environment=` assignments. Additionally, `NODE_BIN` and `SERVER_SCRIPT` are placed in `ExecStart` without systemd-compatible path quoting, so spaces or special characters in installation paths can break argument parsing or redirect execution behavior. Exploitation requires influence over the environment used when the victim runs the setup script. This can occur through a malicious wrapper, copied shell command, compromised parent process, or automated installation environment. ### Attack Path 1. An attacker causes setup to run with a crafted environment variable containing a newline and additional systemd unit syn ...[truncated 937 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Validate `MEDIA_PORT` as an integer from `1` through `65535` before using it. - Reject carriage returns, line feeds, NUL characters, and other control characters in all values written into the unit. - Canonicalize `MEDIA_ROOT` and verify that it is an acceptable absolute path. - Generate systemd values using a dedicated escaping function rather than direct heredoc interpolation. - Properly quote executable and argument paths according to systemd unit syntax. - Prefer a fixed executable wrapper whose configuration is read from a separately generated, permission-restricted file. - Write the unit atomically to a temporary file, validate it with `systemd-analyze verify`, and only then replace the active unit. - Ensure the generated service file is owned by the current user and is not writable by other users. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (4)

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill instructs the agent to run setup and management commands, use environment-variable-controlled configuration, and operate a network-accessible file server, but it declares no explicit tool scope or permissions metadata. That mismatch can cause the platform to under-constrain what the skill is allowed to do, increasing the chance of unintended command execution, environment access, or network exposure without clear review boundaries.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The server binds to 0.0.0.0, making all files under MEDIA_ROOT reachable by any host on the local network without authentication, authorization, or per-file access controls. In this skill’s context, that is the core feature, but it still creates a real exposure: screenshots, documents, and other workspace artifacts may be unintentionally disclosed to anyone on the LAN who can guess or obtain the URL.

Session Persistence

Medium
Category
Rogue Agent
Content
# Enable and start
systemctl --user daemon-reload
systemctl --user enable media-server.service
systemctl --user restart media-server.service

echo ""
Confidence
94% confidence
Finding
The script installs and enables a persistent systemd user service that automatically restarts and survives future sessions, which creates session persistence on the host. In the context of a LAN-accessible file server, this increases exposure because a network service continues running beyond the immediate task and may keep sharing workspace files until explicitly disabled.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
if ! loginctl show-user "$(whoami)" 2>/dev/null | grep -q 'Linger=yes'; then
  echo ""
  echo "⚠️  User lingering not enabled. Run this to survive reboots:"
  echo "   sudo loginctl enable-linger $(whoami)"
fi
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Static analysis

No suspicious patterns detected.