FastMCP OAuth Callback Reflected XSS
FastMCP versions before 2.13.0 build the OAuth callback page by inserting URL parameters directly into HTML without escaping them. An attacker who gets a victim to open a crafted callback link can run JavaScript in the victim's session, right at the moment OAuth tokens are being exchanged.
How the attack works
An attacker crafts a link to the FastMCP OAuth callback endpoint with script markup, an event-handler attribute, or a javascript:/data: URL scheme in the query string or body, possibly percent-encoded to dodge basic filters. The victim opens the link, often after fetching it from an attacker-controlled page. FastMCP reflects the unescaped parameter into the generated HTML and the browser executes it. Because this happens during an active OAuth authorize/token exchange, the injected script can intercept or exfiltrate the resulting access/refresh tokens.
Netzilo detection
Netzilo reports this behaviour when it is observed.
- Signature ID
- 7c3f9a2e-5d41-4b8a-9e02-6f1c8d4b7a35
- Severity
- High
Why it matters
A successful callback injection lets an attacker steal OAuth tokens issued during the exchange, giving them the same access as the victim's authenticated session without needing credentials.
What you can do
- →Upgrade FastMCP to 2.13.0 or later, which escapes callback parameters.
- →Audit any custom OAuth callback handlers for unescaped output of query, body, or header values.
- →Restrict which redirect/callback URLs your OAuth clients accept, and reject ones containing script markup or javascript:/data: schemes.
- →Treat detections from known scanners or developer testing as expected; verify the process lineage before escalating, and investigate anything originating from an unfamiliar browser or agent session.
Known benign look-alikes
- Authorized security testing — DAST scanners, Burp/ZAP payload replay or a red-team engineer manually probing the FastMCP callback endpoint with alert()/img-onerror payloads. These are true detections of the technique but expected activity; correlate with the reported process lineage (scanner binary vs. browser/agent) before escalating.
- A developer working on oauth_callback.py locally and passing markup through the callback query string to verify the escaping fix. Same signature, benign intent.
- An agent fetching an XSS write-up, CVE advisory or cheat-sheet whose URL happens to contain a "/callback" segment together with a sample payload in the query string.
- An OAuth provider redirecting back with an error_description that contains raw angle brackets or quotes from an upstream error message. Mitigated by requiring tag names, on*= handlers, javascript:/data:text/html schemes or entity-encoded '<' rather than bare punctuation.
- Redirect-chain or URL-shortener traffic where an encoded downstream target string resembles a script scheme. Mitigated by requiring the callback-shaped path on the triggering request itself.