Team82 has researched Honeywell ControlEdge Virtual Unit Operations Center (UOC) and found multiple vulnerabilities in the EpicMo protocol implementation within ControlEdge Virtual UOC instances. These vulnerabilities are exploitable and can lead to unauthenticated remote code execution.
The vulnerabilities we found reside in the EpicMo protocol (TCP port 55565)—a proprietary protocol designed by Honeywell that is used to communicate between Honeywell Experion servers and controllers. This protocol contained an undocumented and dangerous function that enabled us to write files on Virtual UOC controllers without sanitation, which exposed the devices to the execution of unauthorized code. An attacker already on an OT network would use a malicious network packet to exploit this vulnerability and compromise the virtual controller. This attack could be carried out remotely in order to modify files, resulting in full control of the controller and the execution of malicious code.
Honeywell has updated Virtual UOC and users are urged to move to current versions. The Cybersecurity Infrastructure & Security Agency (CISA) has published an advisory for CVE-2023-5389 (CVSS v3 score: 9.1) and CVE-2023-5390 (5.3). Honeywell has also published an advisory.
Honeywell is a significant player in the industrial control system (ICS) market, offering an extensive array of controllers for industrial automation. One product is the Honeywell Control Edge Unit Operations Controller (UOC), a modular physical controller that extends the Experion control DCS environment. The ControlEdge UOC includes built-in fault tolerant Ethernet, ModbusTCP, and EtherNet/IP. UOC is also offered as a virtual controller. The Virtual UOC decreases an enterprise’s hardware footprint because it does not require a physical controller. This is essentially a Linux-based virtual machine that can be installed in a virtual environment.
Honeywell controllers use multiple protocols for communications. A service called NameServer
is responsible for routing and opening communications for all protocols. In order to start communication over a specific protocol, we first need to send a UDP message to the NameServer
service (over UDP port 12321) specifying which protocol that will be used.
After sending the UDP initialization message, one can start communicating over TCP. Each session starts with a TCP initialization message specifying the protocol again.
The EpicMo protocol (TCP port 55565) is used for debugging and diagnostics of Honeywell controllers. It includes function codes such as ReadMemory
, WriteMemory
, Reboot
and ReadCrashBlock
, which allow for error detection and debugging.
Sequence Number | Packet Length | Number of Packets | Number of Packets Sent | Function Code |
2 bytes | 2 bytes | 2 bytes | 2 bytes | 1 byte |
Sequence number: A sequential number for the packet.
Packet Length: Length of the packet.
Number Packets Received: Counter for the number of packets received.
Number Packet Sent: Counter for the number of packets sent.
Function Code: The function code the client desires to invoke.
While researching the EpicMo protocol we discovered that the Virtual UOC implements different EpicMo function codes than the C300 controller.
Two commands that caught our attention are LoadFileFromModule
and LoadFileToModule
. After researching these functions, we came to the conclusion that they enable arbitrary file-write and file-read to the virtual controller.
LoadFileToModule
allows users to write files to the controller. One of the parameters this function receives is a Destination_Path
, which is the path the given file will be written to.
We discovered that this functionality allows users to supply an arbitrary path and data, and no validation or limitation exists on the given path. This means users can write files to all writeable locations on the controller, which is a security concern. By leveraging this functionality, attackers could achieve remote code execution on the controller, by writing executable files, for example.
In order to upload a file using the LoadFileToModule
function code, we need to send at least three packets (start write, write data, finish write). Each packet starts with the regular EpicMo header.
LoadFileToModule
start write command
File Type | Upload Packet Number | Unknown | File Data Checksum | File Data Length | Destination Path |
1 Byte | 4 Bytes | 2 Bytes | 4 Bytes | 4 Bytes | String |
File Type: Firmware file or generic file. 0x05 is used for generic file
Upload Packet Number: Packet number in the upload sequence. The first packet is 0.
File Data Checksum: Checksum for the entire file data
File Data Length: Length of the entire data
Destination Path: The name of the file to save on the controller
LoadFileToModule
data command
The maximum length of the file data in a data command is 0x7F. If the data length is more than 0x7F it will be split into multiple packets where Upload Packet Number is updated accordingly.
File Type | Upload Packet Number | Data Length (0x7f max) | Data |
1 Byte | 4 Bytes | 2 Bytes | n-Bytes |
LoadFileToModule
final command
Final command is used to signal that the upload is finished.
File Type | Upload Packet Number | Data Length: Must Be 0 |
1 Byte | 4 Bytes | 2 Bytes |
When the Upload Packet Number is not 0 and the Data length is 0, it is considered as the final upload packet.
In order to send a valid LoadFileToModule
start command, we need to include a checksum of the file. In order to calculate a file’s checksum, we implemented the following function that returns the correct checksum for a given file, below.
In order to demonstrate how an attacker might leverage this vulnerability to achieve remote code execution, we searched for writeable locations on the virtual controller. However, a few limitations existed:
Files uploaded using LoadFileToModule
do not have the UNIX execute attribute
All files that are mounted to /usr/honeywell
are mounted as read-only and this directory is not writeable.
We eventually decided to overwrite a system .so file; .so files do not need to have the execute attribute, and we can create a shared object that will execute our code when it is loaded. We chose /lib/libcap.so.2
as the shared object we overwrite since it is loaded at startup, but overwriting it does not affect the runtime in any major way and enables stable pre-auth RCE.
Our code execution flow looks like this:
Compile libcap.so.2
with our payload set to run at startup
Use the LoadFilefromModule
to write file to /lib/libpcap.so.2
on the controller
Call the Reboot command in order to ensure a reload of libcap.so.2
Finally, we were able to demonstrate a preauth RCE on a remote virtual UOC using CVE-2023-5389.
Proprietary protocols such as Honeywell’s EpicMo used for communication between Honeywell Experion servers and controllers often contain vulnerabilities that can put industrial processes at risk for manipulation or disruption.
We found mechanisms with a function that enables possible remote code execution by writing files on Virtual UOC controllers without sanitization.
We found an undocumented and dangerous function that enabled us to write files on Virtual UOC controllers without sanitation. An attacker on the OT network could send malicious packets to the controller and write files without authenticating to the controller.
Team82 privately disclosed these vulnerabilities, CVE-2023-5389 and CVE-2023-5390, to Honeywell, which has addressed them in an update.
CWE-749: Exposed Dangerous Method or Function
Successful exploitation of this vulnerability could allow an attacker to modify files on Experion controllers or SMSC S300. This exploit could be used to write a file that may result in unexpected behavior based on configuration changes or updating of files that could result in subsequent execution of a malicious application if triggered.
CVSS v3: 9.1
CWE-36: Absolute Path Traversal
Successful exploitation of this vulnerability could allow an attacker to read from the Experion controllers or SMSC S300. This exploit could be used to read files from the controller that may expose limited information from the device.
CVSS v3: 5.3