{"id":"CVE-2026-21873","aliases":["GHSA-mhpg-c27v-6mxr","PYSEC-2026-1702"],"title":"NiceGUI apps which use `ui.sub_pages` vulnerable to zero-click XSS","summary":"NiceGUI apps which use `ui.sub_pages` vulnerable to zero-click XSS","severity":"high","cvss":7.2,"cvssVector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N","vendor":"nicegui","product":"nicegui","ecosystem":"pip","affected":["nicegui >= 2.22.0, < 3.5.0"],"patched":["nicegui 3.5.0"],"published":"2026-01-08","updated":"2026-07-07","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-mhpg-c27v-6mxr","references":[{"url":"https://github.com/zauberzeug/nicegui/security/advisories/GHSA-mhpg-c27v-6mxr"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-21873"},{"url":"https://github.com/zauberzeug/nicegui"},{"url":"https://github.com/zauberzeug/nicegui/releases/tag/v3.5.0"}],"tags":["osv","pip"],"epss":0.00264,"epssPercentile":0.18567,"ingestedAt":"2026-07-08T18:25:51.228Z","slug":"CVE-2026-21873","body":"## Overview\n\n### Summary\n\nAn unsafe implementation in the `pushstate` event listener used by `ui.sub_pages` allows an attacker to manipulate the fragment identifier of the URL, which _they can do despite being cross-site, using an iframe_. \n\n### Details\n\nThe problem is traced as follows:\n\n1. On `pushstate`, `handleStateEvent` is executed. \n\nhttps://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/elements/sub_pages.js#L38-L39\n\n2. `handleStateEvent` emits `sub_pages_open` event. \n\nhttps://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/elements/sub_pages.js#L22-L25 \n\n3. `SubPagesRouter` (used by `ui.sub_pages`), lisnening on `sub_pages_open`, `_handle_open` runs. \n\nhttps://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/sub_pages_router.py#L18-L22\n\n4. `_handle_open` finds any `SubPages` and runs `_show()` on them\n\nhttps://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/sub_pages_router.py#L63-L71\n\n5. If the if-logic is followed or debug prints are added, it can be found that it calls `self._handle_scrolling(match, behavior='smooth')` directly\n\nhttps://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/elements/sub_pages.py#L76-L100\n\n6. **CULPRIT** `_handle_scrolling` runs `_scroll_to_fragment` as there is a fragment, which runs vulnerable JS if the `fragment` (attacker-controlled) escapes out of the quotes. \n\nhttps://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/elements/sub_pages.py#L206-L217\n\n### PoC\n\nJust visiting this page (no click required), consistently triggers XSS in https://nicegui.io domain. \n\n```html\n<html>\n  <body>\n    <iframe id=\"myiframe\" src=\"https://nicegui.io\" width=\"100%\" height=\"600px\" onload=\"triggerXSS()\"></iframe>\n    <script>\n      function triggerXSS() {\n        if (!myiframe.src.includes(\"#\")) {\n          myiframe.src = \"https://nicegui.io#x');alert(document.domain)//\";\n        }\n      }\n    </script>\n  </body>\n</html>\n```\n\n<img width=\"1429\" height=\"643\" alt=\"image\" src=\"https://github.com/user-attachments/assets/310dbb5c-65d5-44f2-8417-dcf044829bc6\" />\n\n### Impact\n\nAny page which uses `ui.sub_pages` and does not actively prevent itself from being put in an iframe is affected.\n\nThe impact is high since by-default NiceGUI pages are iframe-embeddable with no native opt-out functionalities except by manipulating the underlying `app` via FastAPI methods, and that `ui.sub_pages` is actively promoted as the new modern way to create Single-Page Applications (SPA). \n\n### Patch\n\n1. Not use `ui.sub_pages`\n2. Block iframe with the following code\n\n```py\n@app.middleware('http')\nasync def iframe_blocking_middleware(request, call_next):\n    response = await call_next(request)\n    response.headers['X-Frame-Options'] = 'DENY'\n    return response\n```\n\n### Appendix\n\nAI is used safely to judge the CVSS scoring (input is censored).\n\nPlease find the results in https://poe.com/s/3FXuwp7TAYxqLomARXma\n\n### Scoring update after manual review\n\nThe scoring done by AI was quite biased. Upon further review it is less dramatic. \n\n- User Interaction **None**: There's _almost_ no interaction required, and none of the interaction is with the vulnerable system.\n- Confidentiality & Integrity **Low**: The extent of data confidentiality & integrity loss is bounded by the highest priviledged user in the entire NiceGUI application. There does not exist a means of performing data manipulating tasks that said admin cannot already do. \n- Availability **None**: No DDoS is possible with this. Site remains performant as ever.\n\n## Affected packages\n\n- `nicegui >= 2.22.0, < 3.5.0`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `nicegui 3.5.0`","depth":"twilight","depthScore":40,"depthScoreParts":{"impact":39.6,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}