Media

Craft CMS: Stored XSS via Structure entry title in table view

  • Publicada: 06.07.2026
  • Actualizado: 11.10.2026 10:22

Stored XSS via Structure entry title in table view

Summary

An Author-level control panel user can store a JavaScript payload in an entry title. When an admin, or any control panel user with saveEntries for the same Structure section, drags another entry under the poisoned entry in table view, the payload executes in the victim’s session.

The issue is exploitable because the title is escaped into data-title by the server, decoded again by the browser, read with jQuery .data('title'), and then concatenated into a new HTML string without attribute escaping.

Execution was verified with alert(document.domain). Impact was verified against an elevated admin session by changing the admin user’s email through users/save-user, then using the password-reset flow to take over the account.

Preconditions

  • Attacker has createEntries + saveEntries on a Structure-type section (Author-style CP account; no admin permission required).
  • Victim has saveEntries on the same section. Craft only renders drag handles when structureEditable is true, which requires this permission, so any admin qualifies.
  • Section must be type Structure. Channel and Single sections are unaffected.
  • Poisoned entry must have no children at the time of the drag (fires on the 0-to-1 descendant transition).
  • For the email-change account-takeover path, the victim must currently have an elevated session. The stored XSS itself does not require elevation.

Root cause

ElementTableSorter.js lines 643-652:

js
const ancestorTitle = this._updateAncestors._$ancestor.data('title');
$(
  '<button … aria-label="' +
    Craft.t('app', 'Show {title} children', {title: ancestorTitle}) +
  '"></button>'
).insertAfter(…);

Craft.t with a {title} token calls _parseToken, reaches case 'none': return arg (Craft.js:193-194), and returns the title verbatim. The result is handed to jQuery's $() and parsed as HTML.

The server-side template correctly encodes the entry title into data-title, but the browser decodes that attribute before jQuery returns it from .data('title'). At that point the value is attacker-controlled HTML, and it is inserted into the aria-label attribute without Craft.escapeHtml().

Steps to reproduce

Plant (attacker - Author account):

bash
python3 poc.py --url http://target --cp admin \
  --user author@example.com --pass secret --section mySection

Trigger (victim - any control panel user with saveEntries, e.g. admin):

  1. Open the section index in Table view.
  2. Drag the "Drag me" entry and drop it as the first child of the poisoned entry.

alert(document.domain) fires in the victim’s session.

Impact payload tested in an elevated admin session (249 chars, within the 255-char title limit):

html
"><img src=x onerror="fetch(Craft.actionUrl+'users/save-user',{method:'POST',body:Craft.csrfTokenName+'='+encodeURIComponent(Craft.csrfTokenValue)+'&userId=1&email=attacker%40evil.com',headers:{'Content-Type':'application/x-www-form-urlencoded'}})">

When triggered during an elevated admin session, the admin’s email is changed to the attacker-controlled address. The attacker can then request a password reset and receive the reset link.

Impact

Stored XSS in the control panel from an Author-level account. The payload runs as the victim control panel user and can use Craft.csrfTokenName / Craft.csrfTokenValue to send same-origin action requests as that user.

In my test environment, triggering the payload in an elevated admin session allowed Author-to-admin account takeover via admin email change and password reset.

Mitigating factors

  • Requires an existing control panel account (Author role minimum).
  • Victim must perform a drag operation, not just visit the page.
  • The demonstrated email-change takeover requires the victim’s session to be elevated at trigger time.

Resources

https://github.com/craftcms/cms/commit/162321e899cc97517fb6f5a02b5528f549d0c6cc

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Paquete de Composer craftcms/cms >= 5.0.0-RC1, < 5.9.53 5.9.53

Referencias

Puntuación CVSS -
Vector CVSS
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N
Debilidad (CWE)
CWE-79
Plataforma
Composer
Paquete
craftcms/cms
Publicada
06.07.2026
Actualizado
11.10.2026 10:22

Fuentes y licencias

OSV / GitHub Advisory Database - GHSA-xrqc-p465-2xvg - CC BY 4.0

Historial de vulnerabilidades: craftcms/cms

Todas las vulnerabilidades publicadas de este paquete (Paquete de Composer), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Todas (127) en el archivo
Entradas en total
127
Primera → última
07.2021 08.2026
Sin corrección
6
Explotada en ataques
4

Distribución por gravedad

  • Crítica 9
  • Alta 41
  • Media 65
  • Baja 12

Divulgaciones por año

  1. 2021: 3
  2. 2022: 22
  3. 2023: 13
  4. 2024: 9
  5. 2025: 6
  6. 2026: 74
Gravedad:
  1. Crítica CVE-2026-72780 >= 5.0.0-RC1, < 5.10.5 5.10.5
  2. Alta CVE-2026-72781 >= 5.0.0-RC1, < 5.10.7, >= 4.0.0-RC1, < 4.18.3 5.10.7, 4.18.3
  3. Alta CVE-2026-72778 >= 5.0.0-RC1, < 5.10.6, >= 4.0.0-RC1, < 4.18.2 5.10.6, 4.18.2
  4. Craft CMS: Stored XSS via Structure entry title in table view esta entrada
    Media CVE-2026-55793 >= 5.0.0-RC1, < 5.9.53 5.9.53
  5. Alta CVE-2026-55790 >= 5.0.0-RC1, < 5.9.23, >= 4.0.0-RC1, < 4.17.16 5.9.23, 4.17.16
  6. Alta CVE-2026-50282 >= 5.0.0-RC1, < 5.9.21, >= 4.0.0-RC1, < 4.17.14 5.9.21, 4.17.14
  7. Alta CVE-2026-50284 >= 5.0.0-RC1, < 5.9.22, >= 4.0.0-RC1, < 4.17.15 5.9.22, 4.17.15
  8. Crítica CVE-2026-55791 >= 5.0.0-RC1, < 5.10, >= 4.0.0-RC1, < 4.18 5.10, 4.18
  9. Alta CVE-2026-44011 >= 4.0.0, < 4.17.12, >= 5.0.0, < 5.9.18 4.17.12, 5.9.18
  10. Alta CVE-2026-44010 >= 5.0.0, < 5.9.18, >= 4.0.0, < 4.17.12 5.9.18, 4.17.12
  11. Alta CVE-2026-32267 >= 4.0.0-RC1, < 4.17.6, >= 5.0.0-RC1, < 5.9.12 4.17.6, 5.9.12
  12. Alta CVE-2026-32264 >= 4.0.0-RC1, < 4.17.5, >= 5.0.0-RC1, < 5.9.11 4.17.5, 5.9.11
  13. Alta CVE-2026-32263 >= 5.6.0, < 5.9.11 5.9.11
  14. Alta CVE-2026-31857 >= 5.0.0-RC1, < 5.9.9, >= 4.0.0-beta.1, < 4.17.4 5.9.9, 4.17.4
  15. Alta CVE-2026-31858 >= 5.0.0-RC1, < 5.9.9 5.9.9
  16. Alta CVE-2026-29069 >= 5.0.0-RC1, < 5.9.0-beta.2, >= 4.0.0-RC1, < 4.17.0-beta.2 5.9.0-beta.2, 4.17.0-beta.2
  17. Crítica CVE-2026-28697 >= 5.0.0-RC1, < 5.9.0-beta.1, >= 4.0.0-RC1, < 4.17.0-beta.1 5.9.0-beta.1, 4.17.0-beta.1
  18. Alta CVE-2026-28696 >= 4.0.0-RC1, < 4.17.0-beta.1, >= 5.0.0-RC1, < 5.9.0-beta.1 4.17.0-beta.1, 5.9.0-beta.1
  19. Alta CVE-2026-27127 >= 5.0.0-RC1, < 5.8.23, >= 3.5.0, < 4.16.19 5.8.23, 4.16.19
  20. Alta CVE-2026-25498 >= 5.0.0-RC1, < 5.8.22, >= 4.0.0-RC1, < 4.16.18 5.8.22, 4.16.18
  21. Alta CVE-2026-25497 >= 5.0.0-RC1, < 5.9.0-beta.1, >= 4.0.0-RC1, < 4.17.0-beta.1 5.9.0-beta.1, 4.17.0-beta.1
  22. Alta CVE-2026-25495 >= 5.0.0-RC1, < 5.8.22, >= 4.0.0-RC1, < 4.16.18 5.8.22, 4.16.18
  23. Alta CVE-2025-68455 >= 5.0.0-RC1, < 5.8.21, >= 4.0.0-RC1, < 4.16.17 5.8.21, 4.16.17
  24. Alta CVE-2025-68456 >= 5.0.0-RC1, < 5.8.21, >= 3.0.0, < 4.16.17 5.8.21, 4.16.17
  25. Alta CVE-2025-46731 >= 4.0.0-RC1, < 4.14.13, >= 5.0.0-RC1, < 5.6.15 4.14.13, 5.6.15
  26. Crítica · 10.0 Explotada en ataques CVE-2025-32432 >= 3.0.0-RC1, < 3.9.15, >= 4.0.0-RC1, < 4.14.15, >= 5.0.0-RC1, < 5.6.17 3.9.15, 4.14.15, 5.6.17
  27. Alta · 8.0 Explotada en ataques CVE-2025-23209 >= 5.0.0-RC1, < 5.5.8, >= 4.0.0-RC1, < 4.13.8 5.5.8, 4.13.8
  28. Crítica · 9.8 Explotada en ataques CVE-2024-56145 >= 5.0.0-RC1, < 5.5.2, >= 4.0.0-RC1, < 4.13.2, >= 3.0.0, < 3.9.14 5.5.2, 4.13.2, 3.9.14
  29. Alta · 7.2 CVE-2024-52293 >= 4.0.0-RC1, < 4.12.2, >= 5.0.0-RC1, < 5.4.3 4.12.2, 5.4.3
  30. Alta · 7.7 CVE-2024-52292 >= 5.0.0-alpha.1, < 5.4.9, >= 3.5.13, < 4.12.8 5.4.9, 4.12.8
  31. Alta · 8.4 CVE-2024-52291 >= 5.0.0-RC1, < 5.4.6, >= 4.0.0-RC1, < 4.12.5 5.4.6, 4.12.5
  32. Crítica · 9.8 CVE-2024-37843 <= 3.7.31 sin corrección
  33. Alta · 7.5 CVE-2023-36260 < 4.6.2 4.6.2
  34. Crítica · 10.0 CVE-2023-41892 >= 4.0.0-RC1, < 4.4.15 4.4.15
  35. Alta · 7.2 CVE-2023-40035 >= 4.0.0-RC1, < 4.4.15, >= 3.0.0, < 3.8.15 4.4.15, 3.8.15
  36. Alta · 7.2 CVE-2023-32679 >= 4.0.0, < 4.4.6 4.4.6
  37. Alta · 7.5 CVE-2022-37783 >= 3.0.0, < 3.7.33 3.7.33
  38. Crítica · 9.8 CVE-2019-15929 < 3.1.7 3.1.7
  39. Alta · 7.2 CVE-2018-20465 <= 3.0.34 sin corrección
  40. Alta · 8.8 CVE-2018-3814 <= 2.6.3000 sin corrección
  41. Alta · 8.8 CVE-2022-29933 < 3.7.36 3.7.36
  42. Alta · 8.8 CVE-2021-41824 >= 3.4.0, < 3.7.14 3.7.14
  43. Crítica · 9.8 CVE-2021-27903 < 3.6.7 3.6.7

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.