Back to skill

Security audit

手机操控

Security checks for vulnerabilities and agentic risk

Overview

This skill is meant to control a connected Android phone, but it can read screen contents and act inside real apps without clear confirmation safeguards.

Install only if you intend to let an agent operate a real Android phone over USB. Keep the phone visible, avoid banking/payment and private messaging apps unless you explicitly approve each action, and manually remove temporary screenshot/XML files after use.

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
agent.py:95
Finding

Insecure Temporary-File Creation Enables Symlink Race Attacks

Content
View full analysis

Vulnerability Details

File Location: agent.py, lines 95 and 106
Vulnerability Type: Insecure temporary-file creation
Risk Level: Medium

Vulnerable Code

python
def get_screenshot(save_path=None):
    """Get a screenshot and return its path."""
    if not save_path:
        save_path = tempfile.mktemp(suffix='.png')
    out, _, code = adb(['exec-out', 'screencap', '-p'], binary=True)
    if code == 0 and out:
        with open(save_path, 'wb') as f:
            f.write(out)
        return save_path
    return None
python
def get_uidump(save_path=None):
    """Get the UI hierarchy XML."""
    if not save_path:
        save_path = tempfile.mktemp(suffix='.xml')
    _, _, code = adb(['exec-out', 'uiautomator', 'dump', '/sdcard/dump.xml'])
    if code != 0:
        return None
    _, _, code = adb(['pull', '/sdcard/dump.xml', save_path])
    if code == 0 and os.path.exists(save_path):
        return save_path
    return None

Technical Analysis

tempfile.mktemp() generates a candidate pathname but does not atomically create or reserve the corresponding file. A time-of-check-to-time-of-use window therefore exists between generating the path and writing screenshot data with open() or passing the path to adb pull.

A local attacker who can access the shared temporary directory could create a symbolic link or another filesystem object at the generated path during this window. The later operation may then use the attacker-controlled destination. Exploitation depends on the attacker's local access, ability to identify or predict the temporary pathname, filesystem protections, and the privileges under which the skill runs.

Attack Path

  1. A user invokes the screenshot or uidump command.
  2. The process calls tempfile.mktemp() and receives a pathname that has not been securely reserved.
  3. Before open(save_path, 'wb') or adb pull ... save_path executes, a local attacker places a symbolic link at that pathname.

...[truncated 903 chars]

Remediation
View remediation

Remediation Suggestions

  • Replace tempfile.mktemp() with an API that atomically creates the temporary file, such as tempfile.mkstemp() or tempfile.NamedTemporaryFile(delete=False).
  • Retain and use the securely created file descriptor where possible, rather than closing it and reopening the path.
  • Restrict temporary-file permissions to the owning user, preferably mode 0600.
  • For adb pull, securely create a private temporary directory with tempfile.TemporaryDirectory() and place the destination file inside it.
  • Remove screenshots and UI dumps promptly after use because they may contain sensitive device data.
  • When accepting an explicit save_path, validate the destination according to the intended trust boundary and avoid following untrusted symbolic links where supported.

Example hardening pattern:

python
fd, save_path = tempfile.mkstemp(suffix='.png')
try:
    os.fchmod(fd, 0o600)
    with os.fdopen(fd, 'wb') as f:
        f.write(out)
except Exception:
    os.close(fd)
    raise

For UI dumps, use a private temporary directory:

python
temp_dir = tempfile.mkdtemp(prefix='phone-agent-')
os.chmod(temp_dir, 0o700)
save_path = os.path.join(temp_dir, 'dump.xml')
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (13)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill instructs the agent to send messages, open apps, stop apps, and perform taps/text input on a real device, but it does not warn about irreversible effects on user data, accounts, conversations, or transactions. Because it targets consumer apps like WeChat and Alipay, accidental or automated actions could directly affect private communications or financial workflows.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill clearly relies on shell-capable operations through adb and inline Python, but it declares no explicit tool scope or permission boundaries. Without an allowlist, an agent framework may grant broader-than-necessary execution capability, increasing the chance of unintended device control or command execution beyond the documented phone actions.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The activation text is broad enough to match many ordinary phone-related requests, which can cause the skill to trigger in situations where direct device control is unnecessary or unsafe. In this context, over-broad routing is dangerous because the skill can open apps, input text, and interact with real accounts on a connected phone.

Content

No source excerpt is available for this finding.

File System Enumeration

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.

Content

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

adb version

检查 skill 脚本

ls -la ~/.openclaw/skills/phone-agent/agent.py

text

---

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · agent.py (reported line 36)May include surrounding context.

python
def find_device():
    """自动查找已连接的设备"""
    result = subprocess.run(['adb', 'devices'], capture_output=True, text=True)
    lines = result.stdout.strip().split('\n')[1:]
    for line in lines:
        if 'device' in line and not 'unauthorized' in line:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · agent.py (reported line 64)May include surrounding context.

python
for attempt in range(retry):
        try:
            if binary:
                result = subprocess.run(cmd, capture_output=True, timeout=30)
                return result.stdout, b'', result.returncode
            else:
                result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · agent.py (reported line 67)May include surrounding context.

python
result = subprocess.run(cmd, capture_output=True, timeout=30)
                return result.stdout, b'', result.returncode
            else:
                result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
                return result.stdout, result.stderr, result.returncode
        except subprocess.TimeoutExpired:
            if attempt < retry - 1:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · agent.py (reported line 85)May include surrounding context.

python
def _check_devices_raw():
    """直接调用 adb devices,避免递归"""
    try:
        result = subprocess.run(['adb', 'devices'], capture_output=True, text=True, timeout=10)
        return result.stdout
    except:
        return ""

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill captures full-screen device contents and writes them to a local temporary file, which can include messages, credentials, MFA codes, personal images, or other sensitive data. In a phone-control skill, this capability is inherently more dangerous because screenshot capture is central to operation and may occur frequently, increasing the chance of unintended retention or exposure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Dumping and pulling the UI hierarchy to a local XML file can expose visible text, accessibility labels, resource identifiers, and screen structure that may reveal sensitive user data or workflow context. In this skill context, UI dumps are especially sensitive because they are designed to extract semantic information from the phone interface, making privacy leakage more actionable than a raw screenshot alone.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The docstring states that the function uses uiautomator2 Accessibility APIs to directly set EditText content and 'perfectly supports Chinese'. However, the implementation later falls back to _input_pinyin_fallback on ImportError or runtime failure, which types a pinyin transliteration rather than the original Chinese text. This is an active contradiction between the documented intent and actual behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The stop_app function runs am force-stop against a package name, which is a destructive operational action because it terminates the app immediately and may interrupt user activity. The help text and function implementation do not provide any warning or confirmation about this disruptive effect.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The usage text explicitly highlights Chinese-language support in a way that centers a specific language/locale rather than presenting language handling as optional or user-selectable. Under the stated policy, language-specific behavior should offer user choice or be clearly justified as region-specific, which is not provided here.

Content

No source excerpt is available for this finding.