GHSA-MHPG-C27V-6MXR
Vulnerability from github – Published: 2026-01-08 20:16 – Updated: 2026-01-08 20:16Summary
An 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.
Details
The problem is traced as follows:
- On
pushstate,handleStateEventis executed.
https://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/elements/sub_pages.js#L38-L39
handleStateEventemitssub_pages_openevent.
https://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/elements/sub_pages.js#L22-L25
SubPagesRouter(used byui.sub_pages), lisnening onsub_pages_open,_handle_openruns.
https://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/sub_pages_router.py#L18-L22
_handle_openfinds anySubPagesand runs_show()on them
https://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/sub_pages_router.py#L63-L71
- 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
https://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/elements/sub_pages.py#L76-L100
- CULPRIT
_handle_scrollingruns_scroll_to_fragmentas there is a fragment, which runs vulnerable JS if thefragment(attacker-controlled) escapes out of the quotes.
https://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/elements/sub_pages.py#L206-L217
PoC
Just visiting this page (no click required), consistently triggers XSS in https://nicegui.io domain.
<html>
<body>
<iframe id="myiframe" src="https://nicegui.io" width="100%" height="600px" onload="triggerXSS()"></iframe>
<script>
function triggerXSS() {
if (!myiframe.src.includes("#")) {
myiframe.src = "https://nicegui.io#x');alert(document.domain)//";
}
}
</script>
</body>
</html>
Impact
Any page which uses ui.sub_pages and does not actively prevent itself from being put in an iframe is affected.
The 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).
Patch
- Not use
ui.sub_pages - Block iframe with the following code
@app.middleware('http')
async def iframe_blocking_middleware(request, call_next):
response = await call_next(request)
response.headers['X-Frame-Options'] = 'DENY'
return response
Appendix
AI is used safely to judge the CVSS scoring (input is censored).
Please find the results in https://poe.com/s/3FXuwp7TAYxqLomARXma
Scoring update after manual review
The scoring done by AI was quite biased. Upon further review it is less dramatic.
- User Interaction None: There's almost no interaction required, and none of the interaction is with the vulnerable system.
- 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.
- Availability None: No DDoS is possible with this. Site remains performant as ever.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.4.1"
},
"package": {
"ecosystem": "PyPI",
"name": "nicegui"
},
"ranges": [
{
"events": [
{
"introduced": "2.22.0"
},
{
"fixed": "3.5.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-21873"
],
"database_specific": {
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-01-08T20:16:41Z",
"nvd_published_at": "2026-01-08T10:15:55Z",
"severity": "HIGH"
},
"details": "### 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=\u0027smooth\u0027)` 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\u003chtml\u003e\n \u003cbody\u003e\n \u003ciframe id=\"myiframe\" src=\"https://nicegui.io\" width=\"100%\" height=\"600px\" onload=\"triggerXSS()\"\u003e\u003c/iframe\u003e\n \u003cscript\u003e\n function triggerXSS() {\n if (!myiframe.src.includes(\"#\")) {\n myiframe.src = \"https://nicegui.io#x\u0027);alert(document.domain)//\";\n }\n }\n \u003c/script\u003e\n \u003c/body\u003e\n\u003c/html\u003e\n```\n\n\u003cimg width=\"1429\" height=\"643\" alt=\"image\" src=\"https://github.com/user-attachments/assets/310dbb5c-65d5-44f2-8417-dcf044829bc6\" /\u003e\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(\u0027http\u0027)\nasync def iframe_blocking_middleware(request, call_next):\n response = await call_next(request)\n response.headers[\u0027X-Frame-Options\u0027] = \u0027DENY\u0027\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\u0027s _almost_ no interaction required, and none of the interaction is with the vulnerable system.\n- Confidentiality \u0026 Integrity **Low**: The extent of data confidentiality \u0026 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.",
"id": "GHSA-mhpg-c27v-6mxr",
"modified": "2026-01-08T20:16:41Z",
"published": "2026-01-08T20:16:41Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/zauberzeug/nicegui/security/advisories/GHSA-mhpg-c27v-6mxr"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-21873"
},
{
"type": "PACKAGE",
"url": "https://github.com/zauberzeug/nicegui"
},
{
"type": "WEB",
"url": "https://github.com/zauberzeug/nicegui/releases/tag/v3.5.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "NiceGUI apps which use `ui.sub_pages` vulnerable to zero-click XSS"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.