JupyterHub deployments running nbgrader 0.9.4 with default settings allow any authenticated user to exfiltrate formgrader content—grades, ML submissions, research notebooks—by tricking a privileged user into visiting a malicious page on the same origin. Patch to nbgrader 0.9.5 immediately. If patching is delayed, disable frame-ancestors: self in JupyterHub CSP or enable per-subdomain isolation (JupyterHub.enable_subdomains=True).
What is the risk?
Moderate risk for academic and enterprise ML/AI platforms running JupyterHub with nbgrader. Exploitation requires a valid JupyterHub account plus successful social engineering to deliver a malicious link to a formgrader-privileged user, limiting opportunistic mass exploitation. However, formgrader content can include proprietary ML code, research datasets, and student PII. EPSS of 0.277% and no CISA KEV listing indicate no active exploitation in the wild at this time.
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| nbgrader | pip | = 0.9.4 | 0.9.5 |
Do you use nbgrader? You're affected.
How severe is it?
What should I do?
5 stepsPATCH
Upgrade nbgrader to 0.9.5 — the patched version reverts the frame-ancestors: self header change.
WORKAROUND A
Explicitly disable frame-ancestors: self in JupyterHub Content Security Policy configuration.
WORKAROUND B
Set JupyterHub.enable_subdomains=True to enforce per-user origin isolation, preventing cross-frame DOM access even if IFrame embedding is allowed.
VERIFY
Inspect live CSP headers (curl -I or browser DevTools > Network > Response Headers) to confirm frame-ancestors policy before and after remediation.
HARDEN
Restrict formgrader access to the minimum required users via JupyterHub role/group configuration to reduce the attack surface.
What does CISA's SSVC say?
Source: CISA Vulnrichment (SSVC v2.0). Decision based on the CISA Coordinator decision tree.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2025-23205?
JupyterHub deployments running nbgrader 0.9.4 with default settings allow any authenticated user to exfiltrate formgrader content—grades, ML submissions, research notebooks—by tricking a privileged user into visiting a malicious page on the same origin. Patch to nbgrader 0.9.5 immediately. If patching is delayed, disable frame-ancestors: self in JupyterHub CSP or enable per-subdomain isolation (JupyterHub.enable_subdomains=True).
Is CVE-2025-23205 actively exploited?
No confirmed active exploitation of CVE-2025-23205 has been reported, but organizations should still patch proactively.
How to fix CVE-2025-23205?
1. PATCH: Upgrade nbgrader to 0.9.5 — the patched version reverts the frame-ancestors: self header change. 2. WORKAROUND A: Explicitly disable frame-ancestors: self in JupyterHub Content Security Policy configuration. 3. WORKAROUND B: Set JupyterHub.enable_subdomains=True to enforce per-user origin isolation, preventing cross-frame DOM access even if IFrame embedding is allowed. 4. VERIFY: Inspect live CSP headers (curl -I or browser DevTools > Network > Response Headers) to confirm frame-ancestors policy before and after remediation. 5. HARDEN: Restrict formgrader access to the minimum required users via JupyterHub role/group configuration to reduce the attack surface.
What systems are affected by CVE-2025-23205?
This vulnerability affects the following AI/ML architecture patterns: Jupyter notebook platforms, AI/ML training environments, academic ML platforms, data science workspaces.
What is the CVSS score for CVE-2025-23205?
No CVSS score has been assigned yet.
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0011.003Malicious LinkAML.T0012Valid AccountsAML.T0036Data from Information RepositoriesAML.T0049Exploit Public-Facing ApplicationCompliance Controls Affected
What are the technical details?
Original Advisory
### Impact Enabling frame-ancestors: 'self' grants any JupyterHub user the ability to extract formgrader content by sending malicious links to users with access to formgrader, at least when using the default JupyterHub configuration of `enable_subdomains = False`. #1915 disables a protection which would allow user Alice to craft a page embedding formgrader in an IFrame. If Bob visits that page, his credentials will be sent and the formgrader page loaded. Because Alice's page is on the same Origin as the formgrader iframe, Javasript on Alice's page has _full access_ to the contents of the page served by formgrader using Bob's credentials. ### Workarounds - Disable `frame-ancestors: self`, or - enable per-user and per-service subdomains with `JupyterHub.enable_subdomains = True` (then even if embedding in an IFrame is allowed, the host page does not have access to the contents of the frame). ### References JupyterHub documentation on why and when `frame-ancestors: self` is insecure, and why it was disabled by default: https://jupyterhub.readthedocs.io/en/stable/explanation/websecurity.html#:~:text=frame-ancestors
Exploitation Scenario
On a corporate ML research platform or university AI course environment: Attacker Alice, a regular JupyterHub user without formgrader access, creates a malicious HTML page hosted on the same JupyterHub origin. The page silently embeds the formgrader URL in a hidden IFrame and runs JavaScript that polls the IFrame's contentDocument for loaded DOM content. Alice crafts a convincing pretext (e.g., 'check this notebook I shared with you') and sends the link to Bob, a TA or instructor with formgrader access. When Bob clicks the link, his authenticated session transparently loads formgrader inside the IFrame. Because Alice's page and the IFrame share the same origin, her JavaScript reads the full formgrader DOM—extracting student names, grades, submitted ML code, and dataset references—and POSTs everything to an attacker-controlled endpoint. Bob sees nothing unusual; the malicious page can display a decoy notebook to reduce suspicion.
Weaknesses (CWE)
CWE-1021Improper Restriction of Rendered UI Layers or FramesPrimaryCWE-668Exposure of Resource to Wrong SpherePrimaryCWE-1021 — Improper Restriction of Rendered UI Layers or Frames: The web application does not restrict or incorrectly restricts frame objects or UI layers that belong to another application or domain.
- [Implementation] Use the "frame-ancestors" directive in the Content Security Policy, which is intended to define restrictions when it comes to allowing embedded content [REF-1544].
- [Implementation] The use of X-Frame-Options (now partially obsolete as of September 2026 [REF-1545]) allows developers of web content to restrict the usage of their application within the form of overlays, frames, or iFrames. The developer can indicate from which domains can frame the content. The concept of X-Frame-Options is well documented, but implementation of this protection mechanism is in development to cover gaps; often, there is a need for allowing frames from multiple domains.
Source: MITRE CWE corpus.
References
- github.com/advisories/GHSA-fcr8-4r9f-r66m
- github.com/jupyter/nbgrader/commit/73e137511ac1dc02e95790d4fd6d4d88dab42325
- github.com/jupyter/nbgrader/pull/1915
- github.com/jupyter/nbgrader/security/advisories/GHSA-fcr8-4r9f-r66m
- jupyterhub.readthedocs.io/en/stable/explanation/websecurity.html
- nvd.nist.gov/vuln/detail/CVE-2025-23205
Timeline
Related Vulnerabilities
CVE-2026-2185810.0n8n: Input Validation flaw enables exploitation
Same attack type: Data ExtractionCVE-2025-282810.0LangChain RequestsToolkit: SSRF exposes cloud metadata
Same attack type: Data ExtractionCVE-2025-5376710.0Azure OpenAI: SSRF EoP, no auth required (CVSS 10)
Same attack type: Data ExtractionCVE-2023-376510.0MLflow: path traversal allows arbitrary file read
Same attack type: Data ExtractionGHSA-vvpj-8cmc-gx3910.0picklescan: security flaw enables exploitation
Same attack type: Auth Bypass