T01 · Skill Instruction Hijacking
- Location
scripts/scan.sh:170- Finding
Hard-Coded Promotional URL Injected into Recurring Notifications
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly matches its OKX Earn monitoring purpose, but it combines financial account access, recurring background execution, credential-handling risks, and an undisclosed hard-coded OKX link that users should review before installing.
Install only if you are comfortable with a skill that uses your OKX CLI authentication, stores local monitoring state, sends financial opportunity notifications to Telegram/Lark or chat, and can create recurring background jobs. Before enabling it, prefer pinned installs, avoid exposing OKX API-key config output, review the hard-coded OKX link, and confirm exactly which scheduler and notification channel will be used.
scripts/scan.sh:170Hard-Coded Promotional URL Injected into Recurring Notifications
SKILL.md:272Recurring Cross-Session Execution Is Installed Without a Dedicated Persistence Confirmation
scripts/scan.sh:37Writable State File Is Executed as Shell Code by Every Scheduled Scan
SKILL.md:47Authentication Detection Can Expose the API Key to Agent Context and Logs
SKILL.md:13Activation Installs Unpinned Executable Dependencies and Optional Skills
SKILL.md:312Crontab Management Is Non-Idempotent and Can Delete Unrelated Jobs
Referenced artifact was not completely inspected
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which
Referenced artifact was not completely inspected
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which
Referenced artifact was not completely inspected
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which
Referenced artifact was not completely inspected
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which
Referenced artifact was not completely inspected
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which
Referenced artifact was not completely inspected
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which
Referenced artifact was not completely inspected
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which
Referenced artifact was not completely inspected
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
b, no CLI commands). The job runs as an **isolated, light-context** agent turn and delivers its output back to the conversation channel via cron **`announce`** delivery. notify.channel defaults to `"session"` so the scan prints to stdout for `announce` to push (avoids double-send).
**Claude Code / Hermes / Generic (`claude-code.default.json`):**
- scheduler.type = `"cron"` — scheduled via **OS crontab → `scripts/scan.sh`** (zero LLM token cost), notification via TG / Lark curl from the script itself.
### Notification Channels (independent of platform)
Detect in priority order (PRD requirement: TG first):
1. **Telegram** — `$TELEGRAM_BOT_TOKEN` and `$TELEGRAM_CHAT_ID` both set → TG ready
2. **Lark** — `platform.notify.lark_webhook` non-empty → Lark ready
3. **Session** — fallback, only works in interactive mode
TG and Lark are **standalone push channels** — they work regardless of whether the agent client is open. On OS-crontab platforms, scheduled scans send notific
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).
When user says "卸载" / "uninstall":
1. Stop the scheduler (same as Pause). For LaunchAgent, also remove the plist:
`launchctl unload ~/Library/LaunchAgents/com.okx.earn-hunter.plist && rm -f ~/Library/LaunchAgents/com.okx.earn-hunter.plist`
2. Ask: "是否保留配置和历史数据?"
- Yes → only remove scheduler
- No → also remove `~/.okx/earn-hunter/` directory
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.
When user says "卸载" / "uninstall":
1. Stop the scheduler (same as Pause). For LaunchAgent, also remove the plist:
`launchctl unload ~/Library/LaunchAgents/com.okx.earn-hunter.plist && rm -f ~/Library/LaunchAgents/com.okx.earn-hunter.plist`
2. Ask: "是否保留配置和历史数据?"
- Yes → only remove scheduler
- No → also remove `~/.okx/earn-hunter/` directory
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
ding which applies avoids confusion.
### 1. Interactive Session (user is in a live conversation)
The agent outputs markdown directly in the conversation. Works on all platforms (OpenClaw, Claude Code, Hermes, Generic). Full interactivity — user can reply to subscribe immediately.
### 2. OS Crontab (scheduled scan, no LLM session) — Claude Code / Hermes / Generic
Scheduled scans run via OS crontab (no LLM session). **Always use direct curl to TG Bot API or Lark Webhook** for notifications. `scripts/scan.sh` does the curl itself.
### 3. OpenClaw In-Session Cron (`announce` delivery)
On OpenClaw the scheduled scan runs as an **isolated cron agent turn** created via the in-session `cron` tool. Delivery is via the cron job's **`announce`** mode, which pushes the turn's output back to the conversation channel that created the job. `platform.json` `notify.channel` is `"session"` so `scripts/scan.sh` prints the notification to stdout for the turn to relay — **do not curl TG/Lark fr
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.
current_flash_ids = [p.id for each in flash_results]
For each key in state.flash:
Extract id from key (split by ":" → first element)
If id not in current_flash_ids → delete state.flash[key]
Skip any key starting with "test:" (Test Mode immunity)
# 6b. Fixed diff cleanup: key-level
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.
current_flash_ids = [p.id for each in flash_results]
For each key in state.flash:
Extract id from key (split by ":" → first element)
If id not in current_flash_ids → delete state.flash[key]
Skip any key starting with "test:" (Test Mode immunity)
# 6b. Fixed diff cleanup: key-level
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.
current_flash_ids = [p.id for each in flash_results]
For each key in state.flash:
Extract id from key (split by ":" → first element)
If id not in current_flash_ids → delete state.flash[key]
Skip any key starting with "test:" (Test Mode immunity)
# 6b. Fixed diff cleanup: key-level
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
# Scheduler Setup
Two scheduling models, selected by `platform.json` `.scheduler.type`:
- **`openclaw-cron`** (OpenClaw) — scheduled via the in-session **`cron` agent tool**, isolated + light-context, delivered back to the conversation via `announce`. See [OpenClaw](#openclaw-in-session-cron-tool).
- **`cron`** (Claude Code / Hermes / Generic) — scheduled via **OS crontab + `okx` CLI + curl notifications**. No LLM sessions spawned — zero token cost. See [OS Crontab](#os-crontab-configuration).
For OS-crontab platforms, agent-platform `/loop` and cloud Routines are **not recommended**: each tick spawns an LLM session and isolated sessions cannot reliably push TG/Lark notifications. (OpenClaw is the deliberate exception — its in-session cron + `announce` delivery is the supported path.)
## OpenClaw (in-session cron tool)
OpenClaw does **not** use OS crontab or the `openclaw` CLI (the CLI cron path has permission issues here). Scheduling is created
The skill explicitly instructs the agent to execute numerous shell commands, install packages, edit files under the user's home directory, and register scheduled jobs, yet it declares no tool scope or allowed-tools restrictions. That mismatch weakens least-privilege controls and increases the blast radius if the skill is invoked unexpectedly or if later content changes introduce more dangerous commands.
The trigger list contains broad phrases such as 'monitor earn', 'notify me about earn', and similar natural-language variants that can match ordinary conversation without clear intent to activate a powerful automation skill. Because activation can lead to installs, auth checks, config writes, and scheduler setup, accidental routing materially increases risk.
The skill persists configuration, state, platform metadata, scripts, and environment snapshots under ~/.okx/earn-hunter. Persistent writes are necessary for the feature, but they create long-lived local state that can later influence unattended execution and may expose operational details such as resolved binary paths.
- No API key + `okx auth status --json` → `"status":"logged_in"` → **OAuth mode**. No `--profile` flag needed.
- Neither → **stop**. Load `okx-cex-auth` skill and follow login steps.
5. Init config and state:
- If `~/.okx/earn-hunter/` directory does not exist → `mkdir -p ~/.okx/earn-hunter`
- If `~/.okx/earn-hunter/config.json` does not exist → copy `{baseDir}/config/default.json` to it
- If `~/.okx/earn-hunter/state.json` does not exist → write `{"flash":{},"fixed":{},"flexible":{},"consecutive_failures":0,"last_error":""}`
- If `~/.okx/earn-hunter/platform.json` does not exist → run [Platform Detection](#platform-detection-active-probe--user-confirmation)
Writing platform.json establishes durable behavior for later runs, including scheduler and notification configuration. In isolation this is low risk, but in this skill it contributes to a larger persistent automation chain that can later drive unattended scans and outbound messaging.
### Step 1 — Platform Detection & Confirmation
See [Platform Detection](#platform-detection-active-probe--user-confirmation). Probe environment → ask user to confirm → write `platform.json`.
### Step 2 — Detect Notification Channel & Confirm
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
5. User confirms → proceed to Step 5
6. Not received → troubleshoot (see `notify-channels.md`)
7. 5 min no response → ping once
8. Session channel → skip confirmation
**Note:** The smoke test ignores `verboseLog` setting — it always produces output to verify the full pipeline works end-to-end.
The skill directs the agent to install a recurring crontab entry that executes a local script hourly and writes logs, creating durable automated execution outside the immediate chat session. Persistence is expected for a monitoring skill, but it is still security-sensitive because it can continue running, accessing auth context, and sending outbound notifications after the user disengages.
Step A: Try crontab + verify cron daemon (macOS)
(crontab -l 2>/dev/null; echo "0 * * * * PATH=$CRON_PATH OKX_PROFILE=live ~/.okx/earn-hunter/scan.sh >> ~/.okx/earn-hunter/cron.log 2>&1") | crontab -
On macOS (uname -s == Darwin), immediately check if the cron daemon is running:
Generating a LaunchAgent plist is a concrete persistence mechanism that causes future execution of the scan script independent of user presence. In security analysis, background agents are inherently sensitive because they combine file persistence, recurring execution, and access to the user's existing auth/session context.
Step B: macOS LaunchAgent fallback (scheduler.type = "launchagent")
Generate ~/Library/LaunchAgents/com.okx.earn-hunter.plist with the resolved paths:
SCAN_SCRIPT="$HOME/.okx/earn-hunter/scan.sh"
This line is part of the here-doc that writes the LaunchAgent plist, which implements durable background execution. Even though the use case is legitimate monitoring, the persistence mechanism materially increases risk if the skill is mis-invoked or later altered.
LOG_FILE="$HOME/.okx/earn-hunter/cron.log"
INTERVAL=3600 # derive from scheduler.interval: "1h"→3600, "30m"→1800, "10m"→600
cat > ~/Library/LaunchAgents/com.okx.earn-hunter.plist << PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
This line is part of the here-doc that writes the LaunchAgent plist, which implements durable background execution. Even though the use case is legitimate monitoring, the persistence mechanism materially increases risk if the skill is mis-invoked or later altered.
LOG_FILE="$HOME/.okx/earn-hunter/cron.log"
INTERVAL=3600 # derive from scheduler.interval: "1h"→3600, "30m"→1800, "10m"→600
cat > ~/Library/LaunchAgents/com.okx.earn-hunter.plist << PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
No suspicious patterns detected.