Jupyter Server's Login Page Can Be Abused for Phishing: Update to 2.20.0
CVE-2025-61669 (CVSS 6.1) lets attackers craft a Jupyter Server login URL that bounces researchers and data scientists to any external site, turning the familiar Jupyter login page into a phishing launcher. Jupyter Server 2.17.0 and earlier are affected, fixed in 2.18.0. JupyterLab and Notebook 7 inherit the flaw via their backend. Reported by Noriaki Iwasaki of Japan's Cyber Defense Institute.
Table of contents
CVE-2025-61669 (CVSS 6.1) lets attackers craft a Jupyter Server login URL that bounces researchers and data scientists to any external site, turning the familiar Jupyter login page into a phishing launcher. Jupyter Server 2.17.0 and earlier are affected, fixed in 2.18.0. JupyterLab and Notebook 7 inherit the flaw via their backend. Reported by Noriaki Iwasaki of Japan's Cyber Defense Institute.
The web-based programming environment Jupyter Server, used by researchers, data scientists, and students worldwide, has a flaw in versions 2.17.0 and earlier that turns its login page into a launchpad for phishing. Tracked as CVE-2025-61669, it lets a crafted login URL bounce a user who trusted the genuine Jupyter screen straight to an attacker's fake site after they log in. The flaw itself was fixed in 2.18.0, but more serious vulnerabilities followed, so if you are installing or running Jupyter Server today, the safe version is the latest 2.20.0 or later. Staying on 2.19.0 or below leaves a separate CVSS 9.3 (Critical) flaw (CVE-2026-44727), covered below, unfixed.
The issue lives in the validation of the next query parameter used to send users back after they log in. Crafted URLs like /login?next=///example.com trick the server into bouncing the visitor to any external site after authentication. On its own, CVE-2025-61669 scores 6.1 (v3.1) and 6.3 (v4.0) β both rated medium. The attack vector is network-based, requires no login or password, and only needs the victim to click a crafted link. The targets are exactly the people whose accounts unlock the most: researchers holding sensitive datasets, models, and corporate credentials.
CVE-2025-61669 affects all Jupyter Server versions 2.17.0 and earlier, and the fix landed in 2.18.0. But 2.18.0 was not a single-issue release: it closed several vulnerabilities at once, and later 2.19.0 and 2.20.0 fixed still more. Because JupyterLab and Jupyter Notebook 7 run Jupyter Server under the hood, both inherit the same flaw, and multi-user JupyterHub deployments are no exception. That is why the answer is not "bump to 2.18.0 and you are done" but keeping the backend current.
When the Research Comes Out First Under Someone Else's Name
By the numbers this is a medium-severity open redirect. But being logged in to Jupyter is itself a signal that the account is worth chasing, which is why a "middling" score still attracts targeted attackers. The motive side is worth laying out before the URL parsing details.
The crews going after this flaw are not the mass phishing shops; they hunt specific researchers, graduate students, and in-house data scientists by name. Academic espionage clusters monitoring university labs and national institutes for pre-print data, industrial spies after the in-house training models of pharma and AI startups, BEC scam groups using a grad student as a stepping stone to impersonate the supervisor, insider conspirators trading the intermediate results of cryptography and drug-discovery work, and targeted groups that only activate on the eve of paper submissions and hackathons when stress and fatigue are highest. What they walk away with is the kind of material that compresses years of labor into one file β drafts under review and the experiment data behind them, thesis source data, supervisor correspondence, in-house LLM training data and prompts, intermediate drug-discovery results, the API keys and S3 tokens bound to SageMaker and Azure ML, and the weights of a self-trained Stable Diffusion model. What this CVE actually delivers: the victim sees a genuine Jupyter URL on the legitimate institution's domain, logs in, gets redirected to a pixel-perfect copy, types the password again, and notebooks, datasets, and cloud credentials walk out together. Browser warnings never fire because the first hop is the real domain.
The reason this attack lands is that it lines up perfectly with how Jupyter users actually work. Researchers regularly hit "session expired" prompts late at night, and as long as the URL host matches the university domain they do not pause. The /login?next=///attacker.example.com/jupyter form preserves the institutional host in the preview pane of every mail client, which is exactly when guard drops. Once one password is captured, the stolen account becomes a foothold inside the same JupyterHub, so neighboring researchers on the multi-user deployment fall in turn. Because the S3 and SageMaker permissions reachable through Jupyter are usually scoped to the individual user, after-the-fact attribution β "who actually accessed this dataset?" β becomes a forensic problem on top of the breach itself.
A 6.1 score quantifies the redirect mechanics; the loss researchers and graduate students actually face is years of unpublished work and the originality of a PhD thesis being published first under someone else's name, or quietly shipped by a competitor product before yours. In research, data stops being original the moment it is copied.
What Jupyter Server actually is
Jupyter Server is the web backend that powers the "notebook" interface where users write Python or R code in the browser and capture results inline. What people interact with directly is usually JupyterLab or Jupyter Notebook 7. Behind those frontends, Jupyter Server exposes the REST endpoints for file listing, kernel execution, and terminal access.
As the official docs note, most users never install Jupyter Server directly; it is pulled in as a dependency of JupyterLab or Notebook. Real-world deployments include:
- University courses for machine learning, statistics, and data science labs
- Corporate data teams running an in-house alternative to Google Colab
- Research labs with JupyterHub on top of shared GPU servers
- Managed services such as AWS SageMaker, Azure ML, and Google Vertex AI
- Personal Stable Diffusion / LLM experiment environments on home GPU rigs
Modern releases of JupyterLab and Notebook always call into Jupyter Server. A simple pip install jupyterlab or pip install notebook pulls in Jupyter Server without you noticing, so if an old copy is lingering, you are quietly running a vulnerable backend.
Inside CVE-2025-61669
The root cause lives in LoginFormHandler._redirect_safe() in jupyter_server/auth/login.py. The intended design was a two-step check: allow only paths under the server's own base URL, or allow full URLs that pass the CORS origin check. NVD classifies it as CWE-601 (URL Redirection to Untrusted Site).
| Item | Value |
|---|---|
| CVE ID | CVE-2025-61669 |
| GHSA ID | GHSA-qh7q-6qm3-653w |
| JVN ID | JVN#01719116 |
| CVSS v3.1 | 6.1 (Medium) |
| CVSS v4.0 | 6.3 (Medium) |
| Weakness | CWE-601 URL Redirection to Untrusted Site |
| Affected | jupyter_server β€ 2.17.0 |
| Fixed in | 2.18.0 (this flaw) Recommended: latest 2.20.0+ |
| Vulnerable function | LoginFormHandler._redirect_safe() |
| Auth required | No (PR:N) |
| User interaction | Required (click a crafted URL) |
| Exploitation | PoC only as of 2026-07-23 Not in CISA KEV, no widespread abuse |
| Workaround | None β upgrade is the only fix |
The PoC from GHSA-qh7q-6qm3-653w collapses to a single URL.
PoC
http://localhost:8888/login?next=///google.comVisiting that URL and logging in sends the user to Google instead of back to the local server. Replace google.com with any attacker-controlled host and the redirect lands the victim on a fake login page.
Why does ///example.com get through? Browsers interpret //example.com as "same scheme as the current page, host example.com" (a protocol-relative URL), and the same logic extends to ///example.com. The server's validation, however, sees an empty scheme and empty netloc and decides "this must be a local path" β letting the redirect through. It is the textbook CWE-601 bypass.
Why a "mere redirect" makes phishing convincing
An open redirect (an attacker-controlled bounce to any URL) does not steal data by itself. OWASP still warns about it because it is the missing piece that makes a phishing email look legitimate.
A realistic lab / corporate JupyterHub scenario looks like this:
- An attacker hosts a look-alike Jupyter login page at, say,
phish.example - They send an email saying "the server was rebooted, please log in again" with a URL that starts with the real domain:
https://jupyter.lab.univ.ac.jp/login?next=///phish.example - The recipient sees
jupyter.lab.univ.ac.jpat the start of the URL and clicks - They land on the real Jupyter Server, log in, and get redirected to
///phish.exampleβ the look-alike site - The look-alike says "your session expired, please re-enter your password" and harvests the credentials
The reason this works is that the first hostname the user sees is genuinely the lab's or company's. Mail-client link previews and browser address bars both display the real domain initially. In JupyterHub deployments protected by SAML or OAuth, harvested credentials translate directly into a foothold on the university or corporate identity provider.
For an attacker after research data or models, Jupyter credentials are a near-direct path. In SageMaker / Azure ML / Vertex AI environments, the linked IAM role can extend the blast radius far beyond a single notebook server.
Why JupyterLab and Notebook 7 inherit this
The second uncomfortable part of this CVE: users who never knowingly installed Jupyter Server are still exposed. JupyterLab and Jupyter Notebook 7 (the "successor to Classic Notebook") both call Jupyter Server internally. As this notebook dependency issue documents, Notebook 7.1+ requires jupyter_server.
The exposure matrix:
| What you use | Likely vulnerable | Fix |
|---|---|---|
| JupyterLab (self-installed) | High | pip install -U jupyter_server |
| Jupyter Notebook 7 | High | pip install -U jupyter_server |
| Classic Notebook (v6) | Low (no jupyter_server) | Usually unaffected |
| JupyterHub (shared) | High (per-user servers) | Update jupyter_server in spawner image |
| AWS SageMaker / Azure ML / Vertex AI | Depends on cloud vendor | Watch vendor advisories |
| conda / mamba env | High | conda update jupyter_server |
The entry point for remediation is bumping jupyter_server to the latest 2.20.0+ via pip install -U jupyter_server or conda update jupyter_server, then restarting JupyterLab or Notebook. Cross-check the version against the latest PyPI release.
Watch out for Anaconda distributions or internal package mirrors that may lag. Run jupyter --version, check the jupyter_server line, and upgrade it individually if it has not reached the latest 2.20.0.
2.18.0 was not the end: the more serious flaws that followed
The subject of this article is the open redirect, but Jupyter Server has kept shipping security fixes since. That is why the refrain here is "go to the latest," not "2.18.0 and relax." One flaw in particular is clearly more serious than this article's redirect β and it is not fixed in 2.18.0 or 2.19.0.
CVE-2026-44727: stored XSS via nbconvert (CVSS 9.3, Critical)
The HTTP handler in nbconvert β the component that converts notebooks into HTML and other formats β served HTML without a sandbox directive (a setting that blocks embedded scripts from running). As a result, a malicious script planted in a page runs by itself in the browser of whoever opens it later β a "stored XSS." Severity is CVSS v4.0 9.3, the top "Critical" tier. From there an attacker can steal the user's cookies (the small credential that keeps you logged in), seize /api/ privileges, and ultimately reach arbitrary command execution through the code-running kernel. It affects 2.19.0 and earlier and is fixed in 2.20.0. Stay on 2.18.0 or 2.19.0 and this Critical hole remains open. This is the single biggest reason to insist on "2.20.0 or later" today.
Other flaws closed alongside it
2.18.0 did not fix only CVE-2025-61669. Its release notes show it also patched CVE-2026-40110 and CVE-2026-35397. On top of that, CVE-2026-40934 is an authentication-bypass issue where the old session cookie stayed valid even after a password change (insufficient session invalidation, so the previous credential kept working) β also addressed in 2.18.0. On the frontend side, CVE-2026-40171 is a stored XSS in Jupyter Notebook that leaks the authentication token. Chase a single CVE number and you miss this bundle of fixes.
As of 2026-07-23, CVE-2025-61669 is not listed in the U.S. CISA catalog of actively exploited vulnerabilities (KEV); public information shows only a proof-of-concept, with no reports of widespread abuse. Still, more than two months on, the mechanics are public, and once you factor in the more serious CVE-2026-44727, there is no reason not to move to the latest.
Reported by Japan's Cyber Defense Institute
CVE-2025-61669 was reported by Noriaki Iwasaki of the Tokyo-based independent security firm Cyber Defense Institute. The company specializes in penetration testing, forensics, and malware analysis, and has a track record of reporting vulnerabilities in domestic and overseas OSS through JPCERT/CC, including in EC-CUBE.
The GHSA credits also list Jupyter core maintainers Yann-P and Carreau (Matthias Bussonnier, well known as a long-time IPython/Jupyter maintainer) as coordinators, with dlqqq (David L. Qiu, Jupyter AI lead) authoring the fix. The flow β Japanese researcher reports, Jupyter core coordinates, fix ships in 2.18.0 β is a clean example of coordinated vulnerability disclosure.
GitHub's advisory went public on May 5, 2026, and the domestic notice via JPCERT/CC as JVN#01719116 followed on May 28, 2026 β roughly three weeks behind. That gap is typical: many Japanese institutions do not track English-language security feeds directly, and JVN's publication is what triggers downstream awareness inside national research institutes and SIers.
What researchers and operators should do now
Phishing is often framed as a "did the human get tricked?" problem, but whether an attacker can spoof your domain is something the technical side can decisively eliminate. For Jupyter Server operators and individual users alike, the actions are:
- Upgrade
jupyter_serverto the latest 2.20.0 or later (2.18.0 and 2.19.0 leave the Critical CVE-2026-44727 open) - Do not treat one upgrade as final; Jupyter Server ships security fixes on an ongoing basis, so keep tracking the latest release routinely
- For JupyterHub deployments, also update
jupyter_serverin the per-user (singleuser) images and conda environments spawners produce - If you depend on SageMaker / Vertex AI / Azure ML managed notebooks, watch your cloud vendor's security feed for patched images and scheduled restarts
- Brief your lab or organization: a Jupyter login URL containing
?next=should not be clicked - If a reverse proxy fronts your Jupyter Server, restrict
/loginaccess to internal networks as defense in depth
As a longer-term posture, combining a routine review of known-exploited vulnerabilities (see our CISA KEV dashboard) with automated dependency monitoring (our OSS supply chain scanner overview is a useful starting point) is the shortest path to catching this kind of "quietly bundled OSS" flaw early. Dependencies you never knowingly installed β like Jupyter Server β are exactly where automated scanning earns its keep.
Takeaway
CVE-2025-61669 is a CVSS 6.1 "medium" on paper, but the target is Jupyter Server β quietly running under the AI and data-science work of researchers worldwide β the flaw automatically extends to JupyterLab and Notebook 7, and the credentials being phished are exactly those of research and university accounts. That is not something to shrug off. The flaw itself is closed in 2.18.0.
But since 2.18.0, Jupyter Server has shipped fixes for more serious flaws, including the CVSS 9.3 XSS (CVE-2026-44727) that hits 2.19.0 and earlier. As of 2026-07-23 the safe version is the latest 2.20.0 or later; none of these are in KEV and no widespread abuse is confirmed, but the mechanics are public. Run pip list or jupyter --version, check the jupyter_server version, and upgrade if it is below 2.20.0. And the realistic way to keep this backend safe is not "upgrade once and forget" but tracking the latest release on an ongoing basis.
References
- βΈ GitHub Security Advisory β GHSA-qh7q-6qm3-653w (CVE-2025-61669)
- βΈ NVD β CVE-2025-61669
- βΈ JVN#01719116 β Open redirect vulnerability in Jupyter Server (JPCERT/CC)
- βΈ GitHub Security Advisory β GHSA-fcw5-x6j4-ccmp (nbconvert XSS / CVE-2026-44727, CVSS 9.3, fixed in 2.20.0)
- βΈ jupyter_server release notes (fixes across 2.18.0β2.20.0)
- βΈ jupyter_server/auth/login.py (patched code)
- βΈ jupyter-server β PyPI (check the latest release)
- βΈ Cyber Defense Institute, Inc. (reporter affiliation)
- βΈ OWASP β Open Redirect
- βΈ CWE-601: URL Redirection to Untrusted Site

Makoto Horikawa
Backend Engineer / AWS / Django