Back to skill

Security audit

openclaw-security-watchdog

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent security-audit skill, but it needs Review because it performs broad local and Agent-state inspection and can upload stable device identifiers to a third-party service when enabled.

Install only if you want a fairly invasive OpenClaw security audit. Prefer local mode unless you are comfortable sending device identifiers, a persistent agent ID, installed skill inventory, and audit summaries to the Changeway endpoint. Avoid running it as root unless you intentionally want system-wide visibility, review any cron job before enabling it, and do not put --push in scheduled jobs.

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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/openclaw-hybrid-audit-changeway.js:1098
Finding
Threat-intelligence API responses are parsed incorrectly and failures are reported as successful scans<![CDATA[ ## Vulnerability Details **File Location**: `scripts/openclaw-hybrid-audit-changeway.js`, lines 1098-1146 **Vulnerability Type**: Fail-open API response handling **Risk Level**: Medium ### Vulnerable Code ```javascript doSignedPost(assessApiUrl, assessApiPath, { data: skillMetaList }, (err, apiResRaw) => { let intelHits = 0; let hitDetails = []; if (!err && apiResRaw) { try { let apiRes = JSON.parse(apiResRaw); if (apiRes.data && Array.isArray(apiRes.data)) { apiRes.data.forEach(item => { if (item.matched_intel && Array.isArray(item.matched_intel) && item.matched_intel.length > 0) { item.matched_intel.forEach(intel => { intelHits++; const maliciousDesc = intel.is_malicious === 1 || intel.is_malicious === '1' ? '存在恶意' : (intel.is_malicious === 0 || intel.is_malicious === '0' ? '不存在恶意' : '无标记'); hitDetails.push( `🚨 命中威胁情报: [${item.slug} ${item.version}] (Owner: ${item.author})\n` + ` 恶意判定: ${maliciousDesc} (原始 is_malicious: ${intel.is_malicious ?? '无标记'})\n` + ` 风险等级 (severity): ${intel.severity || 'UNKNOWN'}\n` + ` 情报详情: ${JSON.stringify(intel.info || {})}` ); }); } }); } } catch (parseErr) { hitDetails.push(`⚠️ API 响应解析失败: ${parseErr.message}`); } } else { hitDetails.push(`⚠️ 威胁情报 API 请求异常: ${err}`); } let finalDetailText = `${scannedSummary}\n\n>>> 威胁情报扫描结果:\n`; if (intelHits > 0) { finalDe ...[truncated 2471 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse only the response body: ```javascript const rawBody = typeof apiResRaw === 'string' ? apiResRaw : apiResRaw && typeof apiResRaw.body === 'string' ? apiResRaw.body : null; if (!rawBody) { appendSkip(itemNameSkill, 'Threat-intelligence response was unavailable', scannedSummary); return finalizeAndPushData(); } const apiRes = JSON.parse(rawBody); ``` 2. Validate that the decoded response matches the expected schema before using it. 3. Treat transport, HTTP, parsing, and schema errors as `FAIL` or `SKIP`, never as `PASS`. 4. Preserve the diagnostic error in the local report without exposing sensitive response content. 5. Add tests covering valid responses, malformed JSON, unexpected schemas, timeouts, non-2xx responses, and threat matches. 6. Ensure a successful result is emitted only after the server response has been parsed and assessed completely. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/openclaw-hybrid-audit-changeway.js:266
Finding
Default audit execution performs broad sensitive-system and Agent-state reconnaissance<![CDATA[ ## Vulnerability Details **File Location**: `scripts/openclaw-hybrid-audit-changeway.js`, lines 266-384, 741-762, and 800-810 **Vulnerability Type**: Excessive local read scope and least-privilege violation **Risk Level**: Medium ### Vulnerable Code ```javascript if (platform === 'win32') { SENSITIVE_ROOTS = [ OC, path.join(HOME, '.ssh'), path.join(HOME, '.gnupg'), path.join(process.env.PROGRAMDATA || 'C:\\ProgramData', 'ssh') ]; } else { SENSITIVE_ROOTS = [OC, '/etc', path.join(HOME, '.ssh'), path.join(HOME, '.gnupg'), '/usr/local/bin']; } function findRecentFiles(roots, prunePatterns, maxAgeMs) { const cutoff = Date.now() - maxAgeMs; const pruneSet = new Set(prunePatterns); const results = []; function walk(dir) { let entries; try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch (e) { return; } for (const entry of entries) { if (pruneSet.has(entry.name)) continue; const fullPath = buildSafeChildPath(dir, entry.name); if (!fullPath) continue; try { if (entry.isDirectory()) { walk(fullPath); } else if (entry.isFile()) { const stat = fs.statSync(fullPath); if (stat.mtimeMs >= cutoff) { results.push(fullPath); } } } catch (e) {} } } for (const root of roots) { try { const stat = fs.statSync(root); if (stat.isDirectory()) { walk(root); } else if (stat.isFile() && stat.mtimeMs >= cutoff) { results.push(root); } } catch (e) {} } return results; } let lines = findRecentFiles(SENSITIVE_ROOTS, PRUNE_PATTERNS, MS_24H); ``` The script also attempts to read another process's environment: ```javascript const environPath ...[truncated 4646 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Divide the audit into clearly named modules such as basic, system-wide, workspace DLP, process inspection, and Agent-memory correlation. 2. Enable only the basic OpenClaw-specific checks by default. 3. Require separate, explicit user consent before: - Reading another process's environment - Scanning `~/.ssh` or `~/.gnupg` - Recursively scanning the workspace - Reading Agent memory - Scanning system-wide directories such as `/etc` 4. Remove Agent-memory access unless it is essential to a specifically requested forensic workflow. 5. Use allowlisted OpenClaw configuration files rather than recursively traversing broad roots. 6. Refuse or warn before running as root unless elevated access is required for a selected check. 7. Report which sources were accessed, skipped, or unavailable instead of silently swallowing all access errors. 8. Minimize retained metadata and redact user names, home-directory paths, chat identifiers, and other identifying path components from reports. 9. Apply retention controls to generated reports and provide a command for securely deleting audit artifacts. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/openclaw-hybrid-audit-changeway.js:585
Finding
Skill and MCP integrity baselines containing absolute paths are written without explicit restrictive permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/openclaw-hybrid-audit-changeway.js`, lines 585-613 **Vulnerability Type**: Insecure permissions for sensitive local metadata **Risk Level**: Low ### Vulnerable Code ```javascript let hashDir = path.join(OC, 'security-baselines'); fs.mkdirSync(hashDir, { recursive: true }); let curHashPath = path.join(hashDir, 'skill-mcp-current.sha256'); let baseHashPath = path.join(hashDir, 'skill-mcp-baseline.sha256'); let allMcpFiles = SKILL_SCAN_DIRS .flatMap(d => getAllFiles(d)) .concat(getAllFiles(mcpDir)) .sort(); let curHashes = allMcpFiles .map(f => `${getFileHash(f)} ${f}`) .join('\n') + '\n'; fs.writeFileSync(curHashPath, curHashes); if (fs.existsSync(baseHashPath)) { let baseData = fs.readFileSync(baseHashPath, 'utf-8'); if (baseData !== curHashes) { let baseLines = baseData.split('\n'); let curLines = curHashes.split('\n'); let diffLines = []; let maxLen = Math.max(baseLines.length, curLines.length); for (let i = 0; i < maxLen; i++) { if (baseLines[i] !== curLines[i]) { if (baseLines[i]) diffLines.push(`- ${baseLines[i]}`); if (curLines[i]) diffLines.push(`+ ${curLines[i]}`); } } } } else { fs.writeFileSync(baseHashPath, curHashes); } ``` ### Technical Analysis The current and baseline files contain SHA-256 hashes paired with full local filesystem paths for every discovered Skill and MCP file. Unlike the report files and persistent Agent ID, these baseline files are created without an explicit `mode: 0o600`. The `security-baselines` directory is also created without an explicit `0o700` mode. Consequently, initial permissions depend on the process umask and existing parent-directory permissions. On a system with a permissive umask or incorrectly configured OpenClaw state directory, other local users may be able to read the baseline inventory. The hashes d ...[truncated 1431 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the baseline directory with owner-only permissions: ```javascript fs.mkdirSync(hashDir, { recursive: true, mode: 0o700 }); ``` 2. Write both files with explicit owner-only permissions: ```javascript fs.writeFileSync(curHashPath, curHashes, { encoding: 'utf-8', mode: 0o600 }); fs.writeFileSync(baseHashPath, curHashes, { encoding: 'utf-8', mode: 0o600 }); ``` 3. Correct permissions on existing files with `fs.chmodSync()` after verifying that they are regular files and not symbolic links. 4. Avoid storing absolute paths when relative paths under approved Skill roots are sufficient. 5. Validate the baseline directory with `lstat()` before use to reduce symbolic-link and unexpected-filesystem-object risks. 6. Document the baseline contents and retention policy so users understand that component inventory metadata is stored persistently. ]]>
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 (23)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented behavior understates what the skill inspects and writes locally: it reportedly analyzes OpenClaw logs, workspace/memory content, and additional baseline files not disclosed in metadata. This transparency failure is security-relevant because users may unknowingly permit broader surveillance of local data and persistence artifacts than the description suggests.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The file first states that scheduled tasks must never include --push, then later explains how scheduled messaging may include --push and periodically upload audit summaries and skill inventory. This contradiction can mislead operators into enabling recurring exfiltration of device-linked metadata despite earlier assurances that scheduled runs are offline only.

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

High
Category
YARA Match
Content
# OpenClaw 安全巡检定时任务配置指南

## ⚠️ 重要警告(必读)

### 必须使用 `openclaw cron`,禁止使用系统 crontab

❌ **错误做法**:使用 `crontab -e` 或编辑 `/etc/crontab`


✅ **正确做法**:使用 OpenClaw 内置的 cron 系统
```bash
openclaw cron add ...
```

**原因**:
1. 系统 crontab 无法正确初始化 OpenClaw 环境变量和会话
2. 会导致执行失败、权限问题或推送异常
3. `openclaw cron` 自动处理隔离会话、超时、推送等逻辑

## 快速配置

### 使用 OpenClaw Cron 注册

```bash
openclaw cron add \
  --name "changeway-security-audit" \
  --description "每晚安全巡检" \
  --cron "45 23 * * *" \
  --tz "Asia/Shanghai" \
  --session "isolated" \
  --message "Run the security audit script: node <skill-path>/scripts/openclaw-hybrid-audit-changeway.js — then from the output extract and report ONLY these three items: (1) the l
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

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

High
Category
YARA Match
Content
# OpenClaw 安全巡检定时任务配置指南

## ⚠️ 重要警告(必读)

### 必须使用 `openclaw cron`,禁止使用系统 crontab

❌ **错误做法**:使用 `crontab -e` 或编辑 `/etc/crontab`


✅ **正确做法**:使用 OpenClaw 内置的 cron 系统
```bash
openclaw cron add ...
```

**原因**:
1. 系统 crontab 无法正确初始化 OpenClaw 环境变量和会话
2. 会导致执行失败、权限问题或推送异常
3. `openclaw cron` 自动处理隔离会话、超时、推送等逻辑

## 快速配置

### 使用 OpenClaw Cron 注册

```bash
openclaw cron add \
  --name "changeway-security-audit" \
  --description "每晚安全巡检" \
  --cron "45 23 * * *" \
  --tz "Asia/Shanghai" \
  --session "isolated" \
  --message "Run the security audit script: node <skill-path>/scripts/openclaw-hybrid-audit-changeway.js — then from the output extract and report ONLY these three items: (1) the l
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Credential Access

High
Category
Privilege Escalation
Content
const permPairedWin = checkWindowsFilePermission(path.join(OC, 'devices/paired.json'));
    const sshdConfigPath = path.join(process.env.PROGRAMDATA || 'C:\\ProgramData', 'ssh', 'sshd_config');
    const permSshdWin = checkWindowsFilePermission(sshdConfigPath);
    const permAuthKeysWin = checkWindowsFilePermission(path.join(HOME, '.ssh/authorized_keys'));

    detail4 += `\n\n>>> 关键文件权限状态 (Windows ACL):
openclaw.json     : ${permOCWin} (预期: 无 Everyone/Users 写权限)
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const permPairedWin = checkWindowsFilePermission(path.join(OC, 'devices/paired.json'));
    const sshdConfigPath = path.join(process.env.PROGRAMDATA || 'C:\\ProgramData', 'ssh', 'sshd_config');
    const permSshdWin = checkWindowsFilePermission(sshdConfigPath);
    const permAuthKeysWin = checkWindowsFilePermission(path.join(HOME, '.ssh/authorized_keys'));

    detail4 += `\n\n>>> 关键文件权限状态 (Windows ACL):
openclaw.json     : ${permOCWin} (预期: 无 Everyone/Users 写权限)
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Chaining Abuse

High
Category
Tool Misuse
Content
// Event ID 4672 = 特权提升
    let psOut = spawnCmd('powershell', ['-NoProfile', '-Command',
        `(Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4672; StartTime=(Get-Date).AddDays(-1)} -ErrorAction SilentlyContinue | Measure-Object).Count`
    ]);
    sudoCount = parseInt(psOut, 10) || 0;
}
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill invokes shell commands and reads environment-dependent paths but declares no explicit tool scope or allowed-tools boundary. That weakens policy enforcement and user visibility, making it easier for a broad-triggered skill to execute powerful local actions without clear sandbox constraints.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrases include generic terms like '安全检查', '巡检', and '系统安全', which can match ordinary conversation and invoke a shell-capable auditing skill unexpectedly. Because this skill can scan the system, write persistent files, and optionally upload metadata, overbroad activation materially increases the risk of unintended execution.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
运行依赖:
    必需:Node.js v18+、openclaw CLI(用于定时任务管理)
    脚本调用的系统命令(缺失时对应检查项会 SKIP,不影响其他项):
      macOS:find、lsof、netstat、ps、last、lastb、grep、awk、cat、sudo
      Linux:find、ss、lsof、ps、journalctl、last、lastb、grep、awk、cat、sudo
      Windows:wmic、netstat、tasklist、findstr
Confidence
84% confidence
Finding
Declaring sudo as a required command for supported platforms indicates the skill may invoke privileged operations during scanning. In a broadly triggered skill without explicit tool scoping, any privilege escalation path increases the blast radius of misuse or mis-execution.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
必需:Node.js v18+、openclaw CLI(用于定时任务管理)
    脚本调用的系统命令(缺失时对应检查项会 SKIP,不影响其他项):
      macOS:find、lsof、netstat、ps、last、lastb、grep、awk、cat、sudo
      Linux:find、ss、lsof、ps、journalctl、last、lastb、grep、awk、cat、sudo
      Windows:wmic、netstat、tasklist、findstr

  默认行为:本地离线模式,不产生任何网络请求。
Confidence
84% confidence
Finding
The Linux dependency list also includes sudo, again signaling possible root-level execution during the audit. Even if intended for benign inspection, privileged command capability is sensitive and should not be exposed through loosely scoped triggers and undeclared permission boundaries.

Session Persistence

Medium
Category
Rogue Agent
Content
**关于定时任务的硬性要求**:
- 必须使用 `openclaw cron add` 命令
- 禁止使用系统 crontab(`crontab -e` 等)
- 原因:系统 crontab 无法正确初始化 OpenClaw 环境,会导致执行失败
- **cron 命令中严禁添加 `--push` 参数**:定时任务只以本地离线模式运行,绝不自动向远端上报设备标识
Confidence
90% confidence
Finding
The skill explicitly supports setting up recurring background execution through openclaw cron add, which is a persistence mechanism even if framed as legitimate automation. Given the skill's ability to scan host state, write files, and potentially interact with external messaging or upload modes elsewhere in the document, scheduled execution raises the risk of ongoing collection without fresh user review.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
✅ 安全。扫描了工作区的所有文件,没有发现明文写死的私钥或助记词。

### 13. 特权提权(Sudo)操作对账审计
✅ 安全。今天系统没有执行过 sudo 特权命令,Agent 的记忆记录也没有相关内容,两边对账一致,不存在偷偷提权的情况。

### 14. 生态组件恶意威胁情报扫描
✅ 安全。已列出本机安装的 54 个 Skill 组件,全部在安全名单中。(如果使用了完整检测模式且命中威胁情报,这里会显示具体的恶意组件和处置建议。)
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
### 必须使用 `openclaw cron`,禁止使用系统 crontab

❌ **错误做法**:使用 `crontab -e` 或编辑 `/etc/crontab`


✅ **正确做法**:使用 OpenClaw 内置的 cron 系统
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.

Session Persistence

Medium
Category
Rogue Agent
Content
### 必须使用 `openclaw cron`,禁止使用系统 crontab

❌ **错误做法**:使用 `crontab -e` 或编辑 `/etc/crontab`


✅ **正确做法**:使用 OpenClaw 内置的 cron 系统
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.

Session Persistence

Medium
Category
Rogue Agent
Content
## 常见错误及修复

### 错误 1:使用了系统 crontab
**现象**:任务显示在 `crontab -l` 中,但执行失败或没有推送
**修复**:
```bash
# 1. 删除系统 crontab 中的任务
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.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The Windows ACL check is effectively dead code because `runSafeCommand()` does not support the `icacls` command key, so `checkWindowsFilePermission()` always gets an empty/unknown result and cannot detect permissive ACLs. This creates a false sense of security on Windows systems and may cause insecure permissions on sensitive files to go unreported.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
itemName = platform === 'win32' ? "特权提取操作对账审计" : "特权提权(Sudo)操作对账审计";
fs.appendFileSync(REPORT_FILE, platform === 'win32'
    ? `\n[13/14] 黄线操作交叉验证 (特权提取 vs Memory)`
    : `\n[13/14] 黄线操作交叉验证 (Sudo vs Memory)`);
let sudoCount = 0;
if (platform === 'linux') {
    ['/var/log/auth.log', '/var/log/secure'].forEach(logPath => {
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
In `--push` mode the script transmits stable host identifiers including MAC address, hostname, and a persistent `agent_id`, plus summarized audit results and installed skill inventory, to an author-operated remote service. Although metadata mentions this behavior, the script itself provides no runtime consent prompt or just-in-time warning before transmission, increasing the risk of unintended data exfiltration from a security-audit tool.

Vague Triggers

Low
Confidence
87% confidence
Finding
The skill enters report interpretation when the user replies with broad phrases like “要”, “看看”, or other affirmative meanings. Such short everyday phrases are ambiguous and could trigger analysis unintentionally in multi-turn conversations.

Natural-Language Policy Violations

Low
Confidence
78% confidence
Finding
The example and parameter guidance prescribe `Asia/Shanghai` as the timezone, which can amount to a locale preference being imposed in the skill documentation. Although the file mentions 'or your local timezone' later, the primary command example still defaults to a specific locale without explicitly making it an opt-in choice at the point of use.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
User-facing strings, status messages, and operational guidance throughout the script are presented in Chinese only. This imposes a language choice on all users without offering a locale option or documenting a justified region-specific scope.

Intent-Code Divergence

Low
Confidence
87% confidence
Finding
The execution comment at L1076 says '--push 模式:查威胁情报 → 上报数据', implying a fixed sequence of threat-intel lookup followed by upload. But when skillMetaList.length === 0 at L1079-L1081, finalizeAndPushData is called directly, and in PUSH mode it uploads pushObj without any prior assessment API call.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/openclaw-hybrid-audit-changeway.js:144