Talos Vulnerability Report

TALOS-2024-1940

AutomationDirect P3-550E Programming Software Connection Remote Memory Diagnostics Write-What-Where vulnerability

May 28, 2024
CVE Number

CVE-2024-22187

SUMMARY

A write-what-where vulnerability exists in the Programming Software Connection Remote Memory Diagnostics functionality of AutomationDirect P3-550E 1.2.10.9. A specially crafted network packet can lead to an arbitrary write. An attacker can send an unauthenticated packet to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

AutomationDirect P3-550E 1.2.10.9

PRODUCT URLS

P3-550E - https://www.automationdirect.com/adc/shopping/catalog/programmable_controllers/productivity3000plcs(modular)/cpus/p3-550e

CVSSv3 SCORE

9.1 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H

CWE

CWE-284 - Improper Access Control

DETAILS

The P3-550E is the most recent CPU module released in the Productivity3000 line of Programmable Automation Controllers from AutomationDirect. It is an affordable control CPU which communicates remotely via ethernet, serial, and USB and exposes a variety of control services, including MQTT, Modbus, ENIP and the engineering workstation protocol DirectNET.

The P3-550E exposes a “Programming Software Connection” service over UDP port 9999 that is used by the engineering workstation software to program and otherwise configure the device. The protocol is not well documented and what information we do have is pieced together from reverse engineering efforts.

Each message of the protocol is prefixed with a 12-byte header containing the requesting client’s IP address and originating port, two 16-bit fields only referred to as ‘GBS’ and ‘IMM’, and is followed by a payload which varies by type of request.

 0                               1
 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|              GBS              |          Client Port          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           Client IP                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|              IMM              |         Undetermined          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The first byte of the IMM field acts as a function code, dictating which high-level feature set is being requested, and the second byte dictates the exact functionality expected. The protocol format for the remainder of the message is dependent on the IMM value.

This vulnerability arises when the first byte of IMM is 0x01, which appears to host a subset of diagnostic features. The function responsible for implementing this feature set is located at offset 0x759d0 and we refer to it as _DISCOVERY_CALLBACK_1. Within this function, the second byte of IMM is used in a switch-case to identify the exact feature being requested. For this report, we are most interested in the implementation of the handler associated with IMM[1] = 0xA2. This function appears to enable arbitrary remote memory write capabilities, but we did not find any features within the engineering workstation software that interacted with it. From reverse engineering, we can identify that this handler expects the following payload format.

 0                               1
 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             Base Addr                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Chunk Size            |         Chunk Count         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|              UNK                |            Data             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                             |
|                                ...                            | 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The handler for IMM = 0x01A2, located at offset 0x7b2d8, implements a remote memory write, where arbitrary data consisting of chunk_counts of chunk_size (limited to 1, 2, and 4) are written to memory, beginning from base_address. A partial decompilation of this function is included below for reference.

int32_t sub_7b2d8(struct packet* pkt)
{
    int result;
    if (sub_7afa4(0xA2, pkt->field_14) != 0) {
      result = 0;
    }
    else {
      uint32_t chunk_count = pkt->chunk_count;
      uint32_t chunk_size = pkt->chunk_size;
      if (chunk_count == 0) {
          result = 0;
      } else {
          if (chunk_size == 1) {
              // [1] Read the destination pointer from the packet
              char* dest = pkt->address;
              char* data = &pkt->data;
              if (chunk_count > 0x400) {
                  result = 0;
              } else {
                  int32_t bytes_rxd = 0;
                  if (chunk_count != 0 && chunk_count > 8) {
                      uint32_t i = ((chunk_count - 1) / 8);
                      if ((chunk_count - 8) > 0) {
                          do {
                              // [2] Begin writing the attacker-provided data into memory at the attacker-provided address
                              bytes_rxd += 8;
                              address[0] = data[0];
                              address[1] = data[1];
                              address[2] = data[2];
                              address[3] = data[3];
                              address[4] = data[4];
                              address[5] = data[5];
                              address[6] = data[6];
                              address[7] = data[7];
                              address = &address[8];
                              data = &data[8];
                              i--;
                          } while (i != 0);
                      }
                      ...
                // The remainder of this function is effectively just the various unrolled loops associated
                // with the varying selections of `chunk_size` and `chunk_count`, showing the same capability
                ... 

In the above decompilation, we confirm that this endpoint allows an unauthenticated remote endpoint to write arbitrary data to arbitrary memory addresses on the device. An attacker who submits a series of properly formatted requests can use this feature to remotely modify arbitrary memory regions on the device, potentially resulting in arbitrary remote code execution.

VENDOR RESPONSE

A CISA advisory can be found here: https://www.cisa.gov/news-events/ics-advisories/icsa-24-144-01

TIMELINE

2024-02-14 - Initial Vendor Contact
2024-02-15 - Vendor Disclosure
2024-05-23 - Vendor Patch Release
2024-05-28 - Public Release

Credit

Discovered by Matt Wiseman of Cisco Talos.