Blog: Vulnerability Disclosure

Bullied by Bugcrowd over Kape CyberGhost disclosure

Ceri Coburn 05 May 2023

TL;DR

The CyberGhost VPN client suffers from an elevation of privilege vulnerability and is filed under CVE-2023-30237.  A specially crafted JSON payload sent to the CyberGhost RPC service can lead to command line injection when the OpenVPN process is launched, leading to full system compromise.  The latest 8.3.10.10015 version released on the 24 February 2023 fixes this issue.

Hunting the hunter

This post is as much about a recent EoP vulnerability found within the popular CyberGhost VPN client as my experiences of reporting my first vulnerability via Bugcrowd. Suffice to say, it was the worst disclosure experience I have witnessed to date.

It all started from something I experienced within the CyberGhost VPN eco system that almost ruined a 4-month Red Team operation. All disclosure paths led to Bugcrowd. So, with this in mind I thought I would give it a try. Due to the Bugcrowd T&C’s I can’t really go into much more detail than this, but what was reported was deemed a non-security issue, but a configuration change was made that altered the behaviour of what was reported, but was fixed anyway.

So, we at PTP decided to commission further dedicated research into the CyberGhost client itself. After several days of poking around, a command line injection vulnerability was found. Now some, reading this, might conclude that this was a form of retaliation. To some degree, it was, but not because of missing out on a bounty, I had no interest in that. It was more to do with how the original case was handled and how this affected an ongoing Red Team operation. Therefore, I had no intention whatsoever of reporting this new vulnerability via Bugcrowd.

The first challenge was finding a direct route to disclosure without involving Bugcrowd. Every avenue I tried came to a dead end. Online support pointed me toward Bugcrowd or CyberGhosts own disclosure submission page which was also powered by Bugcrowd. Eventually, a CyberGhost support ticket system representative sent me to [email protected]. Kape appeared to be the developers behind several well-known consumer VPN products.

After checking the well-known security.txt from the kape.com domain, additionally I could see the [email protected] email in addition to the Bugcrowd VDP page. Lo and behold, sending an email to this address also resulted in an automated email response from Bugcrowd asking me to claim the submission, but not before agreeing to the T&C’s. Luckily no vulnerability details were sent and was merely a probe to determine if this was a suitable channel to use.

The next day, I did receive a follow up email from a human via the security email address. Kape highlighted that they already covered my concerns via Bugcrowd. Kape had recognised my name from the Bugcrowd report and assumed it was the same vulnerability.

After explaining to Kape that this is nothing to do with the original disclosure, they still insisted that I submit via Bugcrowd. Multiple emails later, as I was clearly getting nowhere, I decided to submit the technical details directly to the security@ email address since there were humans monitoring this mailbox too.

Now what came next amazed me.

Kape had spoken to Bugcrowd directly to ask for advice on how they should deal with out of band contact. Now instead of responding to Kape’s request, Bugcrowd decided to hand me a code of conduct point.

Here are some of the snippets from the email I received from Bugcrowd:

We are reaching out today because we were made aware that you attempted to appeal a submission decision to the team at Kape VDP directly via email. All communication with any Program Owner should be done through the Bugcrowd platform, to ensure that if something needs to be escalated, we can be aware of it, and for our ASE team to have access to all necessary information related to the submissions.

This type of behavior is not tolerated on the Bugcrowd platform because it violates our Code of Conduct and is a behavior explicitly prohibited in our Platform Behavior Standards as “Out of Band Contact”:
We can not allow or condone unprofessional behavior, and have established a new point system to track Researchers who violate our site’s rules and Code of Conduct.

You currently have a total of 1 point which includes this most recent incident.

Kape had mentioned that I was trying to disclose an additional vulnerability outside of the platform, Bugcrowd confirmed this, but my understanding of Bugcrowd’s T&Cs regarding Out of Band communication is related to submissions already made via the platform. Bugcrowd and their clients do not indefinitely hold any rights to future vulnerabilities that a researcher may discover.

After several rounds of communication with Bugcrowd and explaining my rationale, eventually my code of conduct point was deducted with an apology.

Once Kape could see that disclosure was not going to go through Bugcrowd, the dialogue at this point between us was more inline with what I would usually experience. We agreed a way forward, disclosure would remain private until a fix was ready, and a fix was released.

In fairness to Kape, the fix was swift in comparison with other disclosure experiences in the past. I just wish that software vendors would offer direct disclosure routes in addition to bug bounty platforms. Some researchers would prefer the direct approach.

Rant over.

Statement from Kape / CyberGhost

Kape’s Security Team provided the following statement in thanks for the work we performed:

“Kape values collaboration and cooperation with security researchers throughout the world, and we invest heavily in ensuring security researchers are heard and that the lines of communication with our security and development teams are always open. We are sorry that you have had this experience with us on Bugcrowd and we are following up with the relevant parties at Bugcrowd so this doesn’t happen again. At the same time, we are delighted with the research performed by Pen Test Partners into our Windows application, and thank you for your efforts to improve the cybersecurity landscape for all. Collaboration in the cybersecurity community is critical to ensure security and privacy on the internet, and we will continue to act quickly once any valid issue has been reported to us, regardless of how the issue is reported. In this spirit, we are committed to continued transparency and partnership with all researchers who are focused on responsible disclosure.”

CVE-2023-30237

Like many VPN providers, CyberGhost software uses solutions such as OpenVPN or Wireguard to offer VPN services to their customers.  Most of these VPN solutions are typically split across an unprivileged UI component that communicates with a privileged Windows service running as SYSTEM.  If it’s not fully scrutinized it can lead to elevation of privilege vulnerabilities via this communications channel.

When a request is made to connect to the configured city or country via the unprivileged UI, the details are sent to the backed service and eventually the OpenVPN or Wireguard process is started to establish the underlying VPN connection.

In CyberGhost’s case, this communication is done over a named pipe called MachineNameCyberGhost8Service, where machine name is obviously the name of the machine the service is running on.  The developers have clearly made sure that the pipe is not accessible over the network, since the NETWORK principal has a deny ACL set, therefore only local privilege escalation would be possible.

It was found that connecting to the pipe would fail if the client did not originate from the Dashboard.exe process within the CyberGhost installation folder, but this could easily be bypassed via replacing the process name inside the connecting client’s Process Environment Block structure to match that of the legitimate process, or alternatively injecting code into the legitimate Dashboard.exe directly.

Next it was time to look at the communication protocol and how it worked.  Luckily for me, both the client and service were written in .NET, so the job was far easier than a native counterpart.  The RPC communication method was JSON and looked like initially that perhaps an immediate Remote Code Execution could be possible.

As you can see from the format, the direct C# interfaces from the services are exposed and the parameters for a specific method use the often abused $type specifier when deserialising using JsonSerializer from the Newtonsoft library.  But once again, the developers had done their due diligence and JsonSerializer was configured correctly and prevented arbitrary .NET types from being created during JSON deserialization.

With that path exhausted, attention shifted to the callable methods themselves.  The ConnectToVpnServer method was the most interesting, as this was the method that led to the construction of the command line string that was fed to openvpn.exe or the wireguard DLL, depending on which method was currently configured.

This eventually let me to a class called OpenVPN and CommandSanitizeHelpers.  Once again, evidence was found where developers had put efforts in place, this time to prevent command line injection.

There are several openvpn command line arguments that can be used to execute other processes or load arbitrary DLL’s such as the –plugin argument.

If any of these are detected, the resulting openvpn.exe process creation is prevented.

Inception

Now this is where things start to get interesting.  Once the backend service constructs the monolithic command line string argument that will be used, they are parsed using the native Windows API CommandLineToArgvWThis function is responsible for breaking up a single command line string into each unique argument as an array.  Each element within the array representing a single argument.  Spaces are treated as argument delimiters unless quoted, in which case, the complete argument is the text inside the quotes.  But, if you want the API to include a double quote within an argument itself, you can escape the quote using the \ character.  So as an example, the command line arg1 “arg 2” “arg \”” will be parsed into an array that looks like this.    

arg1
arg 2
arg 3”

Notice the parsed elements within the array do not have any quotes and have been stripped, other than what was requested with the escape sequence.

Once the CyberGhost service has broken up the individual command line arguments, each of these starting with the characters are checked for the banned values.  Now the developers clearly missed some nuances of the CommandLineToArgvW API.

The command line -\”-plugin\” c:\path\malicious.dll would result in a parsed array looking something like this

-“-plugin”

c:\path\malicious.dll

Since there is a quote separating the two characters, the processing rules by the CyberGhost service ignore these arguments and treat them as openvpn argument values instead of argument specifiers.  Now one would think that this is fine since openvpn would not recognise this as a valid argument, but, the majority of Windows processes also use the same CommandLineToArgvW API to split the command line into individual parts prior to calling the main(int argc, char**argv) function.  Since the quote is not separated by spaces, the CommandLineToArgVW API call simply strips the quote out of the argument.  Therefore

openvpn.exe -“-plugin” c:\path\malicious.dll

will execute in in the same way as

openvpn.exe –plugin c:\path\malicious.dll

on Windows based platforms.

Payload

The final payload that is sent over the named pipe looks something like this.

You’ll notice that most of the command line injection is built into the ServerIp field.  This is because the ServerIp field is the first field that is concatenated when the command line string is being constructed.  This is then proceeded by internal command line arguments that are added such as the CA server certificate, then finally the remaining ConnectionString field is appended to the end.  By leveraging the ServerIp field, we had far more control over the final command line that was sent to openvpn.exe.  Had the developers sanitised the ServerIp field for a valid IP address only, I’m not sure if exploitation would have been possible.  It certainly would have been more difficult.  The final command line argument that was eventually fed to openvpn looked something like this.

openvpn –remote “powershell -ec AAA….” –“-plugin “c:\path\CyberGhostPlugin.dll” –dev tap0

The reason that the ConnectionString JSON property string is missing from the command line is due to the subtle fact that we embedded a null character at the end of the ServerIp field within the JSON payload.  This meant that even though it was concatenated in the C# code, by the time it reached the CommandLineToArgVW API call, the null character was treated as end of string, allowing greater control over the final generated command line since we also could exclude anything from the ConnectionString property.

The exploit leverages openvpn’s plugin feature to gain code execution, therefore a simple plugin was written that queried the –remote argument and treated this as a command to execute instead.

Disclosure Timeline

As you can see, the disclosure timeline is somewhat colourful:

  • 3rd January 2023 – Initial support ticket sent to CyberGhost support, eventually leading to [email protected] email address.
  • 4th January 2023 – Response from Kape indicating that they already addressed concerns via Bugrowd, response sent indicating this was a new vulnerability unrelated to the original.
  • 5th – 18th January 2023 – Several communications back and forth when attempting to get a direct line of communication.
  • 18th January 2023 – Gave up and sent the details directly to security@ automated Bugcrowd issue capture mailbox.
  • 20th January 2023 – Complaint email received from Bugcrowd and 1 code of conduct point awarded.
  • 20th January 2023 – Explanation sent to Bugcrowd, leading to code of conduct point being deducted.
  • 27th January 2023 – Further details provided regarding PoC sent on the 18th due to binaries not matching source code that was sent.
  • 31st January 2023 – Kape now happy the source code matches the binary and working on validating the issue.
  • 1st March 2023 – Update received from Kape indicating that they are in the process of rolling out the fix but would like to have an advance copy of the blog prior to release and would like to include a few words of appreciation within the post
  • 20th March 2023 – Kape indicated that the fix was released on the 24th February 2023 and querying when they could receive advance copy of the blog.  
  • 27th March 2023 – Draft blog post is provided to Kape
  • 5th May 2023 – Blog published.