Technical findings overview
The discovered vulnerability (CVE-2021-22779) is a authentication bypass vulnerability that can be chained with additional vulnerabilities in the UMAS protocol that were discovered in the past but only partly mitigated. Armis researchers discovered (as we’ll detail below) that while these additional vulnerabilities (CVE-2018-7852, CVE-2019-6829) were categorized as denial-of-service, they can actually lead to native remote-code-execution. These vulnerabilities are essentially undocumented commands in the UMAS protocol, and Armis researchers discovered that instead of removing these commands from the protocol (maybe due to legacy dependencies), SE added an authentication mechanism around them to mitigate their risk. Unfortunately, this mechanism was found to be faulty, and thus in light of this latest discovery these commands will require a more fundamental fix to be fully mitigated.
Due to the above, the following CVEs still impact latest firmware versions of Modicon M340 and M580 PLCs:
- CVE-2021-22779 – Authentication bypass vulnerability via UMAS command MemoryBlockRead
- CVE-2018-7852 – Untrusted pointer dereference via UMAS command PrivateMessage (RCE)
- CVE-2019-6829 – Arbitrary memory write via UMAS command WritePhysicalAddress (RCE)
- CVE-2020-7537 – Arbitrary memory read via UMAS command ReadPhysicalAddress (Information leak)
Additional Modicon models are currently considered not vulnerable, while Armis researchers continue analyzing their full impact.
To fully understand the technical details of the findings described above, some background is required on the Modbus and UMAS protocols.
Modbus is the de facto standard for controlling PLCs in SCADA systems. It was first published by Modicon (now Schneider Electric) in 1979. Modbus was designed a long time ago and is missing features required by modern systems, such as transfer of binary objects to and from the PLC.
Modbus can operate over serial communication or IP communication. The widely used IP version of Modbus is the Modbus/TCP standard.
Modicon chose to extend the Modbus implementation under a reserved Modbus function code. The extended protocol is called UMAS, and it adds authentication, binary data transfer, firmware updates, and additional features to the basic Modbus protocol.
Modicon PLCs (M340, M580, and others) implement UMAS. UMAS re-implements standard Modbus commands and some necessary Modbus functions that are missing.
For example, one of the proprietary UMAS commands is the MemoryBlockWrite command (function code 0x21), which does not require authentication.
The command writes a chunk of binary data to an offset within a certain block ID. The blocks are located in fixed memory addresses, and are marked as writable or read-only blocks. When attempting to write on a read-only block, the MemoryBlockWrite command returns an error response.
UMAS Reservation Mechanisms
Certain changes to a PLC require multiple commands that depend on one another. To allow for such scenarios, Modicon implemented a reservation mechanism. The reservation mechanism was created to synchronize modifications of the PLC’s program – a form of a global locking mechanism over certain critical changes. Once an engineering workstation successfully reserves a PLC over UMAS, it receives a one-byte token that is used to perform modifications to the PLC. This token allows the workstation to change any aspect of the application running on the PLC. UMAS commands that don’t modify the PLC do not require this token and can take place without any authentication by the workstation. Since only one workstation can reserve the PLC at a time, this mechanism protects the PLC from overlapping modifications that can damage the PLC, the equipment it controls and the normal operation of the factory.
The initial version of the reservation mechanism (we’ll name this basic reservation) worked by utilizing a UMAS command with function code 0x10. This command did not require authentication, or challenge-response handshake, and relied on a hardcoded shared secret between the PLC and SE’s managing software on the engineering workstation:
The hardcoded secret used by this mechanism can be observed in the unencrypted UMAS traffic, or located by reverse engineering a Modicon PLC firmware.
Over time, security concerns were raised, and various undocumented UMAS commands were shown to allow remote-code-execution, or other malicious intent. SE decided to enhance the reservation mechanism, so it will not only act as a locking mechanism, but as an authentication mechanism as well.
The Enhanced Reservation mechanism is based on a challenge-response handshake in which a shared password is authenticated. The shared password, called the application password, is dynamically set, when a project file is being uploaded to the PLC. The Enhanced Reservation mechanism utilizes the UMAS command with the function code 0x6E:
In this command the workstation and the PLC are exchanging a randomly generated buffer of 0x20 bytes:
A hash of these buffers, combined with the application password’s hash is used to complete the reservation.
Decompiled code snippet from the umas_EnahcnedResvMngt command handler from a Modicon firmware
As noted above, the secret shared key used in this mechanism is the hash of the password configured in the EcoStruxure software running on the Engineering Workstation. The EcoStruxure software encourages the user to configure a password upon creation of a new project. When a project file is transferred to the PLC the new application password is also configured on the PLC.
In both versions of the reservation mechanism, a one byte token is sent in response upon a successful reservation, and this token will then be prepended to UMAS commands that require authentication.
Bypassing Authentication – Take 1 (CVE-2020-7537)
Having reversed engineered the algorithm of the enhanced reservation mechanism, we wanted to see if we can leak the application password (or it’s hash) via an undocumented UMAS command. Static analysis of the latest firmware (at the time) of the M340 PLC, revealed a suspicious UMAS command:
The pu_ReadPhysicalAddress copies a memory chunk from a chosen address in the input command, to a response buffer. Other than simple validation of buffer sizes, the function poses no limitation on the address being read from the memory. Essentially this undocumented commands allows leaking all memory in the address space of the PLC.
Decompiled snippet from the pu_ReadPhysicalAddress command
This command can be used to leak the hash of the application password, that is stored in the memory of the PLC, and used to reserve and manage a PLC by an unauthenticated attacker.
In addition, a memory read command such as this can be used to leak sensitive information from the PLC that might relate to its operation, or even used as a denial of service primitive (!). Since there are no limitations on the address being read by this command, an attacker can abuse this command to crash the device by reading from certain hardware-specific addresses that will cause drivers on the PLC to be out-of-sync with the hardware. This can lead to various edge cases that can result in denial of service. When a PLC crashes in such a way it will not return quickly to normal operation – a physical button needs to be pushed by an operator to restart the device.
Wireshark capture of an application password hash being leaked via the ReadPhysicalAddress UMAS command
This vulnerability was reported to SE in November 2020, and was disclosed in a security advisory December 2020. The patch introduced by SE to resolve this issue defined the ReadPhysicalAddress command as one that requires reservation – leveraging the mechanism to fend off this attack. While this did mitigate this authentication bypass, it did not fully resolve the underline risks in this command – since it might still be triggered if the project file in use is password-less.
Bypassing Authentication – Take 2 (CVE-2021-22779)
To better understand the UMAS messages flow of the Enhanced Reservation mechanism we connected to the PLC with the EcoStruxure software and analyzed the traffic it created. We noticed something interesting: when the correct password was typed into the EcoStruxure software some UMAS commands were generated as expected, but when an incorrect password was typed the software rejected the password without generating any traffic with the PLC.
This raised the question – how does the EcoStruxure software know the password is incorrect without communicating with the PLC? To answer this question we analyzed the UMAS commands sent by the workstation before the password is entered. One of the commands used by the workstation was the MemoryBlockRead command – that allows reading preconfigured blocks from memory, without authentication (similar to the MemoryBlockWrite command). Unlike the ReadPhysicalAddress command, the block IDs limit the access of the memory to certain fixed memory addresses.
The structure of UMAS command used to read predefined memory blocks from the PLC
Nevertheless, it appears the software uses this command, pre-reservation, to read the hash of the password from the PLC, and validate if the password entered by the user is correct. Needless to say, this mechanism is fundamentally flawed – the password hash is both passed over an unencrypted protocol, and can also be read by any unauthenticated attacker that simply implements the memory block read command.
While the EcoStruxure software uses the memory read command to validate the password hash – an unauthenticated attacker can simply use the read password hash to bypass the authentication mechanism of the Enhanced Reservation altogether.
Simple script that implements this authentication bypass technique (certain details have been censored)
From Authentication Bypass to RCE
As described above, two undocumented UMAS commands where found in the past to lead to denial-of-service – the UMAS command WritePhysicalAddress (function code 0x29, CVE-2019-6829), and PrivateMessage (function code 0x6D, CVE-2018-7852). As we’ll show below, the two can actually lead to remote-code-execution.
Arbitrary Memory Write
The command WritePhysicalAddress is the write equivalent for the ReadPhysicalAddress command described above. This command allows arbitrary write to any address in the PLC memory, with a data buffer supplied in the input buffer. While in the latest firmware version this command is inaccessible when an application password is in use, it can still be triggered if a downgrade attack is performed after the reservation mechanism has been bypassed (more on that in a bit) – or if an application password is never set in the first place.
The WritePhysicalAddress command handler (the test variable represents the current reservation mode)
While it is clear why this command can lead to denial-of-service (by altering memory with arbitrary data), it is unclear why this vulnerability was not categorized as remote-code-execution. Altering memory in an attacker-controlled address with an attacker-controlled payload is probably the easiest exploitation path there is to RCE. Numerous function pointers, stack variables, or C++ vtable pointers reside in memory that can be altered, and shift the execution state from its original state. Using these to then reach attacker-controlled code can be done using ROP techniques. A similar exploit was developed by Armis researchers when an exploit utilizing the URGENT/11 was demonstrated to impact Modicon PLCs in a whitepaper (page 20) from December 2020.
Remote Procedure Call (RPC, a.k.a PrivateMessage)
The second undocumented command – PrivateMessage is a command that triggers internal functions in the PLC, by accessing C++ objects pointed by a pointer provided in the input buffer, and than triggering a callback function from these parsed objects. It is unclear what is the commercial use for this specific command, but it is clear it can lead to denial-of-service when the passed pointer does not point to a valid C++ object in the PLCs memory.
Turning this vulnerability to an RCE is only a bit more complex. A valid structure of a C++ object can be uploaded to the PLC memory via the MemoryBlockWrite command to a known address in memory. Then the PrivateMessage command can be sent – triggering a call to a function within the vtable of the C++ object controlled by the attacker. Similar to the steps detailed above, reaching attacker-controlled code by controlling the PC of the program is only a matter of connecting the dots via several ROP gadgets.
Schneider Electric’s fix for both undocumented commands shown above was similar – disabling them completely when an application password is in use. However, when it isn’t in use, these commands remain accessible, perhaps to retain some legacy compatibility with tools that use these commands.
However, by exploiting the authentication bypass vulnerability (such as CVE-2021-22779) an attacker can downgrade the PLC’s security by uploading a new project file with no password configured. Once this downgrade attack is done, the undocumented commands detailed above can nevertheless be used by attackers to gain native code execution.
The attack steps are as follow:
- Bypass the authentication with CVE-2021-22779 and reserve the PLC
- Upload a new project file with no application password configured
- Release the PLC reservation and disconnect from the device
- Reconnect to the PLC with the basic reservation method, no password is needed
Reach code execution by exploiting one of the undocumented commands that can reach RCE (WritePhysicalAddress, or PrivateMessage)