Back to skill

Security audit

Remote Install

Security checks for vulnerabilities and agentic risk

Overview

The skill’s remote-install purpose is coherent, but it gives broad software-installation and remote-control automation with unsafe handling of credentials and installer execution.

Install only if you are comfortable with a Review-level remote administration tool. Use it only on trusted machines, with trusted installer files, and avoid providing reusable RustDesk passwords until the skill is changed to redact secrets, require explicit approval for each installer, verify installer provenance, and avoid shell=True command construction.

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/remote_installer.py:69
Finding

RustDesk Credentials Are Persisted in Plaintext Logs

Content
View full analysis

Vulnerability Details

File Location: scripts/remote_installer.py, lines 69-70; logging destination configured at lines 250-257
Vulnerability Type: Plaintext sensitive-data exposure
Risk Level: High

Vulnerable Code

python
def connect(self, remote_id, password, timeout=60):
    """连接到远程电脑"""
    logging.info(f"开始连接 RustDesk: ID={remote_id}, 密码={password}")

The message is persisted by the following logging configuration:

python
logging.basicConfig(
    level=log_level,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(log_path, encoding='utf-8'),
        logging.StreamHandler()
    ]
)

Technical Analysis

The connect method interpolates both the RustDesk remote ID and password into an informational log message. The configured FileHandler writes this message to scripts/installer.log, leaving reusable remote-access credentials in plaintext on disk.

The password is also emitted through the stream handler, potentially exposing it to terminal capture, redirected output, parent-process logging, or centralized log collection. Passwords must not be treated as ordinary diagnostic data.

Attack Path

  1. A user starts a remote installation and supplies a valid RustDesk ID and password.
  2. RustDeskController.connect() constructs a log message containing both credentials.
  3. The logging framework writes the message to scripts/installer.log and standard error.
  4. A local user, process, backup service, support bundle, or log collector obtains the log.
  5. The exposed credentials are reused to attempt unauthorized access to the corresponding RustDesk endpoint.

Impact Assessment

Successful exploitation exposes credentials capable of granting interactive remote-machine access. The resulting privileges depend on the RustDesk session and the account active on the remote system. If the connected session is adminis ...[truncated 280 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove the password from every log statement and exception message.
  • Log only a minimally identifying, masked remote ID when operationally necessary.
  • Replace the vulnerable message with a constant message such as logging.info("Starting RustDesk connection").
  • Configure restrictive filesystem permissions for installer.log.
  • Avoid forwarding logs containing authentication context to shared collectors.
  • Add a logging filter that redacts known secret fields as defense in depth.
  • Rotate RustDesk passwords that may already have been written to existing logs.
  • Avoid accepting passwords through command-line arguments; use a protected prompt, standard input, environment-specific secret provider, or OS credential manager.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/remote_installer.py:485
Finding

Shell Command Injection Through Installer Paths

Content
View full analysis

Vulnerability Details

File Location: scripts/remote_installer.py, lines 485-497 and 528-533
Vulnerability Type: OS command injection
Risk Level: High

Vulnerable Code

python
def install_msi(self, installer_path):
    """安装 MSI 包"""
    cmd = f'msiexec /i "{installer_path}" /quiet /norestart'
    return self.run_command(cmd, "MSI")

def install_exe(self, installer_path):
    """安装 EXE 包"""
    silent_params = ["/S", "/VERYSILENT", "/SILENT", "/quiet", "/q"]

    for param in silent_params:
        cmd = f'"{installer_path}" {param}'
        result = self.run_command(cmd, "EXE", timeout=60)
        if result.get('success'):
            return result

The generated command is passed to a command shell:

python
def run_command(self, cmd, pkg_type, timeout=None):
    """运行命令"""
    timeout = timeout or self.config.get('installation', {}).get('timeout_seconds', 300)

    try:
        logging.info(f"执行命令:{cmd}")
        result = subprocess.run(
            cmd,
            shell=True,
            timeout=timeout,
            capture_output=True,
            text=True
        )

Technical Analysis

installer_path is inserted into a command string and then executed with shell=True. Quoting the path does not make this safe because an attacker-controlled filename can contain quote characters and Windows command-shell metacharacters that terminate the intended argument and introduce another command.

Installer paths can originate from recursively scanned, user-writable directories such as Desktop, Downloads, and Documents. A path may also be provided through the --package command-line option. The code performs no validation that the generated shell string contains only a single executable invocation.

Attack Path

  1. An attacker places an EXE or MSI with a shell-metacharacter-bearing filename in a directory scanned by the skill, or causes a crafted p ...[truncated 1144 chars]
Remediation
View remediation

Remediation Suggestions

  • Do not construct shell command strings from installer paths.

  • Set shell=False, which is the default, and pass arguments as a list.

  • Invoke MSI installation as follows:

    python
    subprocess.run(
        ["msiexec.exe", "/i", installer_path, "/quiet", "/norestart"],
        shell=False,
        timeout=timeout,
        capture_output=True,
        text=True
    )
    
  • Invoke EXE installers as follows:

    python
    subprocess.run(
        [installer_path, param],
        shell=False,
        timeout=timeout,
        capture_output=True,
        text=True
    )
    
  • Resolve and validate paths before execution, require a regular file with an allowed extension, and reject ambiguous or unsupported path forms.

  • Restrict automatic discovery to explicitly approved directories.

  • Require explicit user approval for each discovered installer and verify its publisher signature or expected cryptographic hash.

  • Add tests using paths containing spaces, quotes, and shell metacharacters to ensure they remain single arguments.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/remote_installer.py:500
Finding

Unsafe ZIP Extraction Followed by Automatic Executable Launch

Content
View full analysis

Vulnerability Details

File Location: scripts/remote_installer.py, lines 500-517
Vulnerability Type: Unsafe archive extraction and untrusted code execution
Risk Level: High

Vulnerable Code

python
def install_archive(self, archive_path):
    """安装压缩包"""
    import zipfile

    extract_dir = os.path.join(
        os.path.dirname(archive_path),
        "extracted_" + str(int(time.time()))
    )
    os.makedirs(extract_dir, exist_ok=True)

    try:
        if archive_path.endswith('.zip'):
            with zipfile.ZipFile(archive_path, 'r') as zip_ref:
                zip_ref.extractall(extract_dir)
            logging.info(f"解压完成:{extract_dir}")

            for root, dirs, files in os.walk(extract_dir):
                for file in files:
                    if file.lower() in [
                        'setup.exe',
                        'install.exe',
                        'installer.exe'
                    ]:
                        installer = os.path.join(root, file)
                        logging.info(f"找到安装程序:{installer}")
                        result = self.install_exe(installer)

Technical Analysis

The code passes every archive member directly to ZipFile.extractall() without first validating the resolved destination. It does not reject absolute paths, parent-directory traversal components, links, special files, or duplicate/conflicting entries.

After extraction, the code recursively searches for executables with generic names and launches the first match. It does not authenticate the archive, verify a cryptographic hash, validate an Authenticode publisher signature, or request approval for the selected executable.

Modern Python versions may normalize some traversal sequences during extraction, but the skill does not enforce or document a minimum runtime behavior that safely handles all hostile archive forms. Independently of traversal behavior, the ...[truncated 1423 chars]

Remediation
View remediation

Remediation Suggestions

  • Treat all archives and extracted executables as untrusted.
  • Before extraction, resolve each member against the extraction root and reject it unless the final path remains strictly inside that root.
  • Reject absolute paths, drive-qualified paths, .. components, links, reparse points, special files, and conflicting entries.
  • Extract into a securely created temporary directory outside attacker-controlled locations.
  • Apply restrictive permissions to the temporary directory.
  • Impose archive limits for total expanded size, member count, nesting depth, and compression ratio.
  • Do not automatically execute the first binary with a generic installer name.
  • Require explicit user confirmation identifying the exact archive and executable.
  • Verify an expected cryptographic hash or trusted Authenticode publisher before execution.
  • Remove the extraction directory in a finally block after securely terminating any child process that depends on it.

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Mutable and Unverified Third-Party Dependencies

Content
View full analysis

Vulnerability Details

File Location: requirements.txt, lines 1-4; installation instructions in SKILL.md, lines 106-108
Vulnerability Type: Unpinned dependency supply-chain exposure
Risk Level: Medium

Vulnerable Code

text
pyautogui>=0.9.53
pygetwindow>=0.0.9
pywinauto>=0.6.8
Pillow>=9.0.0

The documentation also recommends an unconstrained installation command:

bash
pip install pyautogui pywinauto Pillow

Technical Analysis

The dependency constraints specify only minimum versions. As a result, installations performed at different times may resolve to different, unreviewed releases. The documented command is even less restrictive and omits pygetwindow.

No lock file, package hashes, trusted-index restriction, or integrity-verification procedure is provided. Python packages may execute code during installation and are imported by this privileged automation script at runtime. A compromised package publisher, package index, or malicious future release could therefore introduce code that was not present during the skill audit.

This finding identifies unsafe supply-chain controls; the reviewed files do not establish that any currently named package is malicious.

Attack Path

  1. A user follows SKILL.md or installs from requirements.txt.
  2. Pip queries its configured package index and resolves a version satisfying the open-ended constraint.
  3. A compromised or maliciously replaced release is selected because no exact reviewed version or hash is required.
  4. Package installation hooks or imported package code execute on the host.
  5. The dependency code receives the privileges and data access of the Python process, including access to GUI automation and installation workflows.

Impact Assessment

Successful supply-chain compromise could provide arbitrary code execution under the account installing or running the skill. Since this project automates so ...[truncated 285 chars]

Remediation
View remediation

Remediation Suggestions

  • Pin every direct and transitive dependency to an exact reviewed version using ==.
  • Generate a reproducible lock file for the supported Python and Windows versions.
  • Record cryptographic hashes and install with pip --require-hashes.
  • Use an explicitly configured trusted package index or an internally controlled package mirror.
  • Ensure SKILL.md references the reviewed requirements or lock file instead of listing unconstrained package names.
  • Run dependency vulnerability and provenance checks as part of release preparation.
  • Review and deliberately update pinned versions on a controlled schedule rather than automatically accepting future releases.
  • Install and run the skill with the least privileges required.
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (18)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script logs the remote connection password in plaintext, which exposes sensitive credentials to log files and any process or user with log access. In a remote administration tool, this is especially dangerous because stolen RustDesk credentials can enable unauthorized remote access.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The code performs automatic silent installation and shell-based execution without any trust check, consent gate, or provenance verification. In this skill's context, the automation is designed to find installers and run them unattended, which materially increases the chance of executing malicious software or unsafe installer side effects.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

This is a true tool-parameter abuse issue because the command string passed to subprocess.run with shell=True is built from installer-related input. An attacker who can influence the package path or package discovery could inject shell syntax and execute arbitrary commands, which is especially severe in an automation script intended to run installers remotely or unattended.

Content

Scanner excerpt · scripts/remote_installer.py (reported line 555)May include surrounding context.

python
try:
            logging.info(f"执行命令:{cmd}")
            result = subprocess.run(cmd, shell=True, timeout=timeout, capture_output=True, text=True)
            
            if result.returncode == 0:
                logging.info(f"{pkg_type}包安装成功")

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill title, overview, and usage sections are presented in Chinese, and the examples and operational text continue in Chinese without indicating that another language is supported. This creates a locale/language policy issue because the skill appears to require a specific language without user opt-in or an explicit region-specific justification.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The configuration uses very generic button labels such as "Next", "Install", "Agree", "Accept", "OK", and "Close" as automation triggers. In a UI-driven installer skill, these broad matches can cause the agent to click unrelated dialogs, consent prompts, license agreements, or security warnings, leading to unintended software installation or unsafe acceptance of prompts.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The module docstring and user-facing strings are predominantly in Chinese, including operational messages and CLI descriptions, which imposes a language expectation on users. There is no indication that the user can select another language or that the locale restriction is intentional and justified.

Content

No source excerpt is available for this finding.

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 · scripts/remote_installer.py (reported line 69)May include surrounding context.

python
for path in rustdesk_paths:
            if os.path.exists(path):
                logging.info(f"启动 RustDesk: {path}")
                self.process = subprocess.Popen(path)
                time.sleep(3)
                return True

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 · scripts/remote_installer.py (reported line 75)May include surrounding context.

python
logging.warning("未找到 RustDesk,尝试搜索...")
        try:
            self.process = subprocess.Popen(['rustdesk'])
            time.sleep(3)
            return True
        except:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
91% confidence
Finding

The script executes whatever installer_path it finds or is given, with no trust validation, signature verification, or user confirmation. In this skill's context, it actively searches common user directories and runs installers automatically, so a malicious executable placed there could be launched with the operator's privileges.

Content

Scanner excerpt · scripts/remote_installer.py (reported line 511)May include surrounding context.

python
logging.info("静默安装失败,尝试 GUI 自动化...")
        try:
            subprocess.Popen(f'"{installer_path}"')
            time.sleep(3)
            self.handle_installation_gui()
            return {"success": True, "message": "EXE 包通过 GUI 自动化安装"}

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Content

Scanner excerpt · scripts/remote_installer.py (reported line 549)May include surrounding context.

python
except Exception as e:
            return {"success": False, "message": f"解压失败:{e}"}
    
    def run_command(self, cmd, pkg_type, timeout=None):
        """运行命令"""
        timeout = timeout or self.config.get('installation', {}).get('timeout_seconds', 300)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
99% confidence
Finding

subprocess.run(cmd, shell=True) executes a command string through the shell, and cmd is constructed using installer_path and install parameters. If installer_path contains shell metacharacters or is attacker-controlled, this can lead to arbitrary command execution beyond merely launching an installer.

Content

Scanner excerpt · scripts/remote_installer.py (reported line 555)May include surrounding context.

python
try:
            logging.info(f"执行命令:{cmd}")
            result = subprocess.run(cmd, shell=True, timeout=timeout, capture_output=True, text=True)
            
            if result.returncode == 0:
                logging.info(f"{pkg_type}包安装成功")

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency is specified with a lower bound only, which allows future installs to resolve to different versions over time. This weakens build reproducibility and can unintentionally introduce vulnerable or incompatible releases through normal dependency updates or supply-chain compromise.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
pyautogui>=0.9.53
pygetwindow>=0.0.9
pywinauto>=0.6.8
Pillow>=9.0.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency is unpinned, so installations are not deterministic and may pull in unexpected future versions. This creates a supply-chain risk because security posture depends on whatever version is current at install time rather than a reviewed release.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
pyautogui>=0.9.53
pygetwindow>=0.0.9
pywinauto>=0.6.8
Pillow>=9.0.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

Using a ">=" specifier without an upper bound or exact pin permits uncontrolled version drift. That can expose users to newly introduced malicious, vulnerable, or breaking releases without any code change in the skill itself.

Content

Scanner excerpt · requirements.txt (reported line 3)May include surrounding context.

text
pyautogui>=0.9.53
pygetwindow>=0.0.9
pywinauto>=0.6.8
Pillow>=9.0.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

Pillow is unpinned, which is especially risky because this package has a history of multiple security advisories. Without an exact version, deployments may resolve to a vulnerable release or change behavior across installs, increasing supply-chain and exploit exposure.

Content

Scanner excerpt · requirements.txt (reported line 4)May include surrounding context.

text
pyautogui>=0.9.53
pygetwindow>=0.0.9
pywinauto>=0.6.8
Pillow>=9.0.0

Unverifiable Dependency: Pillow has 16 known advisory(ies) (CVE-2016-2533 (Pillow buffer overflow in ImagingPcdDecode); CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2021-27922 (Pillow Uncontrolled Resource Consumption) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding

The manifest does not pin a specific Pillow version even though the package has many known advisories, including issues such as resource exhaustion and potential code execution in some releases. Because the actual installed version is unknowable from this file, there is a credible risk that vulnerable versions could be pulled during installation.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/remote_installer.py (reported line 259)May include surrounding context.

python
def setup_logging(self):
        """设置日志"""
        log_level = getattr(logging, self.config.get('installation', {}).get('log_level', 'INFO'))
        script_dir = os.path.dirname(os.path.abspath(__file__))
        log_path = os.path.join(script_dir, 'installer.log')

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
99% confidence
Finding

The install method is explicitly documented as 本地安装流程(无远程) at L682 and contains no RustDesk connection logic. However, its success message at L724 says 远程安装流程完成, which misstates what the function actually did and can mislead operators or downstream systems about whether any remote access occurred.

Content

No source excerpt is available for this finding.