Back to skill

Security audit

Otp Challenger

Security checks for vulnerabilities and agentic risk

Overview

This skill implements an OTP workflow, but its security model is too broad and includes unsafe command and credential handling for protecting high-risk actions.

Install only after reviewing the security model. This skill should not be treated as strong per-action approval for deployments, payments, admin changes, or secret release unless you first remove live-code generation utilities, bind verification to a specific user/session/action, harden config parsing and failure hooks, and shorten or constrain the reusable verification window. Avoid storing OTP seeds in broad environment files or shell profiles, and do not enable OTP_FAILURE_HOOK unless the hook target is tightly controlled.

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

T09 · Insecure Skill Coding Practices

Error
Location
verify.sh:204
Finding

Environment-Controlled Configuration Path Enables Python Code Injection

Content
View full analysis
/dev/null; then SECRET=$(python3 -c " import sys try: import yaml with open('$CONFIG_FILE', 'r') as f: config = yaml.safe_load(f) secret = config.get('security', {}).get('otp', {}).get('secret', '') print(secret if secret else '') except Exception: pass " 2>/dev/null) fi fi YUBIKEY_CLIENT_ID="${YUBIKEY_CLIENT_ID:-}" YUBIKEY_SECRET_KEY="${YUBIKEY_SECRET_KEY:-}" if [ -z "$YUBIKEY_CLIENT_ID" ] && [ -f "$CONFIG_FILE" ]; then if command -v python3 &>/dev/null; then YUBIKEY_CLIENT_ID=$(python3 -c " import sys try: import yaml with open('$CONFIG_FILE', 'r') as f: config = yaml.safe_load(f) client_id = config.get('security', {}).get('yubikey', {}).get('clientId', '') print(client_id if client_id else '') except Exception: pass " 2>/dev/null) fi fi if [ -z "$YUBIKEY_SECRET_KEY" ] && [ -f "$CONFIG_FILE" ]; then if command -v python3 &>/dev/null; then YUBIKEY_SECRET_KEY=$(python3 -c " import sys try: import yaml with open('$CONFIG_FILE', 'r') as f: config = yaml.safe_load(f) secret_key = config.get('security', {}).get('yubikey', {}).get('secretKey', '') print(secret_key if secret_key else '') except Exception: pass " 2>/dev/null) fi fi ``` ### Technical Analysis `CONFIG_FILE` is derived from the environment-controlled `CONFIG_FILE` or `OPENCLAW_CONFIG` variable. Its value is interpolated directly into source code supplied to `python3 -c`. Shell quoting does not protect the resulting Python string. A path containing a single quote and valid Python syntax can terminate the argument to `open()`, inject addi ...[truncated 1405 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
verify.sh:18
Finding

Failure Hook Executes Unvalidated Environment-Controlled Commands

Content
View full analysis
Remediation
View remediation
&2 return 2 ;; esac if [ ! -f "$FAILURE_HOOK" ] || [ ! -x "$FAILURE_HOOK" ]; then echo "ERROR: Failure hook is not a regular executable file" >&2 return 2 fi OTP_HOOK_EVENT="$event" \ OTP_HOOK_USER="$user_id" \ OTP_HOOK_FAILURE_COUNT="$failure_count" \ OTP_HOOK_TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \ "$FAILURE_HOOK" & fi ``` Also: - Canonicalize the path and enforce an approved directory or explicit allowlist. - Verify expected ownership and reject group-writable or world-writable hooks. - Do not permit command-line arguments inside `OTP_FAILURE_HOOK`. - Launch the hook with a minimal environment that excludes OTP secrets. - Consider running hooks through a fixed dispatcher with predefined hook identifiers. - Record hook execution and failures in the audit log. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
verify.sh:374
Finding

Shell Error Handling Bypasses YubiKey Failure Recording and Rate Limiting

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
examples/openclaw/interceptor.sh:17
Finding

OpenClaw Interceptor Uses a Shared Default Identity Instead of the Requesting User

Content
View full analysis
/dev/null if [ $? -ne 0 ]; then # 3. Block the output and send a challenge instead echo "[SECURITY BLOCK]: The AI attempted to output sensitive data. Please provide your OTP code using 'verify_identity' to unlock this response." exit 1 fi fi ``` From `check-status.sh`: ```bash USER_ID="${1:-default}" ``` ### Technical Analysis The interceptor calls `check-status.sh` without a user identifier. Consequently, `check-status.sh` authorizes every intercepted response against the shared principal named `default`. The protected response is not bound to a trusted platform identity or session. In a multi-user or multi-session OpenClaw deployment, verification of `default` can therefore authorize sensitive output for unrelated users. The interceptor test suite only mocks an unauthorized status response. It does not test user isolation, session binding, or behavior when a different user has an active verification. ### Attack Path 1. A user with access to the valid OTP invokes `verify.sh default `. 2. The resulting state marks the shared `default` identity as verified. 3. A different user or session requests content containing a token or private-key pattern. 4. The PostGenerate interceptor calls `check-status.sh` without identifying the requesting user. 5. `check-status.sh` retrieves the valid state for `default` and exits successfully. 6. The interceptor releases the sensitive response to the unrelate ...[truncated 432 chars]
Remediation
View remediation
/dev/null ``` Required design changes include: - Remove the `default` fallback for authorization-sensitive uses. - Fail closed when no trusted user identity is available. - Do not derive identity from model-generated response text or other attacker-controlled content. - Bind verification records to both the authenticated user and the current session. - Ensure different users cannot write verification state for one another. - Add tests covering two users and two sessions, including verification of one identity followed by a disclosure attempt from another. - Use absolute or reliably resolved script paths so the intended status checker is invoked. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
verify.sh:395
Finding

Reusable Verification State Is Not Bound to a Specific Sensitive Action

Content
View full analysis
/dev/null) if [ -n "$ALREADY_USED" ]; then audit_log "VERIFY" "$USER_ID" "VERIFY_FAIL" echo "❌ OTP code already used (replay attack prevented)" >&2 exit 1 fi fi jq --arg userId "$USER_ID" \ --arg verifiedAt "$NOW_MS" \ --arg expiresAt "$EXPIRES_MS" \ --arg codeKey "$CODE_KEY" \ --arg codeExpiry "$CODE_EXPIRY" \ --arg nowMs "$NOW_MS" \ '.verifications[$userId] = {verifiedAt: ($verifiedAt | tonumber), expiresAt: ($expiresAt | tonumber)} | .usedCodes[$codeKey] = ($codeExpiry | tonumber) | .usedCodes |= with_entries(select(.value > ($nowMs | tonumber))) | del(.failureCounts[$userId])' \ "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE" ) 200>"$LOCK_FILE" ``` ### Technical Analysis A successful OTP verification creates a generic user-level authorization record containing only `verifiedAt` and `expiresAt`. It does not identify the operation, resource, parameters, session, requesting workflow, or challenge nonce for which the user supplied the OTP ...[truncated 1569 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (63)

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

High
Category
YARA Match
Confidence
82% confidence
Finding

Appending a source command to ~/.bashrc establishes persistent automatic loading of a file that contains OTP-related environment variables, potentially including secrets. While presented as convenience setup, shell-profile persistence increases the blast radius of file compromise, can unintentionally expose secrets to future shells and subprocesses, and resembles persistence behavior that deserves caution in a security-sensitive skill.

Content

Scanner excerpt · INSTALLATION.md (reported line 145)May include surrounding context.

file nano ~/.openclaw/config.yaml

text

Add this section:
```yaml
security:
  otp:
    secret: "YOUR_BASE32_SECRET_HERE"
    accountName: "your-email@example.com"
    issuer: "OpenClaw"
    intervalHours: 24

Option B: Environment Variables

bash
# Copy and customize the template
cp env-template.sh ~/.openclaw-otp-config.sh
nano ~/.openclaw-otp-config.sh

# Source in your shell profile
echo "source ~/.openclaw-otp-config.sh" >> ~/.bashrc
source ~/.bashrc

Option C: 1Password Integration

yaml
security:
  otp:
    secret: "op://Private/OpenClaw OTP/credential"
    accountName: "your-email@example.com"
    issuer: "OpenClaw"

Step 3: Add to Authenticator App

Scan the QR code generated in Step 1 with your preferred authenticator app:

  • Google Authenticator
  • Authy
  • 1Password
  • Bitwarden
  • Microsoft Authenticator
  • Any RFC 6238 compatible app

Step 4: Test Installation

bash
cd ~/.openclaw/skills/otp

# Get current TOTP code from your authenticator ap

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · INSTALLATION.md (reported line 279)May include surrounding context.

bash
# Test input validation
./verify.sh "../../../../etc/passwd" "123456"  # Should reject invalid user ID
./verify.sh "testuser" "'; rm -rf /*; echo '"  # Should reject code injection
./verify.sh "testuser" "12345"                 # Should reject wrong length

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · INSTALLATION.md (reported line 280)May include surrounding context.

bash
# Test input validation
./verify.sh "../../../../etc/passwd" "123456"  # Should reject invalid user ID
./verify.sh "testuser" "'; rm -rf /*; echo '"  # Should reject code injection
./verify.sh "testuser" "12345"                 # Should reject wrong length

# Test state file integrity

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · INSTALLATION.md (reported line 280)May include surrounding context.

bash
# Test input validation
./verify.sh "../../../../etc/passwd" "123456"  # Should reject invalid user ID
./verify.sh "testuser" "'; rm -rf /*; echo '"  # Should reject code injection
./verify.sh "testuser" "12345"                 # Should reject wrong length

# Test state file integrity

Chaining Abuse

High
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Content

Scanner excerpt · INSTALLATION.md (reported line 280)May include surrounding context.

bash
# Test input validation
./verify.sh "../../../../etc/passwd" "123456"  # Should reject invalid user ID
./verify.sh "testuser" "'; rm -rf /*; echo '"  # Should reject code injection
./verify.sh "testuser" "12345"                 # Should reject wrong length

# Test state file integrity

Self-Modification

High
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

Scanner excerpt · INSTALLATION.md (reported line 494)May include surrounding context.

fi

text

3. **Update skill documentation** to mention OTP requirements for sensitive actions

### Monitoring and Maintenance

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding

The declared description emphasizes challenging the user for fresh 2FA proof (TOTP/YubiKey) prior to sensitive operations. The supplied code does not implement a 2FA challenge or verification flow; it only removes a local session token file so later checks will treat the user as unauthorized. While this may indirectly support re-verification, the primary behavior shown is session invalidation/expiration, not direct two-factor proof collection. The cron-based trigger is also materially different from the described use in approval workflows immediately before sensitive actions.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code is not a general-purpose 2FA challenge mechanism for sensitive actions like deployments, financial operations, admin changes, or approval workflows. Instead, it is a response interceptor that performs content scanning for leaked credentials and blocks those outputs pending prior identity verification. While it does involve OTP-based verification, its primary purpose is output filtering/data loss prevention for detected secrets, which is a materially different behavior from the declared description. The hook trigger and resource usage (inspecting generated text and calling a local status script) are also more specific than the declared functionality.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The description says this skill should challenge users for fresh 2FA proof before sensitive operations, implying runtime verification/enforcement. The supplied code only provisions a TOTP secret for later use: it generates a random secret, displays enrollment information, creates an otpauth URI, and optionally shows a QR code. There is no code to prompt for a TOTP, verify a submitted code, enforce freshness, gate sensitive actions, or interface with a YubiKey. This is a materially different primary purpose, so the description does not accurately represent the code.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared purpose implies an authentication verification step where a user is prompted to provide fresh 2FA evidence before sensitive operations. The supplied code does not perform user challenge or proof verification; instead, it derives the valid TOTP itself from a stored/shared secret and outputs it. This is materially different because it enables code generation rather than independent user verification, and it could undermine the intended approval control if the system already has the secret. The description also mentions YubiKey support, which is absent from the code.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description says this skill performs fresh 2FA verification for approval workflows and sensitive actions. However, the actual code chunk does not implement any authentication, TOTP, YubiKey interaction, user challenge flow, approval gating, or identity verification. Instead, it is a maintenance/release-preparation script for assembling a sanitized copy of project files for upload. This is a materially different primary purpose and introduces filesystem staging capabilities that are unrelated to the declared security function.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared purpose is about performing fresh 2FA verification using TOTP or YubiKey before sensitive operations. The actual code chunk does not implement or test any 2FA challenge flow, user identity verification, TOTP handling, YubiKey interaction, approval workflow gating, or sensitive action authorization. Instead, it creates a temporary environment, mocks an unauthorized status check, and validates that an interceptor blocks strings resembling credentials or private keys. This is a materially different security function: secret scanning/interception based on regex and authorization state, not multifactor authentication challenge.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The optional OTP_FAILURE_HOOK is explicitly documented as a privileged script that runs arbitrary shell commands on verification failures. In a security-sensitive skill that processes attacker-controlled inputs such as user_id and OTP values, any hookable shell execution substantially increases the risk of command injection, unsafe automation, or abuse-triggered privileged actions; the surrounding 2FA context makes this more dangerous, not less, because it will likely be deployed in high-trust approval and admin workflows.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 27)May include surrounding context.

md
./verify.sh <user_id> <code>

Self-Modification

High
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

Scanner excerpt · docs/plans/2025-01-31-yubikey-support-design.md (reported line 145)May include surrounding context.

md
- Test with real YubiKey

### Phase 3: Documentation
- Update SKILL.md with YubiKey setup instructions
- Update README.md to mention YubiKey support
- Add troubleshooting for common issues

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

This script directly generates a valid TOTP from a long-lived shared secret, which defeats the stated purpose of challenging a user for fresh second-factor proof. In the context of an approval or identity-verification skill, any component that can mint OTPs locally effectively bypasses 2FA and enables impersonation of the enrolled user whenever the secret is available.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script reads the OTP seed from argv, environment variables, or a config file, exposing a long-lived authentication secret to the local process environment and any agent or tooling with read access. Because TOTP security relies on the seed remaining secret, this design turns the skill from verification into credential possession, allowing repeated generation of valid codes and durable 2FA bypass.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The current command outputs a live valid OTP for any supplied secret, effectively turning the tool into an OTP generator rather than a verifier. In a skill whose purpose is to challenge users for fresh 2FA proof before sensitive actions, this directly undermines the security boundary: anyone with access to the secret and tool can mint valid responses and bypass the intended second-factor challenge.

Content

No source excerpt is available for this finding.

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 · INSTALLATION.md (reported line 90)May include surrounding context.

Ubuntu/Debian:

bash
sudo apt update
sudo apt install jq oathtool python3 python3-pip python3-yaml

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 · INSTALLATION.md (reported line 91)May include surrounding context.

Ubuntu/Debian:

bash
sudo apt update
sudo apt install jq oathtool python3 python3-pip python3-yaml

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 · INSTALLATION.md (reported line 96)May include surrounding context.

Ubuntu/Debian:

bash
sudo apt update
sudo apt install jq oathtool python3 python3-pip python3-yaml

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 · INSTALLATION.md (reported line 97)May include surrounding context.

Ubuntu/Debian:

bash
sudo apt update
sudo apt install jq oathtool python3 python3-pip python3-yaml

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 · docs/implementation-plans/2025-01-31-yubikey-support/phase_01.md (reported line 290)May include surrounding context.

Ubuntu/Debian:

bash
sudo apt update
sudo apt install jq oathtool python3 python3-pip python3-yaml

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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 · INSTALLATION.md (reported line 106)May include surrounding context.

Step 1: Generate TOTP Secret

Use the included secret generator to create a new TOTP secret:

bash
cd ~/.openclaw/skills/otp

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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 · INSTALLATION.md (reported line 367)May include surrounding context.

md
**File Permissions:**
- State directory: `700` (owner only)
- State files: `600` (owner read/write only)
- Configuration files: `600` (contains secrets)
- Script files: `755` (executable)

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
config-template.yaml:50

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
docs/implementation-plans/2025-01-31-yubikey-support/phase_02.md:221

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
env-template.sh:88