Back to skill

Security audit

earn-hunter

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its OKX earn-monitoring purpose, but it creates persistent scheduled execution and has concrete implementation risks that could affect local commands, credentials, scheduled jobs, and live financial actions.

Install only if you are comfortable granting this skill access to your authenticated OKX CLI and allowing it to create recurring jobs. Before enabling background scheduling or purchase flows, require fixes for env.snapshot sourcing, safer crontab management, explicit confirmation for persistence and financial actions, safer Telegram token handling, and integrity-pinned dependency installation.

Vulnerability Patterns
  • 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
  • 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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/scan.sh:37
Finding

Persistent Arbitrary Command Execution Through Sourced Environment Snapshot

Content
View full analysis
~/.okx/earn-hunter/env.snapshot << SNAP # auto-generated by earn-hunter activation — $(date -Iseconds) OKX_BIN=$(command -v okx) NODE_BIN=$(command -v node) JQ_BIN=$(command -v jq) ACTIVATION_PATH=$PATH SNAP ``` ```bash # scripts/scan.sh:37-40 _EH_SNAPSHOT="${EH_STATE_DIR:-$HOME/.okx/earn-hunter}/env.snapshot" # shellcheck disable=SC1090 [[ -f "$_EH_SNAPSHOT" ]] && source "$_EH_SNAPSHOT" ``` ### Technical Analysis The activation procedure generates `env.snapshot` by interpolating executable paths and the current `PATH` without shell escaping. The scanner subsequently loads this file using `source`, which treats its contents as shell code rather than inert configuration data. If an executable path or `PATH` entry contains shell metacharacters, command substitution, or statement delimiters, those characters are written into the snapshot and interpreted when the scheduled scanner sources it. The problem is especially significant because the scanner can be registered in OS crontab or a macOS LaunchAgent, causing injected commands to execute repeatedly across sessions. The file also resides in a user-controlled state directory without an explicit ownership, regular-file, or restrictive-permission check before it is sourced. ### Attack Path 1. An attacker who can influence the activation environment places a maliciously named directory or executable earlier in `PATH`, or modifies `~/.okx/earn-hunter/env.snapshot` after activation. 2. `command -v` or `$PATH` produces text containing shell syntax, such as command substitution or an additional command. 3. Activation writes that text to `env.snapshot` without escaping it. 4. The Skill installs or invokes the recurring scanner. 5. `scan.sh` executes `source "$_EH_SNAPSHOT"`. 6. Bash parses t ...[truncated 553 chars]
Remediation
View remediation
~/.okx/earn-hunter/env.snapshot.json chmod 600 ~/.okx/earn-hunter/env.snapshot.json ``` 3. Read individual values using the already resolved trusted `jq` binary, without evaluating their contents. 4. If shell assignments must be retained, serialize every value with `printf '%q'`; replacing `source` remains preferable. 5. Create `~/.okx/earn-hunter` with mode `0700` and snapshot/configuration files with mode `0600`. 6. Before reading the snapshot, verify that it is a regular file, is owned by the current user, is not a symbolic link, and is not writable by group or other users. 7. Validate resolved paths against an allowlist of expected absolute-path characters and verify the ownership and executable type of each selected binary. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/scan.sh:304
Finding

Telegram Bot Token Exposed in Curl Process Arguments

Content
View full analysis
/dev/null) tg_chat_env=$(jq -r '.notify.telegram.chat_id_env // "TELEGRAM_CHAT_ID"' "$PLATFORM_FILE" 2>/dev/null) local TOKEN CHAT_ID TOKEN=$(printenv "$tg_token_env" 2>/dev/null) CHAT_ID=$(printenv "$tg_chat_env" 2>/dev/null) if [[ "${EH_DRY_RUN:-0}" == "1" ]]; then echo "=== [DRY-RUN TG] chat=$CHAT_ID ===" printf '%s\n' "$msg" log_notify "TG" "OK" "$detail (dry-run)" return 0 fi local resp resp=$(curl -s "https://api.telegram.org/bot${TOKEN}/sendMessage" \ --data-urlencode "chat_id=${CHAT_ID}" \ --data-urlencode "text=${msg}" \ -d "parse_mode=HTML" 2>/dev/null) ``` ### Technical Analysis Although the token is initially obtained from an environment variable, it is interpolated into the Telegram API URL passed directly to `curl`. Consequently, the bot token becomes part of curl's argument vector. Depending on operating-system process visibility, tracing configuration, endpoint-monitoring software, crash collection, or execution wrappers, command-line arguments may be observable while curl is running. The use of an environment variable therefore does not keep the secret out of process metadata. The Telegram Bot API treats the token as an authentication credential. Disclosure allows calls to the bot API without access to the original host. ### Attack Path 1. A scheduled scan finds an opportunity or generates an error notification. 2. `send_telegram` reads the bot token from the configured environment variable. 3. The script starts curl with the token embedded in the command-line URL. 4. A local process observer, diagnostic agent, tracing system, or command-execution logger records the curl argument vector. 5 ...[truncated 620 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:13
Finding

Automatic Installation of Executable Third-Party Dependencies Without Integrity Verification

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:312
Finding

Crontab Installation and Removal Can Overwrite Unrelated Scheduled Jobs

Content
View full analysis
/dev/null; echo "0 * * * * PATH=$CRON_PATH OKX_PROFILE=live ~/.okx/earn-hunter/scan.sh >> ~/.okx/earn-hunter/cron.log 2>&1") | crontab - if [[ "$(uname -s)" == "Darwin" ]] && ! launchctl list com.vix.cron >/dev/null 2>&1; then # cron daemon not running — fallback to LaunchAgent fi ``` ```bash # SKILL.md:475-477 crontab -l | grep -v 'earn-hunter' | crontab - ``` The same unsafe management pattern is repeated in `references/scheduler-setup.md`: ```bash (crontab -l 2>/dev/null; echo "0 * * * * OKX_PROFILE=live ~/.okx/earn-hunter/scan.sh >> ~/.okx/earn-hunter/cron.log 2>&1") | crontab - ``` ```bash crontab -l | grep -v 'earn-hunter' | crontab - ``` ### Technical Analysis The installation pipeline suppresses errors from `crontab -l` and unconditionally pipes the combined output into `crontab -`. If listing the current crontab fails for a reason other than the user having no existing crontab, the pipeline may install only the new Earn Hunter line and discard existing entries. Repeated activation does not check for an existing managed entry, so duplicate jobs can be added. This can produce repeated scans, duplicate API traffic, and duplicate notifications. The pause command removes every line containing the substring `earn-hunter`. It does not use an exact command match or a uniquely delimited managed block. Unrelated jobs whose comments, paths, or commands contain that string can therefore be deleted. ### Attack Path #### Existing-job loss during installation 1. The user already has scheduled jobs. 2. `crontab -l` fails because of a temporary permission, service, storage, or command error. 3. The error is hidden by `2>/dev/null`. 4. The shell ...[truncated 874 chars]
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (78)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
ion/config management. The recurring **scan itself is performed entirely by `scripts/scan.sh`** (shell + jq) — `jq` is required for scanning. Verify with `which

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
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 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Confidence
75% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

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

md
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 Parameter Abuse

High
Category
Tool Misuse
Confidence
91% confidence
Finding

The uninstall flow includes a direct rm -f ~/Library/LaunchAgents/com.okx.earn-hunter.plist shell deletion command. Even though the path is fixed, destructive file operations in response to vague triggers like 'uninstall' are risky in agent contexts, and if similar patterns expand later they can become a vehicle for arbitrary or mistaken deletion.

Content

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

md
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

Chaining Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Chaining launchctl unload ... && rm -f ... combines state-changing operations in one shell command, making partial failures harder to reason about and review. In an agent setting, command chaining increases the risk that a privileged action proceeds without granular validation or clear user visibility.

Content

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

md
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 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Confidence
75% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · references/notify-channels.md (reported line 13)May include surrounding context.

md
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

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The document claims earn-hunter does not directly perform write operations, but later includes direct transfer and redeem commands in its own flow. This contradiction can bypass user expectations and reviewer assumptions, making the skill more dangerous because a supposedly advisory skill is effectively authorized to move funds before final purchase steps complete.

Content

No source excerpt is available for this finding.

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · references/scan-logic.md (reported line 280)May include surrounding context.

md
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

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · references/scan-logic.md (reported line 287)May include surrounding context.

md
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

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · references/scan-logic.md (reported line 295)May include surrounding context.

md
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 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Confidence
75% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · references/scheduler-setup.md (reported line 6)May include surrounding context.

md
# 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

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill explicitly directs the agent to perform shell actions such as installing packages, copying scripts, editing files, invoking cron, and loading LaunchAgents, but it declares no explicit tool scope or permission boundary. That mismatch increases the chance the skill is granted broader execution capability than users expect, making destructive or persistent operations easier to trigger.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The activation trigger list includes broad natural-language phrases like 'monitor earn' and 'notify me about earn' that could match ordinary conversation and invoke a skill that installs software, writes files, and establishes recurring jobs. Because the skill performs persistent system changes, accidental invocation materially raises risk.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
82% confidence
Finding

Initializing ~/.okx/earn-hunter/ and writing config/state files creates durable state on disk. Disk persistence is expected here, but it is still security-relevant because it stores operational behavior and can support later automated execution when combined with the scheduler steps.

Content

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

md
- 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)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The routing table maps very broad phrases like 'subscribe', 'stop', and 'uninstall' to privileged behaviors without sufficient contextual qualification. In a live trading and persistence-oriented skill, such generic triggers could cause unwanted financial workflow handoff, disable monitoring, or remove scheduled jobs and files based on ambiguous user input.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% confidence
Finding

Writing platform.json is another form of persistent local state that controls later scheduling and notification behavior. In context it is less severe than cron installation, but it still contributes to durable automation configuration.

Content

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

md
### 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

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

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.

Content

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

md
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.

Session Persistence

Medium
Category
Rogue Agent
Confidence
98% confidence
Finding

The skill instructs the agent to install an OS crontab entry that runs hourly and persists beyond the current session. Persistent scheduled execution is security-sensitive because it enables continued command execution and external notifications even when the user is absent; in an agent-skill context this is exactly the kind of long-lived automation that requires strong consent and tight scoping.

Content

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

Step A: Try crontab + verify cron daemon (macOS)

bash
(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:

Session Persistence

Medium
Category
Rogue Agent
Confidence
97% confidence
Finding

Generating a LaunchAgent plist is a direct persistence mechanism on macOS. In a skill that can execute shell commands, this allows unattended recurring execution outside the original user interaction, which is inherently sensitive.

Content

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

Step B: macOS LaunchAgent fallback (scheduler.type = "launchagent")

Generate ~/Library/LaunchAgents/com.okx.earn-hunter.plist with the resolved paths:

bash
SCAN_SCRIPT="$HOME/.okx/earn-hunter/scan.sh"