Back to skill

Security audit

Paired: Phone Agent

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed phone-control skill, but it needs careful review because it combines persistent services, SMS/call authority, stored phone-unlock secrets, and some confusing safety documentation.

Install only on a Linux host and phone you own. Keep persistent services disabled until you understand each one, use paired-inbox-hook rather than the deprecated session-log command hook, keep trusted-numbers.conf very small, avoid storing a phone PIN unless you accept automatic unlock risk, enable respond_local_only if SMS content must not leave your machine, and protect Telegram/Gemini/HMAC/voice files with strict permissions.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
wrappers/paired-sms-command-hook.py:294
Finding

Legacy session-log dispatcher trusts forgeable sender metadata and can execute unauthorized phone actions

Content
View full analysis

Vulnerability Details

File Location: wrappers/paired-sms-command-hook.py, lines 294–303, 339–357, 700–713, and 745–763
Vulnerability Type: Authentication bypass through untrusted session content
Risk Level: Medium

Vulnerable Code Snippets

The legacy parser extracts the purported sender identity directly from message text:

python
mid_match = re.search(r'"message_id":\s*"?(\d+)"?', full_text)
sender_match = re.search(r'"sender_id":\s*"?(\d+)"?', full_text)
base = {
    "session_event_id": e.get("id"),
    "session_event_ts": e.get("timestamp"),
    "telegram_message_id": int(mid_match.group(1)) if mid_match else None,
    "sender_id": sender_match.group(1) if sender_match else None,
}

The same text-derived identity is then treated as an authorization check before executing the SMS sender:

python
if not ALLOWED_SENDER_ID or parsed.get("sender_id") != ALLOWED_SENDER_ID:
    log.warning(
        f"REJECTED /sms from sender_id={parsed.get('sender_id')} "
        f"(only {ALLOWED_SENDER_ID} is allowed)")
    record["result"] = {"ok": False, "error": "sender_not_allowed"}
    append_dispatch_log(record)
    return record

log.info(f"DISPATCH /sms: number={parsed['number']} body={parsed['body'][:60]!r}")
try:
    p = subprocess.run(
        [SMS_SEND_BIN, parsed["number"], parsed["body"], "--json", "--auto-unlock", "--relock"],
        capture_output=True, text=True, timeout=45,
    )

New session-log entries are parsed and dispatched as commands:

python
parsed = parse_message_line(line)
if parsed is None:
    continue
eid = parsed.get("session_event_id")
if eid and eid in seen_event_ids:
    continue
if eid:
    seen_event_ids.add(eid)

cmd_type = parsed.get("command", "sms")
log.info(f"Found /{cmd_type} event: msg_id={parsed.get('telegram_message_id')}")
if cmd_type == "sms":
    dispatch_sms(parsed, token, chat_id)
elif cmd_type in ("phone_dial", "phone_verb", "phone_say", "phone_attach"):
    dispatch_phone(parsed, token
...[truncated 3956 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove paired-sms-command-hook.py from the distributed package, or permanently disable its session-log dispatch functionality.
  2. Do not treat identifiers parsed from message bodies as authenticated sender metadata.
  3. Route every actionable command through the HMAC-signed inbox implemented by paired-inbox-hook.py.
  4. If backward compatibility is unavoidable:
    • Require an HMAC signature over the complete command payload.
    • Validate a short timestamp window and unique nonce.
    • Bind the destination, message body, action, and confirmation state into the signed payload.
    • Enforce trusted-number checks immediately before every SMS or call.
    • Do not permit session JSONL, chat memory, SMS bodies, notification text, or web content to act directly as a command source.
  5. Avoid passing --auto-unlock from any compatibility dispatcher unless the signed command explicitly authorizes unlocking.
  6. Add regression tests proving that forged "sender_id" text cannot authorize commands and that unsigned external content never reaches phone-action subprocesses.
  7. Update installation and migration instructions to remove any legacy services and direct users exclusively to paired-inbox-hook.service.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (150)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The document discloses powerful phone-control capabilities including Bluetooth pairing, OBEX transfer, ADB-over-Wi-Fi, SMS sending, and notification reading, but provides no mention of user-consent boundaries or safety controls. In skill context, these capabilities are highly sensitive because they can enable access to private communications or device actions if exposed without clear authorization and restriction elsewhere.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% 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 · bin/bt-adb-setup.py (reported line 79)May include surrounding context.

python
f.write("hello")
            tmp = f.name
        bt_adb.push(tmp, "/sdcard/Download/.paired-probe.txt")
        bt_adb.shell("rm /sdcard/Download/.paired-probe.txt")
        Path(tmp).unlink(missing_ok=True)
        capabilities.append(("File push", "OK", "bt-adb-push"))
    except Exception as e:

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · wrappers/paired-call-watch-tg-hook.sh (reported line 94)May include surrounding context.

sh
TEXT_RAW="$(printf '📞 Incoming call%s\nFrom: %s' "$PRETTY_TS" "$WHO")"
TEXT_JSON=$(python3 -c "import sys, json; print(json.dumps(sys.argv[1]))" "$TEXT_RAW")

RESPONSE=$(curl -sS -m 10 \
    -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
    -H "Content-Type: application/json" \
    -d "{\"chat_id\": ${TG_CHAT_ID}, \"text\": ${TEXT_JSON}}" 2>&1)

Ssd 3

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

By default, the script sends SMS content and conversation history to an external Gemini service unless respond_local_only is enabled. This is a real data-exposure issue because private user messages are transmitted off-host to a third party, which may violate user expectations, privacy requirements, or data-handling policies.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill includes an auto-unlock feature that reads a stored device PIN from disk and uses ADB UI automation to unlock the phone before sending messages. This materially expands the skill from SMS automation into credential handling and lock-screen bypass, increasing the blast radius if the agent, host account, or connected device is compromised and enabling unauthorized access to the device beyond the stated messaging task.

Content

No source excerpt is available for this finding.

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · wrappers/paired-sms-watch-tg-hook.sh (reported line 65)May include surrounding context.

sh
"$TYPE_LBL" "$PRETTY_TS" "$SENDER" "$ADDR" "$SUBJECT")"
TEXT_JSON=$(python3 -c "import sys, json; print(json.dumps(sys.argv[1]))" "$TEXT_RAW")

RESPONSE=$(curl -sS -m 10 \
    -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
    -H "Content-Type: application/json" \
    -d "{\"chat_id\": ${TG_CHAT_ID}, \"text\": ${TEXT_JSON}, \"disable_notification\": false}" \

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The changelog documents collection and local handling of highly sensitive biometric data (the user's voice) and storage of word-level voice clips, but it does not clearly warn users about the privacy, retention, and misuse risks associated with creating and storing voice clones. Even though the text emphasizes 'privacy-first' and 'all weights local,' that reassurance can cause users to underestimate the sensitivity of the data and the consequences of compromise, unauthorized reuse, or accidental disclosure.

Content

No source excerpt is available for this finding.

Session Persistence

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

md
| **Silent SMS send** (`sms_send_silent`) | Headless send on rooted / `WRITE_SMS` devices where the Intent UI isn't usable | **Off** unless `PAIRED_ALLOW_SILENT_SMS=1`; otherwise it refuses and points you to the UI send path |
| **Auto-unlock the phone** | Unlock a locked device to send/read | **Off** unless `--auto-unlock` is passed per invocation; PIN is read only from a mode-0600 file |
| **ADB device control** (`bt-*` / ADB) | Read notifications, send SMS, drive the screen | Owner-operated, own-device-only; shell-level access is inherent to the feature |
| **Persistent listeners** (systemd user services) | Real-time SMS push, incoming-call alerts, command hook | You enable each service yourself; the command hook acts only on HMAC-signed inbox messages, never on raw SMS/chat |
| **Relay to Telegram** | Receive phone events on *your own* Telegram | Your own bot token + chat only; it POSTs to the Telegram API and fetches **no** remote code |
| **Voice cloning** | Speak notes/calls in *your own* voice | Clone your own voice only; disclose AI-generated audio to recipients — **not** for impersonation |
| **Bluetooth auto-pair** | One-shot "pair this device" convenience | Default mode is **interactive**; `--mode auto` is an explicit opt-in (ideally with `--device-filter`) |

Session Persistence

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

md
- **Low-level primitives** at `skill/bin/bt-*.py` — BlueZ/ofono/ADB direct interfaces
- **High-level wrappers** at `skill/wrappers/paired-*.py` — JSON-clean interfaces designed for agents to call
- **Systemd unit files** at `skill/systemd/*.service.txt` — for persistent listeners (SMS push, call watch, command hook). The `.txt` suffix is a packaging convention; rename to `.service` when copying into `~/.config/systemd/user/` (see Installation below).

## Installation

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

bash
# 1. Symlink (or copy) the bin/ and wrappers/ scripts into ~/bin/, dropping .py from filenames
#    so the user/agent can invoke `paired-sms-send` rather than `paired-sms-send.py`.
mkdir -p ~/bin
for f in ~/.openclaw/workspace/skills/paired/bin/*.py; do
  ln -sf "$f" ~/bin/"$(basename "$f" .py)"
done

Session Persistence

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

paired-inbox-hook --keygen

4. Optional: enable the inbox hook (HMAC-signed command dispatcher)

systemctl --user enable --now paired-inbox-hook.service

text

The `.py`, `.sh`, and `.service.txt` extensions exist to satisfy the ClawHub packaging text-file allowlist; on disk in your `~/bin/` and `~/.config/systemd/user/` they should be the unsuffixed names referenced throughout this document. The same `.txt` suffix on `config-templates/*.conf.example.txt` and `engines/*.Dockerfile.txt` is there for the same reason — drop the trailing `.txt` when you copy a template, or pass the suffixed name to `docker build -f` directly.

Session Persistence

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

paired-inbox-hook --keygen

4. Optional: enable the inbox hook (HMAC-signed command dispatcher)

systemctl --user enable --now paired-inbox-hook.service

text

The `.py`, `.sh`, and `.service.txt` extensions exist to satisfy the ClawHub packaging text-file allowlist; on disk in your `~/bin/` and `~/.config/systemd/user/` they should be the unsuffixed names referenced throughout this document. The same `.txt` suffix on `config-templates/*.conf.example.txt` and `engines/*.Dockerfile.txt` is there for the same reason — drop the trailing `.txt` when you copy a template, or pass the suffixed name to `docker build -f` directly.

Session Persistence

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

paired-inbox-hook --keygen

4. Optional: enable the inbox hook (HMAC-signed command dispatcher)

systemctl --user enable --now paired-inbox-hook.service

text

The `.py`, `.sh`, and `.service.txt` extensions exist to satisfy the ClawHub packaging text-file allowlist; on disk in your `~/bin/` and `~/.config/systemd/user/` they should be the unsuffixed names referenced throughout this document. The same `.txt` suffix on `config-templates/*.conf.example.txt` and `engines/*.Dockerfile.txt` is there for the same reason — drop the trailing `.txt` when you copy a template, or pass the suffixed name to `docker build -f` directly.

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

bash
~/bin/bt-gatt-tree <MAC>                   # enumerate services + characteristics
~/bin/bt-gatt-read <MAC> <UUID>            # read a characteristic
~/bin/bt-gatt-write <MAC> <UUID> <HEX>     # write a characteristic

Audio

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

~/bin/bt-pair --connect # pair, trust, connect

text

2. **Write your config:**
```bash
cp config-templates/paired.conf.example.txt ~/.config/paired/paired.conf   # drop the trailing .txt on copy — it's a ClawHub packaging suffix
$EDITOR ~/.config/paired/paired.conf   # set phone_bt_mac, adapter, etc.

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% confidence
Finding

The command-hook service creates a persistent automated action surface that can send SMS and place calls. Although the document claims HMAC-signed inbox protection, this service materially increases risk because any weakness in inbox permissions, key handling, signature verification, or message parsing would turn a background service into a durable command-execution channel affecting a real phone.

Content

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

bash
   systemctl --user enable --now paired-sms-watch.service       # real-time SMS push
   systemctl --user enable --now paired-call-watch.service      # incoming call alerts
   systemctl --user enable --now paired-sms-command-hook.service # /sms /phone Telegram commands
  1. Verify:

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

This markdown file describes what the skill 'uses or depends on' but provides no specific invocation phrases, activation boundaries, or exclusion conditions. For a markdown skill description, the absence of any explicit trigger scope can make activation behavior unclear and increases the risk of unintended invocation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The file states that alsa-utils/arecord is used for capturing user microphone input during setup, which affects user privacy. However, the markdown does not include any warning, consent note, or disclosure about recording audio or how that captured data is handled.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This utility retrieves active phone notifications over ADB and prints their contents directly to stdout or JSON output, which can expose sensitive information such as message previews, MFA codes, email subjects, or app identifiers. Even if this is the tool's intended function, there is no user-facing warning, confirmation, or masking, so accidental disclosure to terminals, logs, shell history capture, or downstream tooling is plausible.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code outputs inbox or sent SMS metadata and bodies directly to stdout, including a JSON mode that can expose private communications in terminal history, logs, or downstream tooling. While the file docstring describes listing SMS, there is no explicit warning in the user-facing output or comments that sensitive message contents will be displayed.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
81% confidence
Finding

Running as a persistent user systemd service and registering as a D-Bus default agent increases the attack surface over time because the pairing logic remains available beyond a single supervised setup action. In security-sensitive contexts, persistence makes accidental exposure or misuse of auto-pairing features more dangerous than a one-shot manual tool.

Content

Scanner excerpt · bin/bt-agent.py (reported line 5)May include surrounding context.

python
"""
bt-agent — BlueZ pairing agent daemon for the OpenClaw `bluetooth` skill.

Runs as a user systemd service. Registers a D-Bus agent so that pair
requests from peer devices (phones, headphones, beacons) are handled
without a human at the keyboard.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
94% confidence
Finding

The documented --mode auto behavior is a real security risk because it allows the agent to approve Bluetooth pairing requests without human verification. In this context, a pairing agent running as a persistent default BlueZ agent can let nearby devices become paired and subsequently authorized for profile access, especially if used without a device filter.

Content

Scanner excerpt · bin/bt-agent.py (reported line 16)May include surrounding context.

python
* stderr (so journalctl shows it)
                Confirms when ~/.cache/bluetooth/confirm exists, denies when
                ~/.cache/bluetooth/deny exists. Times out after 60s -> deny.
  --mode auto   NoInputNoOutput agent. Auto-confirms pairing.
                Works for: BLE peripherals, headphones, speakers, most
                beacons, and Just Works pairings.
                IMPORTANT: --mode auto without --device-filter will accept

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 · bin/bt-agent.py (reported line 30)May include surrounding context.

python
Useful CLI flags:
  --device-filter=AA:BB:CC:DD:EE:FF
        Only auto-confirm pair requests from this MAC; everything else is
        denied. Useful when you want a one-shot "let me pair THIS phone".
"""
from __future__ import annotations

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 · bin/bt-agent.py (reported line 216)May include surrounding context.

python
Useful CLI flags:
  --device-filter=AA:BB:CC:DD:EE:FF
        Only auto-confirm pair requests from this MAC; everything else is
        denied. Useful when you want a one-shot "let me pair THIS phone".
"""
from __future__ import annotations

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
96% confidence
Finding

This code path automatically approves RequestConfirmation in auto mode, meaning Just Works or similar pairing flows can complete without user presence. Because the agent also acts as the default system Bluetooth agent, an attacker in radio range could opportunistically pair a device when the service is active, particularly if no device filter is configured.

Content

Scanner excerpt · bin/bt-agent.py (reported line 177)May include surrounding context.

python
"org.bluez.Error.Rejected", "device filter blocked"
            )
        if self.mode == "auto":
            _log(f"RequestConfirmation {mac} passkey={passkey:06d} -> AUTO-CONFIRM")
            return
        self._publish_pending({
            "event": "RequestConfirmation",

Static analysis

No suspicious patterns detected.