Back to skill

Security audit

12306 Train Assistant

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real 12306 train-ticket helper, but it handles credentials, booking/payment actions, session cookies, and an unrestricted server override in ways that need careful review.

Install only if you are comfortable letting the skill access your 12306 account, passenger records, ticket orders, and payment workflow. Do not use --base-url unless you fully control and trust the destination, avoid passing passwords in visible command lines, protect or clear the cookie cache after use, and manually confirm any booking, cancellation, or payment action before allowing the agent to run it.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
client.py:639
Finding

Unrestricted Base URL Override Can Redirect Credentials and Authenticated Requests

Content
View full analysis
str: if path.startswith("http://") or path.startswith("https://"): return path return f"{self.base_url}{path}" def _build_request_headers(self, *, method: str, referer: str | None = None) -> dict[str, str]: headers: dict[str, str] = {} if self.browser_headers: headers.update( { "Sec-Fetch-Site": "same-origin", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Dest": "empty", } ) if referer: headers["Referer"] = self._url(referer) if method.upper() == "POST": headers["Origin"] = self.base_url headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8" return headers @staticmethod def _raise_if_risk_control_redirect(resp: requests.Response) -> None: if "error.html" in resp.url: raise RuntimeError( "12306 返回 error.html,通常是触发了风控或访问限制。" "请在可访问 12306 的网络环境重试,或重新登录,或降低请求频率。" ) def _send_request( self, method: str, path: str, *, params: dict[str, Any] | None = None, data: dict[str, Any] | None = None, referer: str | None = None, expect_json: bool = True, ) -> requests.Response: resp = self.session.request( method=method.upper(), url=self._url(path), params=params, data=data, headers=self._build_request_headers(method=method, referer=referer), timeout=self.timeout, ) ``` ```python parser.add_argument("--base-url", default=BASE_URL, help="12306 base URL") ``` ```python client = KyfwClient( base_url=args.base_url, timeout=args.timeout, ...[truncated 3058 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
client.py:568
Finding

Authentication Cookies Are Persisted as Plaintext Without Enforced Owner-Only Permissions

Content
View full analysis
None: if not self.cookie_file: return path = Path(self.cookie_file).expanduser() if not path.exists(): return try: payload = json.loads(path.read_text(encoding="utf-8")) cookie_items: list[dict[str, Any]] if isinstance(payload, dict) and isinstance(payload.get("cookies"), list): cookie_items = payload["cookies"] elif isinstance(payload, list): cookie_items = payload else: return jar = requests.cookies.RequestsCookieJar() for item in cookie_items: if not isinstance(item, dict): continue name = item.get("name") value = item.get("value") if not name or value is None: continue kwargs: dict[str, Any] = { "name": str(name), "value": str(value), "path": item.get("path", "/"), "secure": bool(item.get("secure", False)), } if item.get("domain"): kwargs["domain"] = item["domain"] if item.get("expires") is not None: kwargs["expires"] = int(item["expires"]) rest = item.get("rest") if isinstance(rest, dict): kwargs["rest"] = rest jar.set_cookie(requests.cookies.create_cookie(**kwargs)) self.session.cookies.update(jar) except Exception: # Cookie file is best-effort. return def _save_cookies(self) -> None: if not self.cookie_file: return path = Path(self.cookie_file).expandu ...[truncated 3289 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (9)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
用本仓库的 `client.py` 完成 12306 相关查询与下单辅助,优先覆盖:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill advertises and instructs use of shell, network, file, and environment-backed operations, but it does not declare any explicit tool scope or permission boundaries. In a skill that can log in, query account state, submit orders, and retrieve payment links, missing scope declarations increases the chance that an agent executes sensitive actions or accesses local secrets/files without clear restriction or user awareness.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill includes password login, SMS verification, booking, candidate submission, cancellation, and payment retrieval flows, but it does not prominently warn that the agent may handle credentials, session state, personal data, and submit real ticket orders. In this context, omission of an explicit warning is dangerous because users may provide secrets or authorize transactional actions without understanding the privacy and financial consequences.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The constant hard-codes Accept-Language to zh-CN,zh-Hans and is automatically applied to browser-style requests. This enforces a specific locale preference without exposing a user choice or documenting it as a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The client persists 12306 session cookies to a local JSON file without prominent disclosure or protection controls. Those cookies can represent an authenticated session, so local users, malware, or other tools with filesystem access may reuse them to access account data or perform actions as the user.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill silently starts a detached background process to continue monitoring QR login state after generating the QR code. In a user-facing agent context, this is risky because it persists activity beyond the original command, writes logs to disk, and may continue handling authentication state without clear consent or visibility.

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 · client.py (reported line 4165)May include surrounding context.

python
cmd.append("--no-browser-headers")
            cmd.append("--qr-check-worker")
            with checker_log.open("ab") as log_fp:
                proc = subprocess.Popen(
                    cmd,
                    stdin=subprocess.DEVNULL,
                    stdout=log_fp,

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The QR login state file stores session metadata such as UUID, timestamps, cookie file path, and login progress without explicit user notice. While less sensitive than raw cookies, it still reveals authentication workflow details and local file paths that may aid session tracking or privacy leakage.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The QR login image is written to disk, potentially in a temp directory, without explicit warning that it will persist on the filesystem. A local attacker or another process could read the image before cleanup and use it to monitor or interfere with the login flow, depending on timing.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.