Back to skill

Security audit

OpenClaw Backup

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent OpenClaw backup tool, but it needs review because it handles sensitive backups, schedules recurring automation, and includes weak backup-storage and deletion safeguards.

Review before installing. Use a private backup directory such as mode 700, protect archives as sensitive secrets, avoid shared or synced locations unless encrypted, confirm the cron schedule and timezone, and do not run cleanup with --execute unless the selected archives have been reviewed.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
references/SETUP_GUIDE.md:175
Finding
Sensitive backup directory is configured with overly permissive access<![CDATA[ ## Vulnerability Details **File Location**: `references/SETUP_GUIDE.md:175-180`; related directory creation logic at `scripts/backup.py:18-19` **Vulnerability Type**: Insecure filesystem permissions for sensitive backup storage **Risk Level**: Medium ### Vulnerable Code `references/SETUP_GUIDE.md:175-180`: ```bash ### "Permission denied" on backup directory Create the directory: ```bash mkdir -p ~/openclaw_backups chmod 755 ~/openclaw_backups ``` ``` Related code in `scripts/backup.py:18-19`: ```python def ensure_backup_dir(): os.makedirs(get_backup_dir(), exist_ok=True) ``` ### Technical Analysis The backup archives are documented as containing configuration, credentials, session history, workspace files, skills, and settings. Despite the sensitivity of this data, the setup guide recommends permission mode `0755` for the backup directory. Mode `0755` allows every local user to traverse and list the directory. This exposes archive names, timestamps, and other filesystem metadata. If the OpenClaw CLI creates archives using a permissive process umask or permissive explicit file mode, other local users may also be able to read the archive contents. The Python code creates the directory without explicitly enforcing a private mode or verifying its ownership and effective permissions. For an existing directory, `os.makedirs(..., exist_ok=True)` does not correct insecure permissions. It also does not verify that the configured path is not a symbolic link. ### Attack Path 1. A user follows the setup guide and configures `~/openclaw_backups` with mode `0755`. 2. The backup script creates archives containing sensitive OpenClaw state in that directory. 3. Another local account lists or traverses the directory and obtains archive names and metadata. 4. If an archive is group- or world-readable because of the OpenClaw CLI's output mode or the invoking process's umask, the local account copies and inspects it. 5. The attacker gains access to any unpr ...[truncated 762 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the documented `chmod 755` command with private permissions: ```bash mkdir -p ~/openclaw_backups chmod 700 ~/openclaw_backups ``` 2. Enforce secure permissions in `backup.py`, including for existing directories: ```python def ensure_backup_dir(): backup_dir = get_backup_dir() os.makedirs(backup_dir, mode=0o700, exist_ok=True) os.chmod(backup_dir, 0o700) ``` 3. Before use, validate that the directory: - Is owned by the current user. - Is an actual directory rather than a symbolic link. - Has no group or world permissions. - Resolves to the intended location. 4. After backup creation, verify that each archive is a regular file owned by the current user and set its mode to `0600`. 5. Run the backup process with a restrictive umask such as `077`. 6. Document that backup archives contain sensitive information and must not be placed in shared, network-mounted, or world-accessible directories without appropriate encryption and access controls. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cleanup_old_backups.py:28
Finding
Negative retention values can delete all matching backup archives<![CDATA[ ## Vulnerability Details **File Location**: `scripts/cleanup_old_backups.py:28-35`, `scripts/cleanup_old_backups.py:42-47`, and `scripts/cleanup_old_backups.py:79-84` **Vulnerability Type**: Insufficient input validation leading to unintended destructive file deletion **Risk Level**: Medium ### Vulnerable Code Archive age calculation at `scripts/cleanup_old_backups.py:28-35`: ```python cutoff = datetime.now() - timedelta(days=days) old = [] for arch in all_archives: mtime = datetime.fromtimestamp(os.path.getmtime(arch)) if mtime < cutoff: old.append(arch) return old ``` Argument handling at `scripts/cleanup_old_backups.py:42-47`: ```python parser.add_argument("--days", type=int, default=None, help="Show backups older than N days (default: show all)") parser.add_argument("--dry-run", action="store_true", default=True, help="Preview deletions (default: True)") parser.add_argument("--execute", action="store_true", help="Actually delete (overrides dry-run)") ``` Deletion sink at `scripts/cleanup_old_backups.py:79-84`: ```python else: deleted = 0 for arch in to_delete: os.remove(arch) deleted += 1 print(f" Deleted: {os.path.basename(arch)}") print(f"\nDeleted {deleted} archive(s).") ``` ### Technical Analysis The `--days` option accepts any integer, including negative values. The cutoff is calculated as: ```python datetime.now() - timedelta(days=days) ``` When `days` is negative, subtracting a negative duration produces a cutoff in the future. Consequently, every existing matching archive normally has a modification time earlier than the cutoff and is classified as old. If `--execute` is also supplied, the script removes every archive selected by that calculation. There is no validation requiring a positive retention period, no ...[truncated 1547 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject zero and negative retention periods during argument parsing: ```python def positive_days(value): days = int(value) if days <= 0: raise argparse.ArgumentTypeError("--days must be a positive integer") return days parser.add_argument( "--days", type=positive_days, default=None, help="Show backups older than N days" ) ``` 2. Require `--days` whenever `--execute` is used and fail closed if it is absent. 3. Preserve a configurable minimum number of recent backups, even when they exceed the age threshold. 4. Before deletion, display the exact number of selected archives and require explicit confirmation unless a separately named noninteractive option is supplied. 5. Revalidate each file immediately before deletion: - Confirm it remains inside the resolved backup directory. - Confirm it is a regular file rather than a symbolic link. - Confirm its filename matches the expected archive convention. 6. Add automated tests covering negative, zero, extremely large, and omitted `--days` values, as well as preservation of the newest backup. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The primary backup behavior matches part of the description: it uses the built-in `openclaw backup create` command and supports verification and dry-run. However, the declared description materially overstates the implemented functionality by claiming cleanup of old backups, health verification workflows, and first-run setup with cron prompting, none of which are present in the supplied code. This is a description-to-behavior mismatch due to missing declared capabilities rather than undeclared extra behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The description presents a broader backup-management skill whose core capability is creating OpenClaw backups and also verifying backup health, cleaning old backups, and handling setup/cron guidance. The provided code chunk is narrowly scoped to cleanup of old backup archives in `~/openclaw_backups` (or `OPENCLAW_BACKUP_DIR`), with dry-run behavior by default and optional deletion using `--execute`. This is not malicious or unrelated, but it materially underimplements the declared primary purpose. Since the code lacks the key declared behaviors—especially backup creation and health verification—the description does not accurately represent what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description centers on running backups via `openclaw backup create`, plus cleanup and health verification. The provided code does none of those things: it only lists existing cron jobs and adds a scheduled cron entry if one is absent. While cron setup is mentioned in the description as a first-run behavior, here it is the entire implementation, so the actual code chunk's primary behavior is materially narrower and different from the declared backup/verification/cleanup functionality.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The changelog explicitly states that backups include credentials and session history, which are highly sensitive data types, but it provides no warning, opt-in language, encryption note, or handling guidance. In the context of a backup skill, this increases the risk that users will create archives containing secrets and activity history without understanding the exposure if the backup directory is accessed, copied, or retained insecurely.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill advertises and invokes shell-based scripts and implies access to files, environment, and backup operations, but it does not declare any explicit tool scope such as permissions or allowed-tools. That creates an authorization ambiguity where an agent may run higher-risk capabilities than users expect, especially for a skill that handles archives containing configuration, session history, and credentials.

Vague Triggers

Medium
Confidence
95% confidence
Finding
Trigger phrases like 'run backup', 'backup now', and especially the cron phrase 'backup openclaw' are broad enough to collide with ordinary user language. That can cause unintended activation of backup, verification, or deletion-related flows, which is risky for a skill that touches sensitive archives and may schedule persistent automation.

Session Persistence

Medium
Category
Rogue Agent
Content
- "backup openclaw", "run backup", "backup now"
    - "check backup health", "verify backup"
    - "cleanup old backups", "remove old backups"
    - First run on new machine → run setup + prompt to create cron job
  Uses built-in `openclaw backup create` — no extra tools needed.
  Cron message: "backup openclaw" → runs scripts/backup.py
---
Confidence
90% confidence
Finding
The skill is designed to create a cron job, which introduces persistence beyond the current user interaction. Persistence increases risk because a sensitive operation involving backups of credentials, session history, and workspace data may continue automatically and repeatedly, potentially after the user forgets it was enabled or in contexts where broad natural-language triggers could be misinterpreted.

Vague Triggers

Medium
Confidence
93% confidence
Finding
Using a broad natural-language cron trigger message means a background scheduled session may activate based on an ambiguous phrase rather than a constrained command. In a persistent automated context, ambiguity is more dangerous because it can repeatedly invoke a high-sensitivity backup skill without fresh user review.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The file specifies a daily 4 AM HKT backup schedule, which forces a locale-specific timezone choice in natural-language instructions. There is no indication that users can select their own timezone or that HKT is required for a region-specific purpose.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The statement that the cron job runs automatically at 04:00 HKT reflects a locale-specific policy choice presented as the default behavior. The documentation does not offer localization options or explain why this timezone is mandatory.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill explicitly states that backups include credentials, session history, and workspace files, but it does not present a prominent warning about the sensitivity of the resulting archives or the need to protect storage and transfer. Users may treat the backups like ordinary files and inadvertently expose highly sensitive data.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The setup instructions direct users to configure backups specifically for 4 AM HKT, and later the manual cron example fixes the timezone to `Asia/Hong_Kong`. This imposes a locale-specific policy in natural language without offering alternatives or explaining that users should choose their own timezone.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This markdown file includes a command that will actually delete backup archives, but the surrounding text does not clearly warn that the operation is irreversible or may remove user data permanently. Under the markdown-specific warning criteria, destructive behavior affecting stored backups should be accompanied by an explicit caution.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
Create the directory:
```bash
mkdir -p ~/openclaw_backups
chmod 755 ~/openclaw_backups
```

### Cron not running
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
os.makedirs(get_backup_dir(), exist_ok=True)


def run_backup(verify=False, dry_run=False):
    backup_dir = get_backup_dir()
    ensure_backup_dir()
    cmd = ["openclaw", "backup", "create", "--output", backup_dir]
Confidence
75% confidence
Finding
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
cmd.append("--dry-run")

    print(f"Running: {' '.join(cmd)}")
    result = subprocess.run(cmd, capture_output=True, text=True)

    print(result.stdout)
    if result.stderr:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
cmd.append("--dry-run")

    print(f"Running: {' '.join(cmd)}")
    result = subprocess.run(cmd, capture_output=True, text=True)

    print(result.stdout)
    if result.stderr:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def verify_archive(archive_path):
    print(f"Verifying: {archive_path}")
    result = subprocess.run(
        ["openclaw", "backup", "verify", archive_path],
        capture_output=True,
        text=True
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def cron_exists(name):
    result = subprocess.run(
        ["openclaw", "cron", "list", "--json"],
        capture_output=True, text=True
    )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Low
Confidence
78% confidence
Finding
The text references an optional daily cron at 04:00 HKT, which imposes a specific locale/timezone in natural language without documenting user opt-in to that locale or a reason for the regional constraint. This can be a language/locale policy concern because it assumes a fixed locale context rather than offering a neutral or user-configurable one.

Static analysis

No suspicious patterns detected.