This is Part 2 of Team82's OPC UA Deep Dive series, a comprehensive guide to the security of the OPC UA network protocol for unified OT communication. In this blog, we will explain OPC UA’s significance, how it’s used, and explore its data model.
OPC UA (Open Platform Communications Unified Architecture) is a protocol used in industrial automation and process control systems. It is a machine-to-machine communication protocol that allows various industrial devices to communicate with each other seamlessly. OPC UA provides a secure and reliable communication channel for exchanging data between devices, machines, and control systems, irrespective of the manufacturer or platform. It is designed to be platform-independent, which means that it can work on different operating systems and hardware platforms.
OPC UA uses a client-server architecture, where the clients are typically software applications—or edge devices—that request data from the servers, which are the devices or systems that provide the data. It supports various data types, including text, numbers, and binary data, and can transmit information over different transport layers, such as TCP/IP, HTTP, or MQTT. The protocol has become a widely adopted standard in the industrial automation industry due to its interoperability, reliability, and security features such as authorization, authentication and encryption. It is used in various applications, including manufacturing, energy, building automation, and transportation systems.
The protocol is primarily developed by the OPC Foundation organization, which determines its specifications. The OPC UA specification consists of all the different aspects of the protocol including: Concepts, Security Model, Address Space Model, Services, Information Model, Mappings, Profiles, Data Access, Alarms and Conditions, Programs, Historical Access, Discovery and Global Services, Aggregates, PubSub, Safety, State machines (not published yet), Alias names, User Authorization, Dictionary References, and more (see here for the full list of specification chapters). The specification determines everything in OPC UA from the basic building blocks of OPC UA which are the nodes, up to security levels in the protocol. We have read most of its specifications so you don’t have to; we’ll cover them here.
Let’s start with an example: Assume we have a simple automation process consisting of a water tank, fill valve, discharge valve, flow meter, and level meter. Our goal is to remotely monitor the entire process. To do so, we need to keep track of all our components including the actuators—valves, and sensors—level meter, and flow meter. For simplicity’s sake, let’s focus on the level meter which keeps track of how much water is in the tank.
The PLC which controls this system is programmed with a certain logic that opens the fill valve and closes it when the water reaches a certain level. The value of the level meter is read by the PLC and stored in a tag (variable) called
WATER_LEVEL. This value will be constantly updated as part of the PLC logic. Luckily for us, the PLC in our example is a Siemens S7-1500 which has built-in support for OPC UA, allowing us to query the PLC directly using OPC UA and constantly read the
WATER_LEVEL tag. Now we can update the value in our HMI or SCADA software to remotely monitor the process.
But what would have happened had the PLC not had built-in OPC UA support? In that case, we would need to use a gateway server, which from one side queries the PLC in its native protocol—in our example S7Comm+—and from the other side would expose an OPC UA server for us to query the desired tag. With a protocol gateway in place, we don’t have to worry about how we query PLCs in their native protocol because the gateway has driver support for the proprietary protocols. Instead, we can focus on the process logic itself and just query the data using OPC UA.
The goal of OPC UA is to exchange data between industrial processes to external resources and applications. To support this, the OPC Foundation defined different “node” classes or types. Each node class has a different role and goal in the OPC UA realm. For example, objects are nodes, the type of each object is a node, variables are nodes, methods are nodes, and even the relationships between nodes are nodes.
Nodes are hierarchically arranged within a namespace in the overall address space (Part 3: Address Space Model). Each node can be referenced with a unique identifier called NodeId (i) and a Namespace index (ns). While usually NodeId is numeric, it can also be of type String, GUID or even Byte String.
Namespace is a mechanism used to uniquely identify nodes in the address space of an OPC UA server. Namespaces have URIs and a numeric index: for example namespace index 0 and URI: http://opcfoundation.org/UA. This namespace is the default namespace for any OPC UA implementation. It was defined by OPC Foundation and consists of all the basic types including Boolean (ns=0, i=1), Int16 (ns=0, i=4), Float (ns=0, i=10), String (ns=0, i=12), etc.
All the nodes are first defined in a huge XML document called NodeSet. For example, you can view the NodeSet for the default namespace 0 of OPCFoundation with all the basic building blocks.
A type of node that represents an entity or concept within the address space. It can contain variables, methods, and other objects, and is used to organize and represent the structure and behavior of the underlying system being modeled.
A type of node that represents a data value within the address space. It can be read or written to by clients, and can have properties such as DataType (DataType is a node class too), access level, and historical data. Variables are used to represent real-time data, configuration settings, and other types of information within an OPC UA system.
A type of node that represents a function or operation that can be invoked by clients. It can have input and output arguments, and may perform actions or return values based on those arguments. Methods are used to represent operations such as starting or stopping a process, performing a calculation, or executing a command on a device within an OPC UA system.
A type of node that represents a relationship between nodes in the address space. It defines the type of reference and the rules governing that relationship, such as whether it is hierarchical or non-hierarchical, and whether it is one-way or bidirectional. Reference types are used to model the structure and relationships between nodes within an OPC UA system.
All nodes are arranged in a node tree and exposed by the OPC UA server. The node tree of an OPC UA server is used to expose its objects, methods, and variables, and defines the relationships between them, enabling clients to browse the address space in a structured and meaningful way. Each node can be referenced using NodeId (i) and a NameSpace (ns).
When a client connects to an OPC UA server, it can use the browse functionality to explore the server's address space, which represents the hierarchy of the objects and data points available for access. The browse function provides a structured view of the server's address space and allows the client to navigate through the objects and their attributes, such as data type, access level, and description.
The OPC UA browse functionality enables clients to discover the available data sources and objects exposed by an OPC UA server and to access them in a standardized and efficient manner, regardless of the underlying hardware and software platform. This makes it easier for developers to create interoperable industrial automation systems that can communicate with each other using the OPC UA protocol.
Now that we are familiar with the basic modeling of OPC UA we can go back to our example and understand how each part of the automation process, below, could be addressed using OPC UA.
In our simple example, we are constantly reading the Flow Meter value which is represented in the PLC as the
WATER_LEVEL tag. The PLC’s OPC UA server exposes the Flow Meter value as a node of class type Variable which has a value with DataType Float.
The OPC UA data model describes the concepts of objects and types, but it’s not enough because they need to be represented somehow in memory and on the network when sending OPC UA packets. Therefore, OPC Foundation defined an encoding system which helps to encode and decode OPC UA nodes to/from bytes.
OPC UA defines a set of built-in types, either basic types like Int32 or complex object-like types like NodeID type. (Learn the specifics of how to encode each built-in type). The complex types are built like objects, constructed from multiple basic types and other complex types behind the scenes. One particularly interesting type is the Variant. Variant is a special type that can be any other type.
Everything in OPC UA is eventually constructed out of the built-in types. For example: an ExtensionObject is encoded as a sequence of bytes prefixed by the
NodeId of its
DataEncoding used and the number of bytes encoded.
And here is an example to a binary representation of a NodeID struct:
The detailed definition of OPC UA encoding enables us to use very detailed and complex data structures without the need to understand how everything works behind the scenes. Users can focus on the purpose of the data instead of how it’s constructed or transferred.
The recognition of OPC UA as a standardized OT communication protocol alleviates interoperability incompatibilities among devices developed by different platform vendors and manufacturers. Users have a secure, reliable channel for data exchanges between devices, machines, and control systems that is platform independent and includes important security features including authentication and encryption for data protection.
In future installments of Team82’s OPC UA Deep Dive series, we’ll look at those security capabilities in depth, examine the OPC UA attack surface, disclose some information about vulnerabilities and new attack techniques Team82 has uncovered, and introduce an exploit framework and network fuzzers used throughout the entirety of our OPC UA research.
CWE-749 Exposed Dangerous Method or Function
When user authentication is not enabled the shell can execute commands with the highest privileges. Red Lion SixTRAK and VersaTRAK Series RTUs with authenticated users enabled (UDR-A) any Sixnet UDR message will meet an authentication challenge over UDP/IP. When the same message comes over TCP/IP the RTU will simply accept the message with no authentication challenge.
CVSS V3: 10
CWE-288: Authentication Bypass Using an Alternative Path or Channel
Red Lion SixTRAK and VersaTRAK Series RTUs with authenticated users enabled (UDR-A) any Sixnet UDR message will meet an authentication challenge over UDP/IP. When the same message is received over TCP/IP the RTU will simply accept the message with no authentication challenge.
CVSS V3: 10
The vulnerability is caused by the using deprecated deserialization functions and/or classes such as BinaryFormatter in the zenon internal graphic utility DLLs.
CVSS V3: 6.3
The vulnerability is caused by the default directory permissions for the Zenon Projects directory in the engineering studio default workspace. By allowing access to all the users on the system, the attacker may alter the zenon project itself to load arbitrary zenon projects in the zenon runtime.
CVSS V3: 5.9
Code Execution through overwriting service executable in utilities directory. The vulnerability is caused by the weakly configured default directory permission for the ABB Utilities directory.
CVSS V3: 7.0