T09 · Insecure Skill Coding Practices
- Location
dashboard.py:275- Finding
Unauthenticated Network Control Plane Enables Remote Command Execution
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is a real WeChat auto-reply tool, but it exposes chat data and automation controls through an unauthenticated network dashboard and uses unsafe shell-based automation.
Install only after reviewing the code and preferably after it is changed to bind to localhost, require authentication, remove shell=True command strings, sanitize dashboard rendering, and add explicit approval or a dry-run mode before sending messages. Do not expose the dashboard to a LAN/WAN or grant Accessibility and Screen Recording permissions on a machine with sensitive data unless you accept the risk.
dashboard.py:275Unauthenticated Network Control Plane Enables Remote Command Execution
dashboard.py:258Unauthenticated Disclosure of Private Chats, Logs, and Screenshots
dashboard.py:108Stored Cross-Site Scripting in the Dashboard
monitor_main.py:49Shell Injection in UI-Automation and Media Utilities
monitor_main.py:245Indirect Prompt Injection Controls Automatically Executed Replies and Actions
monitor_main.py:200Predictable Temporary Prompt Files Permit Symlink Attacks and Data Disclosure
requirements.txt:3Unpinned Flask Dependency Creates Non-Reproducible Supply-Chain Risk
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).
def get_daemon_status():
global monitor_process
# Double check via process list just in case
check = subprocess.run("pgrep -f wechat-auto-reply/monitor_main.py", shell=True, capture_output=True)
is_running = check.returncode == 0
return jsonify({"running": is_running})
The unauthenticated start endpoint executes a destructive pkill command that forcibly terminates matching processes system-wide. Even though the command string is static, exposing this behavior over HTTP enables denial of service against local matching processes and makes the dashboard a process-kill primitive.
persona = "回复要简短、干脆,不带句号,用词口语化,像哥们一样聊天,语气轻松。"
# Kill any existing instances
os.system("pkill -9 -f wechat-auto-reply/monitor_main.py")
script_path = os.path.expanduser("~/.openclaw/workspace/skills/wechat-auto-reply/monitor_main.py")
cmd = ["python3", script_path, "--targets", targets, "--persona", persona, "--interval", "60"]
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
script_path = os.path.expanduser("~/.openclaw/workspace/skills/wechat-auto-reply/monitor_main.py")
cmd = ["python3", script_path, "--targets", targets, "--persona", persona, "--interval", "60"]
env = os.environ.copy()
if apikey:
env["GEMINI_API_KEY"] = apikey
The stop endpoint exposes an unauthenticated force-kill operation via HTTP, allowing any reachable client to terminate the monitor process. This creates a straightforward denial-of-service condition and can affect other matching processes because pkill uses a pattern match rather than a tracked PID.
@app.route('/api/stop', methods=['POST'])
def stop_daemon():
global monitor_process
os.system("pkill -9 -f wechat-auto-reply/monitor_main.py")
if monitor_process:
try:
monitor_process.terminate()
This is a classic tool-parameter abuse issue: a generic command runner executes shell commands built from mutable inputs, and the rest of the file feeds it many attacker-influenceable values. In this skill, the dangerous context is amplified because those values come from chat metadata, temporary filenames, and model-generated content, making exploitation paths numerous.
def run_cmd(cmd, timeout=120):
try:
return subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
except subprocess.TimeoutExpired:
logger.error(f"Command timed out after {timeout}s: {cmd}")
# Return a dummy completed process to avoid crashing the caller
This code actively focuses a WeChat window, injects clipboard contents, and sends messages via simulated keystrokes without per-message user approval. In a skill context, that enables unauthorized impersonation, accidental delivery to the wrong chat, and automated exfiltration or social-engineering actions if upstream inputs or model outputs are manipulated.
The skill captures screenshots of chat windows and submits their contents to external CLI tools for interpretation and reply generation. That exposes potentially sensitive conversation data to additional processing components and creates a privacy and data-leak risk, especially because the tool behavior, transport, and retention are not controlled in this file.
This is a true tool-parameter-abuse risk because the code builds shell commands from values that can be influenced by runtime input, including target names used in filenames and shell substitutions. In a desktop automation skill with permissions to control UI, capture screens, and access the filesystem, command injection would give an attacker broad code-execution capability and amplify downstream harm.
def run_cmd(cmd, timeout=120):
try:
return subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
except subprocess.TimeoutExpired:
logger.error(f"Command timed out after {timeout}s: {cmd}")
class DummyProcess:
This function pastes arbitrary generated text into the active WeChat UI and presses Enter with no confirmation step. In the context of an auto-reply agent, prompt injection, model mistakes, or misfocus on the wrong window can directly cause unauthorized messages or sensitive data disclosure to be sent to real contacts.
The script sends conversation screenshots to an external summarization command, which may forward private message content to a local or remote model service. This creates a significant confidentiality risk because highly sensitive chat data is being processed outside WeChat without explicit disclosure, consent, or trust guarantees.
The prompt explicitly says not to send a URL for location requests, but the code converts the action into a generated map URL and sends it anyway. This creates a policy/implementation mismatch that can leak location information in a form the higher-level safety rule was meant to prevent, and undermines operator trust in the guardrail.
Generated replies are sent automatically to WeChat without user review. Because the reply content is derived from model output influenced by chat content and persona instructions, this can lead to accidental harassment, misinformation, privacy leaks, or compliance issues being sent under the user's identity.
This is a true tool-parameter-abuse issue because the generic run_cmd wrapper accepts a full command string and executes it via the shell. In the context of this skill, user input flows into shell commands used for UI automation, making exploitation realistic and especially dangerous because successful injection would run with the local user's privileges on macOS.
def run_cmd(cmd):
try:
res = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
return res
except subprocess.CalledProcessError as e:
print(f"❌ Command failed: {cmd}\nError: {e.stderr}")
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).
print("🛡️ Safety Lock Check...")
check_focus_cmd = "osascript -e 'tell application \"System Events\" to get name of first application process whose frontmost is true'"
res = subprocess.run(check_focus_cmd, shell=True, capture_output=True, text=True)
if "微信" not in res.stdout and "WeChat" not in res.stdout:
print("🚨 SAFETY ABORT: WeChat is not the active window!")
sys.exit(1)
The skill exposes unrestricted shell execution and interpolates untrusted arguments into commands used to focus windows and set the clipboard. In an automation skill that sends content to a chat application, this is especially risky because attacker-controlled inputs can trigger arbitrary command execution in addition to unintended message delivery.
This is a concrete instance of tool parameter abuse: a general-purpose shell execution primitive is exposed and then fed formatted command strings. Because the skill accepts external parameters and performs desktop automation, abuse of this primitive can lead to arbitrary system command execution and unauthorized actions on the user's workstation.
logger = logging.getLogger(__name__)
def run_cmd(cmd):
return subprocess.run(cmd, shell=True, capture_output=True, text=True)
def check_focus():
res = run_cmd("osascript -e 'tell application \"System Events\" to get name of first application process whose frontmost is true'")
The skill advertises a 'memory bank,' screenshot parsing, live snapshot preview, and historical chat viewing, but it does not clearly warn that chat history, screenshots, and derived data may be stored or displayed. In a messaging automation context, this omission is risky because users and affected contacts may be unaware that sensitive communications could persist locally or be exposed through the dashboard.
The documentation describes a tool requiring Screen Recording and Accessibility permissions and performing automated UI control, but it does not clearly warn users that this grants broad system-observation and input-injection capability. In this context, missing disclosure is dangerous because users may enable powerful permissions without understanding that the skill can read visible content and control other apps, increasing misuse and privacy risk.
The instructions explicitly mention access via the machine's LAN address (0.0.0.0) but do not warn that exposing the Flask dashboard beyond localhost can allow other hosts to reach a control panel that may start/stop automation and view sensitive chat artifacts. Given the dashboard's described capabilities, accidental network exposure materially increases the risk of unauthorized access, privacy loss, and misuse.
The safety statement is materially misleading because the skill does more than local visual OCR and keyboard/mouse simulation: it also runs a Flask dashboard, stores or serves screenshots/history, and manages API keys and subprocess state. Users may trust the '100% safe' and 'absolute no ban' language and underestimate the attack surface, privacy exposure, and operational risk introduced by the web interface and credential handling.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def get_daemon_status():
global monitor_process
# Double check via process list just in case
check = subprocess.run("pgrep -f wechat-auto-reply/monitor_main.py", shell=True, capture_output=True)
is_running = check.returncode == 0
return jsonify({"running": is_running})
The endpoint accepts a credential-like value from the web UI and injects it into the child process environment as GEMINI_API_KEY. There is no warning in code-visible user messaging that the provided key will be transmitted to another process or used for external model access.
This code performs safety-impacting operations: it forcibly terminates existing monitor processes and starts a new background process using user-supplied targets and an API key. The endpoint has no confirmation step, no visible server-side warning/log about the kill/start action, and no inline comment or docstring disclosing the operational impact to users.
This endpoint launches a background Python process based on HTTP requests without any authentication or CSRF protection. An attacker who can reach the dashboard can start arbitrary monitoring activity repeatedly, consume resources, and trigger downstream behavior in monitor_main.py using attacker-supplied parameters.
if apikey:
env["GEMINI_API_KEY"] = apikey
monitor_process = subprocess.Popen(cmd, env=env)
return jsonify({"status": "started", "targets": targets})
@app.route('/api/stop', methods=['POST'])
This endpoint triggers a destructive process-management action by issuing a forceful pkill and terminating the tracked subprocess, but it provides no confirmation prompt, warning message, or descriptive comment/docstring. Users invoking stop are not informed that the action forcibly kills matching processes rather than performing a graceful shutdown.
No suspicious patterns detected.