Back to skill

Security audit

ZFS

Security checks for vulnerabilities and agentic risk

Overview

This ZFS administration skill is mostly coherent, but its replication guidance grants broad passwordless SSH access and destructive ZFS permissions that users should review carefully.

Review the replication section before using it in production. Prefer restricted SSH keys, non-interactive accounts, per-replica keys, and only the ZFS permissions actually needed; add explicit checks before any rollback, destroy, forced receive, or automated pruning command.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
references/replication.md:220
Finding
Passwordless SSH Replication Credential Grants Broad Destructive Access<![CDATA[ ## Vulnerability Details **File Location**: `references/replication.md`, lines 220-232 **Vulnerability Type**: Excessive delegated permissions and insufficiently restricted passwordless SSH authentication **Risk Level**: Medium ### Vulnerable Code ```bash # On each replica host useradd -m -s /bin/bash zfsrepl # Grant only necessary ZFS permissions zfs allow -u zfsrepl create,mount,receive,destroy,rollback,hold,release backup ``` ```bash # On primary ssh-keygen -t ed25519 -f /root/.ssh/zfsrepl_key -N "" ssh-copy-id -i /root/.ssh/zfsrepl_key zfsrepl@replica1 ssh-copy-id -i /root/.ssh/zfsrepl_key zfsrepl@replica2 ``` ### Technical Analysis The guide creates a replication account with an interactive shell and delegates destructive ZFS permissions, including `destroy` and `rollback`. It then installs an SSH key with no passphrase through `ssh-copy-id`, which ordinarily adds an unrestricted entry to the remote account's `authorized_keys`. These capabilities are related to the declared replication functionality, so the behavior is not inherently malicious. However, the configuration does not enforce least privilege at the SSH boundary: - The account receives `/bin/bash` rather than a non-interactive or purpose-restricted command environment. - The SSH key is not restricted using an `authorized_keys` forced command. - No `from=` source-address restriction is applied. - Port forwarding, agent forwarding, X11 forwarding, PTY allocation, and arbitrary SSH commands are not explicitly disabled. - The delegated `destroy` and `rollback` permissions allow deletion of snapshots and reversal of destination data. - One private key is authorized on multiple replicas, increasing the blast radius of credential disclosure. The later replication example uses `zfs recv -F`, which can roll back or remove destination-side state: ```bash zfs send -Rw "$SNAP" | ssh -i "$SSH_KEY" "$host" "zfs recv -F $dataset" ``` Although `receive` is necessary for replication, destru ...[truncated 2039 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a non-interactive replication account where practical: ```bash useradd -m -s /usr/sbin/nologin zfsrepl ``` 2. Use a narrowly scoped forced-command wrapper that validates the permitted ZFS operation and destination dataset. Do not interpolate unvalidated dataset names into a shell command. 3. Restrict the public key in `authorized_keys`, for example: ```text from="192.0.2.10",restrict,command="/usr/local/sbin/zfs-receive-wrapper backup/data" ssh-ed25519 AAAA... ``` On systems without the `restrict` option, explicitly apply: ```text no-agent-forwarding,no-port-forwarding,no-X11-forwarding,no-pty ``` 4. Grant only the ZFS permissions needed by the selected workflow. Omit `destroy`, `rollback`, and `mount` unless forced receive or destination-side retention demonstrably requires them. 5. Prefer separate keys for each replica. This prevents disclosure of one key from compromising every backup target. 6. Protect private keys with strict ownership and mode: ```bash chown root:root /root/.ssh/zfsrepl_key chmod 600 /root/.ssh/zfsrepl_key ``` 7. Where unattended operation permits it, use an SSH agent, hardware-backed key, or tightly controlled secrets service instead of a permanently unencrypted private key. 8. Enable destination-side immutable retention, ZFS holds, snapshots inaccessible to the replication identity, or offline backup copies so compromise of the replication credential cannot erase every recovery point. 9. Document that `zfs recv -F` is destructive and require explicit confirmation or a verified common-snapshot check before enabling it. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/zfs_health_check.sh:121
Finding
Unquoted User-Controlled Pool Argument Permits ZFS Option Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/zfs_health_check.sh`, line 121 **Vulnerability Type**: Improper shell argument handling and command-option injection **Risk Level**: Low ### Vulnerable Code ```bash zfs list -o name,used,avail,refer,mountpoint -r ${POOL:-} 2>/dev/null | head -50 ``` The pool value originates directly from the script's first command-line argument: ```bash POOL="${1:-}" ``` ### Technical Analysis Most uses of the pool argument are correctly quoted, but the dataset-summary command expands `${POOL:-}` without quotes and without an end-of-options delimiter. Consequently, shell word splitting and pathname expansion are applied to the supplied value. An argument beginning with `-` can be interpreted by `zfs list` as an additional option rather than as a pool name. An argument containing whitespace can become multiple command arguments. Shell metacharacters embedded inside a variable are not reparsed as shell syntax, so this is not direct arbitrary shell-command execution. Nevertheless, the caller can modify the effective `zfs list` invocation, alter its output, broaden the queried scope, or trigger unintended errors. Because the script is intended as a read-only health check, the immediate effect is limited to confidentiality of ZFS metadata and integrity of the generated report. The flaw becomes more consequential if the script is wrapped by a privileged monitoring service that passes attacker-controlled pool values and exposes the output to a less-privileged requester. ### Attack Path 1. A privileged user, monitoring wrapper, or automation process invokes the script with an untrusted first argument. 2. The attacker supplies a value beginning with a valid `zfs list` option or containing whitespace-separated option operands. 3. The unquoted expansion causes the value to be split into multiple arguments. 4. `zfs list` interprets the injected tokens as command options or additional dataset operands. 5. The resulti ...[truncated 999 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Validate the pool argument as an existing ZFS pool and always pass it as one quoted argument: ```bash if [[ -n "$POOL" ]]; then if ! zpool list -H -o name -- "$POOL" >/dev/null 2>&1; then printf 'Invalid or unavailable ZFS pool: %s\n' "$POOL" >&2 exit 2 fi zfs list -o name,used,avail,refer,mountpoint -r -- "$POOL" \ 2>/dev/null | head -50 else zfs list -o name,used,avail,refer,mountpoint \ 2>/dev/null | head -50 fi ``` Additional hardening should include: 1. Reject pool values containing whitespace or control characters. 2. Use `--` where supported to terminate option parsing. 3. Keep every expansion of caller-controlled data quoted. 4. If the installed ZFS implementation does not support `--`, resolve the value against the exact output of `zpool list -H -o name` before invoking `zfs list`. 5. Do not expose this script through a privileged API or monitoring wrapper without strict input validation and output-access controls. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (10)

Vague Triggers

Medium
Confidence
91% confidence
Finding
The manifest description provides many specific examples, but then broadens activation to 'any other ZFS/zpool/zfs administration task.' In a manifest trigger description, this catch-all makes the activation boundary less explicit and increases the chance of unintended invocation for loosely related storage questions.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill presents `zfs rollback` and `zfs destroy` as straightforward examples without prominently warning that they are destructive and can irreversibly discard newer data or delete snapshots. In an administrative skill, users may copy commands directly, so omission of safety framing materially increases the risk of accidental data loss on production systems.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
sysctl kstat.zfs.misc.arcstats.size

# Set max ARC (not persistent — add to startup script)
sudo sysctl -w kstat.zfs.misc.arcstats.c_max=8589934592
```

## Key Behavioral Differences
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
# Linux: ZFS mounts datasets automatically at boot via zfs-mount.service
systemctl enable zfs-mount.service
systemctl enable zfs-import-cache.service

# macOS: ZFS mounts via LaunchDaemon, generally automatic
Confidence
80% 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
```bash
# Linux: ZFS mounts datasets automatically at boot via zfs-mount.service
systemctl enable zfs-mount.service
systemctl enable zfs-import-cache.service

# macOS: ZFS mounts via LaunchDaemon, generally automatic
Confidence
80% 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.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The guide includes rollback commands that destroy newer changes and, with `-r`, can also remove intermediate snapshots, but it does not present a prominent warning immediately around the example. In an administrative ZFS skill, users may copy commands directly, so omission of a clear caution materially increases the risk of accidental irreversible data loss.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The snapshot destruction examples permanently delete snapshots, including a range deletion, without a prominent caution about irreversibility or dependency implications. In backup/replication documentation, deleting the wrong snapshot can also break incremental replication chains in addition to causing data loss.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The example uses `zfs recv -F`, which can forcibly roll back and overwrite the target dataset to match the incoming stream, yet the example lacks a clear inline warning. Because this is replication guidance for real systems, a user could unintentionally destroy valid destination-side data by copying the command as shown.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The automated pruning script pipes snapshot names into `zfs destroy` with no embedded safeguards, dry-run mode, or explicit warning, making accidental mass deletion plausible if naming assumptions or dataset scope are wrong. Since the file targets operational automation, users are especially likely to deploy such scripts directly in production.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The guide presents `zfs rollback tank/data@last-good-snapshot` as a recovery step without an immediate explicit warning that rollback discards all newer changes in that dataset. In a troubleshooting context, operators may copy-paste commands under pressure, so omission of the destructive consequence increases the chance of accidental data loss.

Static analysis

No suspicious patterns detected.