T09 · Insecure Skill Coding Practices
- Location
scripts/george.py:3078- Finding
Banking Bearer Token Forwarded to an Unvalidated Polling URL
- Content
View full analysis
0: interval_s = max(poll_interval / 1000.0, 1.0) start = time.time() last_state = state while time.time() - start < args.timeout: time.sleep(interval_s) try: poll_result = page.evaluate("""async ([url, token]) => { try { const resp = await fetch(url, { headers: {'Authorization': token} }); ``` ### Technical Analysis The data-carrier signing API response supplies `poll.url`. The Skill uses that value as a request destination without validating its scheme, hostname, port, or path. It attaches the captured George bearer token to the resulting request through the `Authorization` header. This differs from the account and transaction functions, which use fixed official Sparkasse URLs. If a compromised or manipulated signing response returns an attacker-controlled polling URL, the browser may send the banking bearer token to that destination. Because an `Authorization` header triggers a CORS preflight for cross-origin requests, an attacker-controlled server can permit th ...[truncated 1371 chars]- Remediation
View remediation
str: parts = urlsplit(url) if parts.scheme != "https": raise ValueError("Signing poll URL must use HTTPS") if parts.hostname != "api.sparkasse.at": raise ValueError("Unexpected signing poll URL host") if parts.port not in (None, 443): raise ValueError("Unexpected signing poll URL port") if parts.username or parts.password or parts.fragment: raise ValueError("Malformed signing poll URL") if not parts.path.startswith("/rest/netbanking/"): raise ValueError("Unexpected signing poll URL path") return url ``` Apply this check before the polling loop and never attach the bearer token to an unapproved destination. ]]>
