Back to skill

Security audit

Reolink Remote Backup

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its camera-backup purpose, but it includes root-level setup and persistence steps with unsafe scoping that users should review carefully.

Install only if you are comfortable reviewing and running root-level VPS setup scripts. Before use, validate the retention-days input, avoid removing SSH forced-command restrictions unless replaced with a constrained wrapper, consider a non-login shell or separate pull user, and back up existing vsftpd/UFW configuration.

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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/setup_vps_retention.sh:7
Finding

Root Cron Command Injection Through an Unvalidated Retention Argument

Content
View full analysis
/dev/null | grep -v '/srv/reolink/incoming -type f -mtime' ; \ echo "30 3 * * * find /srv/reolink/incoming -type f -mtime +${DAYS} -delete" ) | crontab - ``` ### Technical Analysis The script is intended to run with `sudo` and therefore normally modifies root's crontab. The user-controlled `DAYS` argument is interpolated directly into a cron command without verifying that it is a positive integer. Although the variable is quoted while being passed to `echo`, quoting only protects the setup script's current shell. The generated text is subsequently stored in a crontab and interpreted by `/bin/sh` when cron executes it. Shell metacharacters embedded in `DAYS`, including semicolons, command substitutions, comments, or newline characters, therefore become executable syntax in the persistent root cron job. For example, a value conceptually shaped like the following would add a second shell operation to the generated cron command: ```text 30; # ``` The resulting cron entry would resemble: ```cron 30 3 * * * find /srv/reolink/incoming -type f -mtime +30; # -delete ``` The scheduled attacker command would then execute with the privileges of the crontab owner, normally root. ### Attack Path 1. An attacker influences the argument passed as the retention period, such as through an administrative wrapper, copied command, automation variable, or social-engineering instruction. 2. An administrator invokes the documented command with `sudo`. 3. The script accepts the malicious value without validation. 4. The value is incorporated into a root crontab entry as shell syntax. 5. At 03:30, cron executes the injected operation as root. 6. Because the entry persists in root's ...[truncated 577 chars]
Remediation
View remediation
3650 )); then echo "ERROR: retention days must be an integer between 1 and 3650" >&2 exit 1 fi ``` Additional hardening should include: 1. Prefer a fixed root-owned cleanup script whose arguments are validated internally, with cron invoking only that fixed path. 2. Use a systemd service and timer with a static `ExecStart` instead of dynamically generating shell commands. 3. Install the cron entry in `/etc/cron.d/` as a root-owned file with restrictive permissions rather than rewriting the invoking user's entire crontab. 4. Verify the generated configuration before installation. 5. Avoid printing the complete root crontab, since unrelated entries may contain sensitive operational information. 6. Provide an explicit uninstall procedure for the retention schedule. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/troubleshooting.md:80
Finding

Removal of SSH Forced-Command Restriction Exposes an Interactive Shell

Content
View full analysis
/dev/null 2>&1 || useradd -m -d "$FTP_ROOT" -s /bin/bash "$FTP_USER" ``` The troubleshooting guide recommends removing an SSH forced command: ```markdown This happens when a **forced rsync command** is set in `authorized_keys` and the flags in that command don't match what the client negotiates. Do **not** use a forced rsync command for this setup. Instead, use restriction flags only: ``` no-pty,no-agent-forwarding,no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAA... ``` Remove a forced command with: ```bash sed -i 's|command="rsync[^"]*",||' /srv/reolink/.ssh/authorized_keys ``` The `reolinkftp` user is already restricted (no sudo, home-dir only) so the forced command provides little extra security while breaking rsync negotiation. ``` ### Technical Analysis The statement that the account is “home-dir only” is inaccurate for SSH access. The `chroot_local_user=YES` setting in `vsftpd.conf` confines FTP sessions handled by vsftpd; it does not chroot sessions authenticated through OpenSSH. The account is explicitly assigned `/bin/bash`. Removing the `command="..."` option from `authorized_keys` therefore allows the corresponding key to request arbitrary SSH commands. Options such as `no-pty`, `no-agent-forwarding`, and `no-port-forwarding` reduce selected SSH features, but they do not prevent non-interactive command execution. A client can still use SSH to invoke shell commands directly. The generated pull script also runs an SSH command to delete empty directories, so some remote command capability is required by the current design. That requirement does not justify unrestrict ...[truncated 2047 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (22)

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The guidance explicitly recommends removing a forced command restriction from authorized_keys because it interferes with rsync negotiation. Even with some restriction flags retained, relaxing key restrictions increases the capability of anyone holding that SSH key and broadens post-compromise options from a narrowly constrained rsync action to a more general account login within the user's shell/environment.

Content

Scanner excerpt · references/troubleshooting.md (reported line 91)May include surrounding context.

Remove a forced command with:

bash
sed -i 's|command="rsync[^"]*",||' /srv/reolink/.ssh/authorized_keys

The reolinkftp user is already restricted (no sudo, home-dir only) so the

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Confidence
75% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · scripts/setup_vps_retention.sh (reported line 10)May include surrounding context.

sh
#!/usr/bin/env bash
set -euo pipefail

# Usage:
#   sudo ./setup_vps_retention.sh [days]
# Default days: 30

DAYS="${1:-30}"

( crontab -l 2>/dev/null | grep -v '/srv/reolink/incoming -type f -mtime' ; \
  echo "30 3 * * * find /srv/reolink/incoming -type f -mtime +${DAYS} -delete" ) | crontab -

echo "Installed VPS retention prune: delete files older than ${DAYS} days"
crontab -l

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill instructs operators to run shell scripts as root on a VPS and to configure local systemd services, but it declares no explicit tool scope or allowed-tools metadata. That mismatch can cause an agent or automation framework to execute powerful shell-capable actions without clear permission boundaries, increasing the risk of unintended command execution or privilege-impacting changes.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

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

Content

Scanner excerpt · references/troubleshooting.md (reported line 29)May include surrounding context.

md
- Confirm writable ingest path:
  - `mkdir -p /srv/reolink/incoming`
  - `chown -R reolinkftp:reolinkftp /srv/reolink`
  - `chmod 775 /srv/reolink/incoming`
- Confirm passive ports open (21 + 50000-50100/TCP).
- Temporarily disable forced TLS for debugging only:
  - `force_local_logins_ssl=NO`

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

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

Content

Scanner excerpt · references/troubleshooting.md (reported line 75)May include surrounding context.

bash
mkdir -p /srv/reolink/incoming
chown reolinkftp:reolinkftp /srv/reolink/incoming
chmod 775 /srv/reolink/incoming

8) rsync fails with protocol version mismatch -- is your shell clean?

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

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

Content

Scanner excerpt · scripts/setup_local_pull.sh (reported line 132)May include surrounding context.

sh
STATIC
} > "$PULL_SCRIPT"

chmod 700 "$PULL_SCRIPT"

# ── Systemd user units ────────────────────────────────────────────────────────

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/setup_local_pull.sh (reported line 160)May include surrounding context.

sh
UNIT

systemctl --user daemon-reload
systemctl --user enable --now reolink-pull.timer

echo
systemctl --user list-timers | grep reolink-pull || true

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

The script persists a scheduled task by rewriting the current user's crontab and interpolates the unvalidated DAYS argument directly into the cron command line. If an attacker can influence the argument passed to the script, they may inject additional cron syntax or shell metacharacters, resulting in unintended commands running repeatedly with the privileges of the crontab owner.

Content

Scanner excerpt · scripts/setup_vps_retention.sh (reported line 10)May include surrounding context.

sh
DAYS="${1:-30}"

( crontab -l 2>/dev/null | grep -v '/srv/reolink/incoming -type f -mtime' ; \
  echo "30 3 * * * find /srv/reolink/incoming -type f -mtime +${DAYS} -delete" ) | crontab -

echo "Installed VPS retention prune: delete files older than ${DAYS} days"

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% 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.

Content

Scanner excerpt · scripts/setup_vps_retention.sh (reported line 14)May include surrounding context.

sh
echo "30 3 * * * find /srv/reolink/incoming -type f -mtime +${DAYS} -delete" ) | crontab -

echo "Installed VPS retention prune: delete files older than ${DAYS} days"
crontab -l

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 · references/troubleshooting.md (reported line 51)May include surrounding context.

md
set -euo pipefail

# Usage:
#   sudo ./setup_vps_vsftpd.sh [FTP_USER] [FTP_ROOT]
# Defaults:
#   FTP_USER=reolinkftp
#   FTP_ROOT=/srv/reolink

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 · references/troubleshooting.md (reported line 52)May include surrounding context.

md
set -euo pipefail

# Usage:
#   sudo ./setup_vps_vsftpd.sh [FTP_USER] [FTP_ROOT]
# Defaults:
#   FTP_USER=reolinkftp
#   FTP_ROOT=/srv/reolink

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 · scripts/setup_local_pull.sh (reported line 172)May include surrounding context.

sh
set -euo pipefail

# Usage:
#   sudo ./setup_vps_vsftpd.sh [FTP_USER] [FTP_ROOT]
# Defaults:
#   FTP_USER=reolinkftp
#   FTP_ROOT=/srv/reolink

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 · scripts/setup_local_pull.sh (reported line 175)May include surrounding context.

sh
set -euo pipefail

# Usage:
#   sudo ./setup_vps_vsftpd.sh [FTP_USER] [FTP_ROOT]
# Defaults:
#   FTP_USER=reolinkftp
#   FTP_ROOT=/srv/reolink

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 · scripts/setup_local_pull.sh (reported line 180)May include surrounding context.

sh
set -euo pipefail

# Usage:
#   sudo ./setup_vps_vsftpd.sh [FTP_USER] [FTP_ROOT]
# Defaults:
#   FTP_USER=reolinkftp
#   FTP_ROOT=/srv/reolink

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 · scripts/setup_vps_retention.sh (reported line 5)May include surrounding context.

sh
set -euo pipefail

# Usage:
#   sudo ./setup_vps_vsftpd.sh [FTP_USER] [FTP_ROOT]
# Defaults:
#   FTP_USER=reolinkftp
#   FTP_ROOT=/srv/reolink

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 · scripts/setup_vps_vsftpd.sh (reported line 5)May include surrounding context.

sh
set -euo pipefail

# Usage:
#   sudo ./setup_vps_vsftpd.sh [FTP_USER] [FTP_ROOT]
# Defaults:
#   FTP_USER=reolinkftp
#   FTP_ROOT=/srv/reolink

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This shell script updates packages, installs software, creates a user, overwrites /etc/vsftpd.conf, modifies firewall rules, and enables services, but it does not include an upfront warning or confirmation prompt describing these system-altering actions. While some status messages are printed, they do not disclose the full scope before execution.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

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

Content

Scanner excerpt · scripts/setup_vps_vsftpd.sh (reported line 21)May include surrounding context.

sh
mkdir -p "$INGEST_DIR"
chown -R "$FTP_USER:$FTP_USER" "$FTP_ROOT"
chmod 755 "$FTP_ROOT"
chmod 775 "$INGEST_DIR"

echo "Set password for ${FTP_USER}:"

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
84% confidence
Finding

Setting the ingest directory to mode 775 makes it group-writable, which can allow unintended modification of uploaded camera data by any account placed in the FTP user's primary group. In a backup-ingest context, that weakens integrity of evidence and recordings, especially on multi-user VPS hosts or where group membership is broadened later.

Content

Scanner excerpt · scripts/setup_vps_vsftpd.sh (reported line 22)May include surrounding context.

sh
mkdir -p "$INGEST_DIR"
chown -R "$FTP_USER:$FTP_USER" "$FTP_ROOT"
chmod 755 "$FTP_ROOT"
chmod 775 "$INGEST_DIR"

echo "Set password for ${FTP_USER}:"
passwd "$FTP_USER"

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

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

Content

Scanner excerpt · scripts/setup_vps_vsftpd.sh (reported line 33)May include surrounding context.

sh
-keyout /etc/ssl/private/vsftpd.key \
    -out /etc/ssl/private/vsftpd.crt \
    -subj "/CN=$(hostname)"
  chmod 600 /etc/ssl/private/vsftpd.key
fi

cat >/etc/vsftpd.conf <<EOF

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script replaces the system's FTP server configuration by writing directly to /etc/vsftpd.conf via a heredoc. There is no specific warning that an existing configuration will be overwritten, which could cause loss of prior settings.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/setup_vps_vsftpd.sh (reported line 74)May include surrounding context.

sh
echo "$FTP_USER" >/etc/vsftpd.userlist

systemctl enable --now vsftpd

ufw allow OpenSSH
ufw allow 21/tcp

Static analysis

No suspicious patterns detected.