A look at OPC-UA, an emerging modern ICS protocol

 

Intro

The emergence of the Industry 4.0 is characterized by the digitization of industry and greater interconnection between the various machines that make up an industrial IS (Information System). However, this growth in communications within industrial Control Systems also leads to an increase in their attack surface. Moreover, the protocols used historically (such as Modbus), offer little or no security mechanism. Some of these protocols were also proprietary, which could cause interoperability problems between the different machines of the IS.

The OPC UA standard was created in 2008 by the OPC Foundation to address these issues, by proposing a standardization of communications between ICS machines, and by integrating many mechanisms to ensure the security of these communications.

 

The OPC UA standard

The OPC UA standard is an open-source and multiplatform communication standard. It can be implemented on any type of device, regardless of their operating system.


Possible communications offered by OPC UA (Source: OPC Foundation website)

Two types of architecture can be set up:

  • Client-server architecture: this is the most widely used architecture. It is composed of hardware and/or software elements that contain data, OPC UA servers that provide this data or services, and OPC UA clients that can interact with the servers to use their services or access their data.
  • PubSub architecture: it can be used to exchange a higher data volume. It is composed of Publishers who send messages, and Subscribers who receive these messages through a Message Oriented Middleware (MOM).

 

Client-server architecture security

As the client-server architecture is by far the most widely used, we will now look in more detail at the security mechanisms offered by the OPC UA standard in this type of architecture.

First of all, three levels of security are available regarding the encryption of communications between a client and an OPC UA server:

  • None: messages are sent in clear text, without any protection
  • Sign: messages are signed. This protects the integrity of the transmitted data, but not their confidentiality
  • SignAndEncrypt: messages are signed and encrypted. In this case, the confidentiality of the messages is also protected

To set up an encrypted channel, the client and server each have an X.509 certificate and an associated private key, which they use to exchange a session key in a secure channel. Then, they can use this session key to encrypt the rest of the exchanges, using symmetric encryption algorithms.

Several levels of security for user authentication are also available. To authenticate, clients send tokens to the servers called UserIdentityTokens, which contain the information necessary for the authentication process. There are several types of UserIdentityToken, and the server chooses which types it accepts:

  • AnonymousIdentityToken: this token does not contain any specific information. If the server accepts it, and authenticates the user as an anonymous user
  • UserNameIdentityToken: this token contains a username and a password. If these are valid, the user is authenticated and then obtains the profile and rights associated with his username
  • X509IdentityToken: this token contains an X.509 certificate. If the server has registered this certificate, the user is authenticated and then obtains a profile and the rights associated with the certificate
  • IssuedIdentityToken: this token encapsulates an access token provided by a third-party access management service, like an OAuth2 server for example

Finally, once authenticated, the user has access to the server’s nodes. Below is an example of nodes that could be encountered on an OPC UA server:

                 

 

 

 

 

 

 

 


OPC UA nodes

Access control can be set up to restrict access to some nodes to high-privileged users (administrators, etc.), or to require that the communication channel be encrypted to access some sensitive nodes. The figure below summarizes how access management to a node works:


Role overview extracted from chapter 2 of the OPC UA specifications

 

OPC UA audit tooling

Only few public tools are available to audit OPC UA applications. One of the most well-known is the Metasploit module called « msf-opcua ».

This module is composed of three scripts:

  • opcua_hello: sends a “Hello Message” to a list of IP addresses, for a given port, to detect the presence of OPC UA servers among this list
  • opcua_server_config: this script requires an authenticated access to an OPC UA server to be used. It allows to retrieve information on the configuration of the server endpoints (encryption, authentication…)
  • opcua_login: performs a dictionary attack on a server using username and password authentication

Although it provides some useful functionalities, this tool has some limitations. For example, it is not possible to scan several ports at once with the opcua_hello script. Another example is that the opcua_server_config script requires authentication to retrieve configuration information, which is available without authentication.

Therefore, Wavestone decided to improve this tool. It was decided to stop using the Metasploit framework, which imposed too many constraints, therefore the tool is now an independent Python script, renamed « opcua_scan ». It is based on the opcua-asyncio library, unlike the msf-opcua module which uses the python-opcua library declared deprecated by its authors.

The tool is accessible with this link, and provides two commands: “hello” and “server_config”, which reimplement and improve the functionality of the opcua_hello and opcua_server_config scripts of the msf-opcua module. The opcua_login script is not included, as no improvement were performed, and it can be used directly.

 

The hello command

This command is used to detect OPC UA applications in a network. It sends “Hello Message” to a list of IP addresses, on a given list of ports, and deduces the presence or absence of OPC UA servers on the targets. Then, the FindServers service, which is supposed to be implemented by any OPC UA server, is used to retrieve the ApplicationDescription of the server (and other OPC UA applications known by the server). This object contains useful information, such as the productUri, which gives information about the software or library used to run the detected server, or the discoveryUrls, which indicates the URLs to the server’s DiscoveryEndpoints. These endpoints can be used by the server_config command to retrieve more information about the server configuration.

Several options have been added to the command, such as the configuration of the timeout or the possibility to retrieve the list of detected servers in a JSON output file.

This is how the hello command could be used in practice:

$ python opcua_scan.py hello -i <IPs> -p <ports> -o hello_output.json


Example of results generated by the hello command

And the screenshot below shows an extract of the generated JSON file:


Extract of an output file generated by the hello command

The complete documentation of the hello command and all its options is available here.

 

The server_config command

Thanks to the DiscoveryEndpoints retrieved with the hello command, we now have access to the entire Discovery Service Set of the server. No authentication or encryption mechanisms are required to use these services. Among these services, the one called GetEndpoints can be used to retrieve the endpoints to connect to the server, as well as information about the configuration of these endpoints. This information is given through EndpointDescriptions objects, which contain, among others:

  • The security level of the encryption accepted on the endpoint (None, Sign ou SignAndEncrypt)
  • The signature or encryption algorithm used
  • The types of UserIdentityToken accepted by the endpoint (AnonymousIdentityToken, UserNameIdentityToken, X509IdentityToken or IssuedIdentityToken)

The server_config command allows to retrieve the EndpointDescriptions of all the servers detected via the hello command, and to identify among these servers those that accept anonymous authentication or the None security level. All this information is accessible for a non-authenticated user.

In addition, if an authenticated access to a server is possible, the command also allows to browse the nodes of the server and identify the rights that the current user has on these nodes. For example, it is possible to obtain a list of nodes of type Variable that can be written to, or a list of methods that can be executed by the user.

Finally, other useful options have been added to the server_config command:

  • -o (or –output) allows to set up a JSON output file to store the results of the command and browse them more easily than on a terminal. Additional information is stored there, such as the value of the UserWriteMask attribute of the nodes, which indicates which attributes of the nodes can be modified by the user.
  • -r (or –root_node) allows to browse only a subset of the server’s nodes from a starting node specified in the argument. Indeed, browsing all the nodes can be long and this option can be used to target the nodes of interest.

The complete documentation of the server_config command and all its options is available here.

In practice, this is how the server_config command could be used:

The output file of the hello command is given as an argument (via the -t option) and will be used to retrieve information about the endpoints of the detected servers::

$ python opcua_scan.py server_config -t hello_output.json


Example of results generated by the server_config command

Here, the server allows unencrypted and anonymous connections or authenticated with a username and password. If the server did not allow anonymous connections, the opcua_login script of msf-opcua could be used to try to find valid credentials, but this is not necessary in this example

It is therefore possible to anonymously access the server, browse its nodes and search for interesting nodes (the beginning of the command result has been deliberately cut off, and the « TemperatureControl » directory has been targeted with the -r option to reduce the number of nodes browsed):

$ python opcua_scan.py server_config -t hello_output.json -o config_output.json -nw -r ‘ns=3;s=85/0:Simulation’


Example of results obtained during a search for writeable nodes

Writeable nodes can then be further analysed in the output file that was configured in the previous command:


Extract of an output file generated by the server_config command

Here, it seems possible for an anonymous user to remotely turn on or off an air conditioners via the detected OPC UA server

 

Conclusion

Despite the security mechanisms provided by the OPC UA standard, misconfigurations can easily occur and can impact the availability of industrial assets. The tool developed by Wavestone and presented in this article facilitates the audit of these configurations to better assess the security of Industrial Control Systems.

Finally, the OPC UA specifications defines more security mechanisms, such as the management of certificates by a Global Discovery Server or the encryption of PubSub messages thanks to the implementation of a Security Key Server. The OPC UA standard could therefore enable further progress in terms of security, but few implementations of these mechanisms exist to this date.


The tool is available on Wavestone’s Github account: https://github.com/wavestone-cdt/opcua-scan

This tool was also used during a Arsenal lab session at BlackHat Asia 2023 in Singapore: https://github.com/wavestone-cdt/bhasia23-opcuhack

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top