zrok CVE-2026-40302
MEDIUMSeverity by source
AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Network-delivered reflected XSS requires victim interaction (UI:R) and executes in OAuth server's origin (S:C), with no credentials needed and low bounded impact on confidentiality and integrity.
Primary rating from GitHub Advisory.
CVSS VectorGitHub Advisory
Lifecycle Timeline
4DescriptionGitHub Advisory
Summary The proxyUi template engine uses Go's text/template (which performs no HTML escaping) instead of html/template. The GitHub OAuth callback handlers in both publicProxy and dynamicProxy embed the attacker-controlled refreshInterval query parameter verbatim into an error message when time.ParseDuration fails, and render that error unescaped into HTML. An attacker can deliver a crafted login URL to a victim; after the victim completes the GitHub OAuth flow, the callback page executes arbitrary JavaScript in the OAuth server's origin.
- Attack Vector: Network - the attack is delivered as a crafted URL over the internet.
- Attack Complexity: Low - no race conditions or special environment prerequisites.
- Privileges Required: None - the attacker needs no account on the zrok instance.
- User Interaction: Required - the victim must click the crafted link and complete the GitHub OAuth flow.
- Scope: Changed - the injected script executes in the OAuth server's origin, not the victim's share origin.
- Confidentiality Impact: Low - the script runs in the OAuth server origin after a failed flow; no session cookie is set at this point, limiting what can be exfiltrated to what is visible in the DOM and what can be requested from the OAuth server.
- Integrity Impact: Low - the script can initiate new OAuth flows or submit forms on behalf of the victim in the OAuth server origin.
- Availability Impact: None.
Affected Components
- endpoints/proxyUi/template.go - init() / WriteTemplate (lines 8, 18, 99) - text/template used for HTML rendering
- endpoints/proxyUi/template.html - line 119 - {{ .Error }} in HTML without escaping
- endpoints/publicProxy/providerGithub.go - login callback closure (lines 93, 128, 130)
- endpoints/dynamicProxy/providerGithub.go - loginHandler() (lines 110, 146, 148)
AnalysisAI
Reflected XSS in zrok's GitHub OAuth callback allows an unauthenticated attacker to execute arbitrary JavaScript in the OAuth server's browser origin by delivering a crafted login URL to a victim. The proxyUi template engine uses Go's text/template (no HTML escaping) rather than html/template, and both publicProxy and dynamicProxy OAuth callback handlers embed the attacker-controlled refreshInterval query parameter verbatim into an error message when time.ParseDuration fails, rendering that error unescaped into HTML. Exploitation requires user interaction (victim must click the link and complete the GitHub OAuth flow); no active exploitation is confirmed (not in CISA KEV, EPSS 0.01%, SSVC exploitation: none), but a vendor-confirmed fix is available in v2.0.1 while v1.x remains unpatched.
Technical ContextAI
zrok is an open-source, Go-based network sharing and tunneling tool maintained by OpenZiti (pkg:go/github.com/openziti/zrok and pkg:go/github.com/openziti/zrok/v2). The root cause (CWE-79: Improper Neutralization of Input During Web Page Generation) is the use of Go's text/template package in endpoints/proxyUi/template.go (init() and WriteTemplate, lines 8, 18, 99) instead of html/template, which provides automatic contextual HTML escaping. The template variable {{ .Error }} at line 119 of template.html is rendered without escaping. When the GitHub OAuth callback handlers in publicProxy/providerGithub.go (lines 93, 128, 130) and dynamicProxy/providerGithub.go (lines 110, 146, 148) encounter a time.ParseDuration failure on the attacker-supplied refreshInterval parameter, they embed the raw, unvalidated input into the error struct and pass it to WriteTemplate. Because text/template does not sanitize output, the attacker's payload is emitted directly into the HTML response served by the OAuth callback endpoint.
RemediationAI
For zrok v2 deployments, upgrade to v2.0.1 or later; the release notes explicitly confirm the fix: 'Escaped proxyUi output by switching to html/template and removed reflected refreshInterval values from github oauth error pages' (https://github.com/openziti/zrok/releases/tag/v2.0.1). No patch has been released for v1.x (<=1.1.11) per the advisory; v1 operators should migrate to the v2 branch. For unpatched or unmigrated deployments, the most targeted compensating control is to disable GitHub OAuth integration for publicProxy and dynamicProxy if it is not operationally required - this eliminates the vulnerable code path entirely. If GitHub OAuth must remain enabled, deploying a WAF rule that blocks or strips the refreshInterval query parameter on OAuth callback requests (e.g., paths matching the GitHub OAuth callback endpoint) will prevent the malicious parameter from reaching the template. Restricting access to OAuth callback URLs by IP allowlist can reduce phishing exposure but will interfere with legitimate OAuth flows from arbitrary client IPs. Full advisory and patch reference: https://github.com/openziti/zrok/security/advisories/GHSA-4fxq-2x3x-6xqx.
Same weakness CWE-79 – Cross-site Scripting (XSS)
View allVendor StatusVendor
SUSE
Severity: Moderate| Product | Status |
|---|---|
| SUSE Linux Enterprise Server 16.1 | Affected |
| SUSE Linux Enterprise Server for SAP applications 16.1 | Affected |
| SUSE Linux Enterprise Module for Package Hub 15 SP5 | Affected |
| SUSE Linux Enterprise Module for Package Hub 15 SP6 | Affected |
| openSUSE Leap 15.5 | Affected |
| openSUSE Leap 15.6 | Affected |
Share
External POC / Exploit Code
Leaving vuln.today
GHSA-4fxq-2x3x-6xqx