Back to skill

Security audit

OpenClaw Self Backup & Restore

Security checks for vulnerabilities and agentic risk

Overview

This is a real backup and restore skill, but it creates unencrypted archives of sensitive OpenClaw secrets and restores archives unsafely.

Review before installing. Treat every backup archive as a secret: encrypt it, keep it out of shared or synced locations unless protected, restore only archives you created and trust, and avoid enabling the cron example until retention, removal, and secrets handling are clear.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/backup.py:21
Finding
Unencrypted backups expose API credentials and other sensitive agent data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/backup.py:21-29`, `scripts/backup.py:65-82`; documented in `SKILL.md:17-25` **Vulnerability Type**: Plaintext storage of credentials and sensitive information **Risk Level**: High ### Vulnerable Code ```python BACKUP_PATHS = [ # Core configuration ".openclaw/openclaw.json", ".openclaw/.env", ".openclaw/cron/jobs.json", # Credentials ".openclaw/credentials", ".openclaw/identity", ``` ```python with tarfile.open(backup_path, "w:gz") as tar: for rel_path in BACKUP_PATHS: full_path = HOME / rel_path if not full_path.exists(): skipped.append(f" ⚠️ 不存在: {rel_path}") continue if should_exclude(str(full_path)): skipped.append(f" ⏭️ 已排除: {rel_path}") continue tar.add(full_path, arcname=rel_path, filter=lambda ti: None if should_exclude(ti.name) else ti) included.append(f" ✅ {rel_path}") ``` ### Technical Analysis The backup includes `~/.openclaw/.env`, the complete credentials directory, identity material, configuration, memory, and workspace files by default. These files are written to a gzip-compressed tar archive. Gzip compression does not provide confidentiality, integrity protection, or authentication. The script does not encrypt the archive, explicitly set restrictive permissions on the backup directory or archive, or ask the user to opt in before collecting credentials. Resulting permissions depend on the process umask and existing directory permissions. The documentation describes API credentials as required backup content, even though secret restoration should be separable from ordinary configuration and workspace backup. Although a source comment states that credentials are encrypted, this does not establish that every file under the credentials directory is protected, and it does not protect plaintext environment files or other sensitive workspace con ...[truncated 1054 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Exclude `.env` and credential directories from the default backup profile. 2. Require an explicit option such as `--include-secrets` and show a prominent confirmation before collecting credentials. 3. Encrypt secret-bearing backups with authenticated encryption using a user-supplied key or a supported key-management facility. 4. Create `~/backups` with mode `0700` and archives and manifests with mode `0600`, without relying solely on the process umask. 5. Separate workspace/configuration backups from credential exports so users can apply different retention and handling controls. 6. Avoid following symlinks when collecting sensitive paths and verify that every source resolves beneath the intended OpenClaw directory. 7. Document that compressed archives are not encrypted and provide secure storage, transfer, deletion, and key-recovery guidance. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/restore.py:41
Finding
Unvalidated tar extraction permits path traversal and unsafe archive entries<![CDATA[ ## Vulnerability Details **File Location**: `scripts/restore.py:41-48` **Vulnerability Type**: Unsafe archive extraction **Risk Level**: High ### Vulnerable Code ```python # Extract backup print("解压备份文件...") with tarfile.open(backup_path, "r:gz") as tar: members = tar.getmembers() for member in members: target = HOME / member.name target.parent.mkdir(parents=True, exist_ok=True) print(f" 恢复: {member.name}") tar.extractall(HOME) ``` ### Technical Analysis The restore operation trusts every archive member name and calls `extractall(HOME)` without explicit validation or a safe extraction policy. It does not reject: - Absolute paths - Names containing `..` traversal components - Symbolic or hard links - Device nodes, FIFOs, or other special entries - Files outside the expected `.openclaw` backup allowlist The preliminary loop is not a security check. For an absolute member name, `HOME / member.name` resolves to the absolute path. For traversal names, the resulting path can resolve outside the home directory. The call to `target.parent.mkdir()` may itself create attacker-selected directories wherever the user has write permission. On Python versions or configurations where tar extraction does not apply a restrictive default filter, `extractall()` can then write files outside `HOME` or follow malicious archive links. Even where newer runtime defaults mitigate some tar entry types, the code does not explicitly enforce a compatible policy and still fails to restrict restoration to declared OpenClaw paths. ### Attack Path 1. An attacker creates or modifies a `.tar.gz` backup containing an entry such as `../../.profile`, an absolute path, or a link targeting another writable file. 2. The attacker convinces the user to restore the archive, or replaces an archive in a writable or synchronized backup location. 3. `restore.py` creates parent directories derived from the untrusted member name. 4. On an affected Python run ...[truncated 805 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit only expected relative paths under an explicit allowlist such as `.openclaw/openclaw.json`, approved workspace files, and approved subdirectories. 2. Reject empty names, absolute paths, drive-qualified paths, and any path containing `..`. 3. Resolve each proposed destination and verify with `Path.is_relative_to()` or an equivalent containment check that it remains under the approved restoration root. 4. Reject symbolic links, hard links, device nodes, FIFOs, sockets, and other special archive members. 5. On supported Python versions, pass an explicit safe extraction filter rather than relying on version-dependent defaults. 6. Extract into a newly created private staging directory first, validate the complete result, and then copy approved regular files into place. 7. Verify archive provenance and integrity using a trusted signature or authenticated checksum before restoration. 8. Refuse to run as root unless privileged restoration is explicitly designed and independently secured. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/restore.py:26
Finding
Pre-restore snapshot captures the entire OpenClaw directory beyond the declared scope<![CDATA[ ## Vulnerability Details **File Location**: `scripts/restore.py:26-36` **Vulnerability Type**: Excessive sensitive-data access and collection **Risk Level**: Medium ### Vulnerable Code ```python # Back up the current state first, if present openclaw_dir = HOME / ".openclaw" if openclaw_dir.exists(): pre_restore_backup = HOME / f"backups/pre-restore-{datetime.now().strftime('%Y%m%d_%H%M')}.tar.gz" pre_restore_backup.parent.mkdir(parents=True, exist_ok=True) print(f"⚠️ 先备份当前状态到: {pre_restore_backup}") with tarfile.open(pre_restore_backup, "w:gz") as tar: tar.add(openclaw_dir, arcname=".openclaw", filter=lambda ti: None if ".venv-stock" in ti.name or "__pycache__" in ti.name else ti) print(f"✅ 当前状态已备份") print() ``` ### Technical Analysis Before restoring an archive, the script automatically archives the complete `~/.openclaw` directory. The only exclusions are names containing `.venv-stock` or `__pycache__`. This behavior is broader than the documented backup scope. `SKILL.md` states that logs, media, completion history, and certain environments are not backed up, but the pre-restore path can include all of those categories except the two explicitly filtered patterns. It can also capture future OpenClaw files that the skill author did not review or intend to collect. The pre-restore archive is also an unencrypted gzip-compressed tar file. Consequently, an operation intended to restore selected configuration creates a second broad snapshot containing current credentials, logs, caches, media, and other potentially private state. ### Attack Path 1. The user invokes `restore.py` while `~/.openclaw` exists. 2. The script automatically traverses the entire OpenClaw directory. 3. Nearly all current files are copied into an unencrypted `pre-restore-*.tar.gz` archive. 4. Data that the documentation claims is excluded, such as logs or media, remains in the backup directory. 5. A local process, synchronized-s ...[truncated 669 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reuse one centralized, documented allowlist for ordinary and pre-restore backups. 2. Apply all documented exclusions consistently, including logs, media, completion data, virtual environments, repositories, caches, and dependencies. 3. Display the exact files and categories to be preserved and obtain confirmation before creating a pre-restore snapshot. 4. Make the snapshot optional through a flag such as `--pre-restore-backup`. 5. Handle credentials through a separate opt-in and encrypted backup mechanism. 6. Apply restrictive directory and archive permissions and define retention and secure-deletion behavior. 7. Avoid broad recursive capture of future files whose sensitivity and purpose are unknown. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/restore.py:55
Finding
Recovery instructions install unpinned third-party dependencies<![CDATA[ ## Vulnerability Details **File Location**: `scripts/restore.py:55-58`; duplicated in `SKILL.md:74-80` **Vulnerability Type**: Unpinned dependency installation **Risk Level**: Medium ### Vulnerable Code ```python print(" 1. 重建 Python 环境(如果是新机器):") print(" cd ~/.openclaw/workspace") print(" python3 -m venv .venv-stock") print(" source .venv-stock/bin/activate") print(" pip install yfinance pandas numpy pandas-ta ta ddgs tavily-python requests beautifulsoup4") ``` The documentation similarly instructs: ```bash cd ~/.openclaw/workspace python3 -m venv .venv-stock source .venv-stock/bin/activate pip install yfinance pandas numpy pandas-ta ta ddgs tavily-python requests beautifulsoup4 ``` ### Technical Analysis The recommended recovery process installs packages by name without exact versions, hashes, a lockfile, or an explicitly trusted package index. The code restored by the skill does not establish that these packages are required for the core backup and restore functionality. Package resolution therefore depends on the mutable state of the configured Python package indexes at installation time. A compromised upstream release, compromised index, maliciously configured index, or unexpected dependency update could cause different code to be installed than the code reviewed when the skill was published. Python package installation can run build backends and package-controlled code. A virtual environment limits where packages are installed but does not sandbox build or installation processes from the user's account and files. ### Attack Path 1. A user completes restoration and follows the printed environment-rebuild instructions. 2. `pip` queries the user's configured package indexes for the latest acceptable versions. 3. An index serves a compromised, replaced, or unexpectedly changed package or transitive dependency. 4. Package build or installation logic runs with the restoring user's privileges. 5. The installed code can ac ...[truncated 679 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove recovery dependencies that are unrelated to backup and restoration. 2. Provide a reviewed lockfile with exact direct and transitive versions. 3. Require package hashes, for example through a hash-locked requirements file and `pip install --require-hashes`. 4. Use a documented trusted index or an internally reviewed package mirror. 5. Separate optional investment or workspace dependencies from the base recovery procedure. 6. Audit packages and transitive dependencies before publishing lockfile updates. 7. Prefer prebuilt, verified artifacts where appropriate and avoid unexpected source builds. ]]>

T06 · System Persistence

Note
Location
SKILL.md:53
Finding
Optional scheduled backup repeatedly processes secrets and announces activity externally<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:53-61` **Vulnerability Type**: Persistent scheduled processing of sensitive data **Risk Level**: Low ### Vulnerable Configuration ```bash openclaw cron add \ --name "weekly-self-backup" \ --cron "0 2 * * 0" \ --tz "Asia/Shanghai" \ --message "Run backup: python3 ~/.openclaw/workspace/skills/self-backup/scripts/backup.py and report result" \ --session isolated \ --announce \ --channel telegram ``` ### Technical Analysis The documentation offers a command that creates a weekly scheduled task surviving the immediate skill run. That task invokes the backup script, which collects credentials and other sensitive files into unencrypted archives. The command also requests announcement of the result through Telegram. The behavior is transparent and user-invoked rather than silently installed, so it is not evidence of a concealed backdoor. Nevertheless, it extends sensitive-data processing across sessions, increases the number of secret-bearing archives retained over time, and introduces an external reporting channel. The documentation does not provide a corresponding removal command, permission-hardening procedure, or warning about the content and exposure of recurring backups. ### Attack Path 1. A user copies and executes the documented `openclaw cron add` command. 2. A persistent weekly task invokes `backup.py` without further per-run confirmation. 3. Each execution reads credentials, memory, identity data, and configuration and creates an unencrypted archive. 4. Archives accumulate according to the script's retention behavior. 5. Backup status is announced through Telegram, exposing operational metadata to the configured channel. 6. Compromise of local backup storage or the announcement destination increases the likelihood or visibility of disclosure. ### Impact Assessment The scheduled task operates with the privileges of the OpenClaw account or user that created it and repeatedly ...[truncated 432 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Keep scheduled backups explicitly opt-in and require confirmation that the archive contains sensitive data. 2. Encrypt archives and apply strict owner-only permissions before recommending automation. 3. Remove `--announce --channel telegram` from the default example, or clearly state exactly what information is sent externally. 4. Document how to inspect, disable, and remove the scheduled job. 5. Use short, configurable retention and verify deletion of expired secret-bearing archives. 6. Prefer a narrowly scoped service account or execution context where supported. 7. Avoid including credentials in recurring backups unless the user separately enables secure secret export. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill advertises full backup/restore and auto-backup behavior, but the markdown only documents commands and a narrower hardcoded backup scope, with important implementation details absent. This mismatch can mislead users and reviewers about what is actually backed up, restored, or scheduled, increasing the chance of unsafe assumptions around disaster recovery and sensitive data handling.

Credential Access

High
Category
Privilege Escalation
Content
BACKUP_PATHS = [
    # 核心配置
    ".openclaw/openclaw.json",
    ".openclaw/.env",
    ".openclaw/cron/jobs.json",
    # 凭证(加密存储,备份后需妥善保管)
    ".openclaw/credentials",
Confidence
99% confidence
Finding
Including .openclaw/.env in the backup captures environment secrets such as API keys, tokens, or service credentials. In a self-backup skill, this materially increases exposure because backups are likely to be copied, synced, or retained outside the original protected environment, turning routine backup handling into credential disclosure risk.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The restore logic extracts every archive member directly into the user's home directory using `tar.extractall(HOME)` without validating member paths or restricting extraction to an expected `.openclaw` subtree. A crafted tarball can overwrite arbitrary files in the home directory via unexpected paths or symlink/path traversal behaviors, which is especially dangerous because this skill is specifically designed to ingest external backup archives.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill instructs running backup/restore scripts and setting up a cron job, which implies file-write and likely network/notification capabilities, yet it declares no tool scope or permissions. For a skill that handles configuration, memory, credentials, and scheduled execution, missing explicit scoping weakens reviewability and can enable overbroad access to sensitive data and persistence mechanisms.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The backup scope explicitly includes `~/.openclaw/.env` and `~/.openclaw/credentials/`, which likely contain API keys and authentication material, but the skill provides no prominent warning about the sensitivity of the resulting archive. Users may store, transmit, or automate backups insecurely, leading to credential compromise and full agent or account takeover.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The restore instructions tell users to run a restore operation without warning that it may overwrite existing configuration, skills, memory, or credentials. That omission can cause accidental destruction of current state, reintroduction of stale or compromised files, or unsafe rollback of security settings.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The script archives highly sensitive files including .env, credentials, and identity material without any explicit warning, consent step, encryption, or permission hardening for the output archive. In the context of an agent self-backup skill, this is more dangerous because users may invoke it routinely and then store or transfer backups, unintentionally exposing secrets that enable account or agent compromise.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The backup scope is described as agent configuration, skills, memory, and workspace state, but it also includes unrelated investment documents. This broadens data collection beyond the stated purpose and can cause unintended exfiltration or retention of sensitive personal/business content during backup handling or sharing.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
The script automatically deletes older backups once more than six exist, without warning or confirmation. In a recovery-oriented backup tool, silent retention pruning can cause data loss, remove needed restore points, and reduce forensic/history availability, especially if backups are created frequently or unexpectedly.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script performs bulk writes into `HOME` immediately after printing filenames, with no interactive confirmation, dry-run, or summary of affected paths. In a restore skill, this raises the chance of accidental destructive overwrite and makes exploitation of a malicious archive easier because the user gets no final checkpoint before filesystem changes occur.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The example cron command specifies `--tz "Asia/Shanghai"`, which imposes a specific locale setting in natural-language guidance without indicating that users should choose their own timezone. This can violate locale policy expectations unless the region-specific constraint is optional or clearly justified.

Natural-Language Policy Violations

Low
Confidence
81% confidence
Finding
The natural-language strings and description are entirely in Chinese, and the file does not indicate that the locale is intentionally region-specific or optional. Under the policy, forcing a specific language without opt-in can be a natural-language policy violation.

Description-Behavior Mismatch

Low
Confidence
90% confidence
Finding
The skill description says it 'also handles restore' including restore and recovery use cases, but this script only creates archives and lists existing backups. No restore behavior is implemented here despite the manifest implying that capability is part of the skill's actual behavior.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The docstring and all user-facing console output are written in Chinese, which imposes a specific language on users without any opt-in or alternative. This matches the locale/language policy concern for natural-language content in code files.

Static analysis

No suspicious patterns detected.