T05 · Unauthorized Access and Privilege Escalation
- Location
skill_main.py:588- Finding
Arbitrary Termination of Processes Bound to a User-Selected Port
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill does what it says, but it also controls a logged-in WeChat browser session while saving sensitive logs/screenshots and can force-kill unrelated local processes.
Install only if you are comfortable with this skill controlling a logged-in WeChat Official Account Chrome session and creating remote drafts. Before use, review or change the port-killing behavior, treat skill.log and mp_login_screenshot.png as sensitive, delete them after runs, and prefer a pinned dependency in an isolated Python environment.
skill_main.py:588Arbitrary Termination of Processes Bound to a User-Selected Port
wechat_tietu_draft.py:180WeChat Session Data and Browser-Control Endpoints Written to Plaintext Logs
skill_main.py:239Persistent Capture of Potentially Sensitive WeChat Login or Account Screenshots
requirements.txt:1Unbounded and Unverified Third-Party Dependency Version
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
if not ensure_chrome_ready_for_draft(port):
return False
try:
child_env = os.environ.copy()
child_env["CDP_TIETU_CHILD"] = "1"
child_env["CDP_TIETU_PORT"] = str(port)
with open(log_file, "w", encoding="utf-8") as f:
The skill documentation declares no explicit tool scope even though the skill clearly relies on shell execution, filesystem access, environment inspection, network connectivity, and browser/session manipulation. This weakens least-privilege enforcement and makes it easier for the skill to be invoked with broader capabilities than users or a host framework may expect, increasing the chance of unintended local or account-affecting actions.
The skill description does not prominently warn that it may automatically launch Chrome, attach to a live logged-in WeChat Official Account session, and create or save drafts on a remote service. Because these actions can change account state and publishable content, insufficient disclosure can mislead users or orchestrators into granting consent for automation they do not fully understand.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
"""本机打开 URL(无 CDP 时打开 mp 首页等)"""
try:
if sys.platform == "darwin":
subprocess.run(["open", url], check=False, timeout=10)
elif sys.platform == "win32":
os.startfile(url) # noqa: S606
else:
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
elif sys.platform == "win32":
os.startfile(url) # noqa: S606
else:
subprocess.run(["xdg-open", url], check=False, timeout=10)
except Exception:
pass
The skill writes browser screenshots to a predictable local file without clearly warning the user that authenticated page contents are being captured. This creates unnecessary local persistence of potentially sensitive publisher-session data and can expose information to other local users or later processes.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
成功返回绝对路径字符串,失败返回 None。
"""
try:
r = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/json/list"],
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
for i in range(checks):
elapsed = i * interval
try:
r = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/json/list"],
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def refetch_best_wechat_page(port=DEFAULT_CDP_PORT):
try:
r = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/json/list"],
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def refetch_best_wechat_page(port=DEFAULT_CDP_PORT):
try:
r = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/json/list"],
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
try:
# 1. 检查调试端口是否响应
try:
result = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/json/version"],
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# 2. 获取页面列表
try:
result = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/json/list"],
capture_output=True,
text=True,
The skill forcefully frees the chosen CDP port by discovering and killing whatever process currently owns it. That capability is not necessary for creating a draft and creates an unjustified local denial-of-service primitive against unrelated software.
The script kills processes occupying the target port without warning or confirmation, violating least surprise and safe automation practices. In practical terms this can disrupt unrelated developer workflows, browsers, proxies, or monitoring agents on the host.
This subprocess call is part of logic that enumerates arbitrary processes bound to the configured port and enables later forced termination. In the skill context, killing unrelated local processes exceeds the stated draft-creation purpose and can disrupt other applications or security tooling.
def _chrome_kill_port_pids(port=DEFAULT_CDP_PORT):
"""释放调试端口(macOS/Linux:lsof + kill)。"""
try:
r = subprocess.run(
["lsof", "-ti", f":{port}"],
capture_output=True,
text=True,
This line forcefully kills every PID listening on the configured port using kill -9, without ownership checks or user confirmation. In context, that can terminate unrelated local applications and cause denial of service or data loss, which is beyond the skill's declared purpose.
return False
for pid in r.stdout.strip().split():
try:
subprocess.run(["kill", "-9", pid], capture_output=True, timeout=2)
except Exception:
pass
time.sleep(2)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
chrome_cmd = p
break
if not chrome_cmd:
r = subprocess.run(["which", "google-chrome"], capture_output=True, text=True)
chrome_cmd = r.stdout.strip() if r.returncode == 0 else "google-chrome"
ts = int(time.time())
user_data = os.path.expanduser(f"~/.chrome-real-wechat-{ts}")
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
WECHAT_MP_HOME,
]
try:
subprocess.Popen(
cmd,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.
time.sleep(3)
for i in range(15):
try:
with urllib.request.urlopen(
f"http://127.0.0.1:{port}/json/version", timeout=3
) as resp:
if resp.status == 200:
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def _sr_list_has_page(port):
try:
r = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/json/list"],
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def _sr_list_has_page(port):
try:
r = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/json/list"],
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def _sr_list_has_page(port):
try:
r = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/json/list"],
capture_output=True,
text=True,
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.
body = json.dumps(
{"id": 1, "method": "Page.navigate", "params": {"url": WECHAT_MP_HOME}}
).encode()
req = urllib.request.Request(
f"http://127.0.0.1:{port}/json/session/{pid}",
data=body,
headers={"Content-Type": "application/json"},
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
"""
debug_ok = False
try:
r = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/json/version"],
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
with open(log_file, "w", encoding="utf-8") as f:
f.write(f"# Run at {datetime.now().isoformat(timespec='seconds')} file={Path(file_path).name}\n")
f.flush()
result = subprocess.run(
[sys.executable, str(draft_script), str(file_path)],
stdout=f,
stderr=subprocess.STDOUT,