Skip to content

Commit

Permalink
Removes session logic from mixins and uses client instead of datastor…
Browse files Browse the repository at this point in the history
…e for rhost and rport
  • Loading branch information
cgranleese-r7 committed Feb 6, 2024
1 parent 577304c commit 98f961e
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 64 deletions.
32 changes: 12 additions & 20 deletions lib/msf/core/exploit/remote/mysql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ module Exploit::Remote::MYSQL

include Exploit::Remote::Tcp

# @!attribute [rw] mysql_conn
# @return [::Mysql]
attr_accessor :mysql_conn

def initialize(info = {})
super

Expand All @@ -32,18 +36,12 @@ def initialize(info = {})
)
end

def mysql_login(user='root', pass='', db=nil)
unless defined?(session).nil? || session.nil?
print_status("Using existing session #{session.sid}")
@mysql_handle = session.client
return true
end

disconnect if self.sock
def mysql_login(user='root', pass='', db=nil)
disconnect if sock
connect

begin
@mysql_handle = ::Mysql.connect(rhost, user, pass, db, rport, io: sock)
self.mysql_conn = ::Mysql.connect(rhost, user, pass, db, rport, io: sock)

rescue Errno::ECONNREFUSED
print_error("Connection refused")
Expand All @@ -62,20 +60,14 @@ def mysql_login(user='root', pass='', db=nil)
return false
end

vprint_good "#{rhost}:#{rport} MySQL - Logged in to '#{db}' with '#{user}':'#{pass}'"
vprint_good "#{mysql_conn.host}:#{mysql_conn.port} MySQL - Logged in to '#{db}' with '#{user}':'#{pass}'"

return true
end

def mysql_logoff
# Don't log out if we are using a session.
if defined?(session) && session
vprint_status "#{rhost}:#{rport} MySQL - Skipping disconnecting from the session"
return
end

@mysql_handle = nil if @mysql_handle
disconnect if self.sock
self.mysql_conn = nil if mysql_conn
disconnect if sock
vprint_status "#{rhost}:#{rport} MySQL - Disconnected"
end

Expand All @@ -92,7 +84,7 @@ def mysql_login_datastore

def mysql_query(sql)
begin
res = @mysql_handle.query(sql)
res = mysql_conn.query(sql)
rescue ::Mysql::Error => e
print_error("MySQL Error: #{e.class} #{e.to_s}")
return nil
Expand All @@ -101,7 +93,7 @@ def mysql_query(sql)
return nil
end

vprint_status "#{rhost}:#{rport} MySQL - querying with '#{sql}'"
vprint_status "#{mysql_conn.host}:#{mysql_conn.port} MySQL - querying with '#{sql}'"
res
end

Expand Down
13 changes: 10 additions & 3 deletions modules/auxiliary/admin/mysql/mysql_enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,14 @@ def report_cred(opts)
end

def run
return if not mysql_login_datastore
# If we have a session make use it
if session
print_status("Using existing session #{session.sid}")
self.mysql_conn = session.client
else
# otherwise fallback to attempting to login
return unless mysql_login_datastore
end

print_status("Running MySQL Enumerator...")
print_status("Enumerating Parameters")
Expand Down Expand Up @@ -114,8 +121,8 @@ def run
res.each do |row|
print_good("\t\tUser: #{row[0]} Host: #{row[1]} Password Hash: #{row[2]}")
report_cred(
ip: rhost,
port: rport,
ip: mysql_conn.host,
port: mysql_conn.port,
user: row[0],
password: row[2],
service_name: 'mysql',
Expand Down
9 changes: 8 additions & 1 deletion modules/auxiliary/admin/mysql/mysql_sql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,14 @@ def cmd_select(*args)
end

def run
return unless mysql_login_datastore
# If we have a session make use it
if session
print_status("Using existing session #{session.sid}")
self.mysql_conn = session.client
else
# otherwise fallback to attempting to login
return unless mysql_login_datastore
end

print_status("Sending statement: '#{datastore['SQL']}'...")
res = mysql_query(datastore['SQL']) || []
Expand Down
4 changes: 2 additions & 2 deletions modules/auxiliary/scanner/mysql/mysql_authbypass_hashdump.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def run_host(ip)

# Short circuit if we already won
if results.length > 0
@mysql_handle = results.first
self.mysql_conn = results.first
return dump_hashes
end

Expand Down Expand Up @@ -157,7 +157,7 @@ def run_host(ip)

if results.length > 0
print_good("#{rhost}:#{rport} Successfully exploited the authentication bypass flaw, dumping hashes...")
@mysql_handle = results.first
self.mysql_conn = results.first
return dump_hashes
end

Expand Down
23 changes: 15 additions & 8 deletions modules/auxiliary/scanner/mysql/mysql_file_enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,21 @@ def initialize
# This function does not handle any errors, if you use this
# make sure you handle the errors yourself
def mysql_query_no_handle(sql)
res = @mysql_handle.query(sql)
res = self.mysql_conn.query(sql)
res
end

def run_host(ip)
vprint_status("Login...") unless session

return unless mysql_login_datastore
# If we have a session make use it
if session
print_status("Using existing session #{session.sid}")
self.mysql_conn = session.client
else
# otherwise fallback to attempting to login
return unless mysql_login_datastore
end

begin
mysql_query_no_handle("USE " + datastore['DATABASE_NAME'])
Expand Down Expand Up @@ -84,20 +91,20 @@ def check_dir dir
rescue ::Mysql::TextfileNotReadable
print_good("#{dir} is a directory and exists")
report_note(
:host => rhost,
:host => mysql_conn.host,
:type => "filesystem.dir",
:data => "#{dir} is a directory and exists",
:port => rport,
:port => mysql_conn.port,
:proto => 'tcp',
:update => :unique_data
)
rescue ::Mysql::DataTooLong, ::Mysql::TruncatedWrongValueForField
print_good("#{dir} is a file and exists")
report_note(
:host => rhost,
:host => mysql_conn.host,
:type => "filesystem.file",
:data => "#{dir} is a file and exists",
:port => rport,
:port => mysql_conn.port,
:proto => 'tcp',
:update => :unique_data
)
Expand All @@ -112,10 +119,10 @@ def check_dir dir
else
print_good("#{dir} is a file and exists")
report_note(
:host => rhost,
:host => mysql_conn.host,
:type => "filesystem.file",
:data => "#{dir} is a file and exists",
:port => rport,
:port => mysql_conn.port,
:proto => 'tcp',
:update => :unique_data
)
Expand Down
17 changes: 12 additions & 5 deletions modules/auxiliary/scanner/mysql/mysql_hashdump.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,18 @@ def initialize
end

def run_host(ip)
return unless mysql_login_datastore
# If we have a session make use it
if session
print_status("Using existing session #{session.sid}")
self.mysql_conn = session.client
else
# otherwise fallback to attempting to login
return unless mysql_login_datastore
end

service_data = {
address: rhost,
port: rport,
address: ip,
port: mysql_conn.port,
service_name: 'mysql',
protocol: 'tcp',
workspace_id: myworkspace_id
Expand Down Expand Up @@ -75,8 +82,8 @@ def run_host(ip)
end

service_data = {
address: ::Rex::Socket.getaddress(rhost, true),
port: rport,
address: ::Rex::Socket.getaddress(mysql_conn.host, true),
port: mysql_conn.port,
service_name: 'mysql',
protocol: 'tcp',
workspace_id: myworkspace_id
Expand Down
21 changes: 14 additions & 7 deletions modules/auxiliary/scanner/mysql/mysql_schemadump.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,35 @@ def initialize
end

def run_host(ip)
return unless mysql_login_datastore
# If we have a session make use it
if session
print_status("Using existing session #{session.sid}")
self.mysql_conn = session.client
else
# otherwise fallback to attempting to login
return unless mysql_login_datastore
end

mysql_schema = get_schema
mysql_schema.each do |db|
report_note(
:host => rhost,
:host => mysql_conn.host,
:type => "mysql.db.schema",
:data => db,
:port => rport,
:port => mysql_conn.port,
:proto => 'tcp',
:update => :unique_data
)
end
output = "MySQL Server Schema \n Host: #{datastore['RHOST']} \n Port: #{datastore['RPORT']} \n ====================\n\n"
output = "MySQL Server Schema \n Host: #{mysql_conn.host} \n Port: #{mysql_conn.port} \n ====================\n\n"
output << YAML.dump(mysql_schema)
this_service = report_service(
:host => datastore['RHOST'],
:port => datastore['RPORT'],
:host => mysql_conn.host,
:port => mysql_conn.port,
:name => 'mysql',
:proto => 'tcp'
)
p = store_loot('mysql_schema', "text/plain", datastore['RHOST'], output, "#{datastore['RHOST']}_mysql_schema.txt", "MySQL Schema", this_service)
p = store_loot('mysql_schema', "text/plain", mysql_conn.host, output, "#{mysql_conn.host}_mysql_schema.txt", "MySQL Schema", this_service)
print_good("Schema stored in: #{p}")
print_good output if datastore['DISPLAY_RESULTS']
end
Expand Down
9 changes: 5 additions & 4 deletions modules/auxiliary/scanner/mysql/mysql_version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ def initialize
def run_host(ip)
begin
if session
version = session.client.server_info
print_good("#{rhost}:#{rport} is running MySQL #{version}")
sql_conn = session.client
version = sql_conn.server_info
print_good("#{sql_conn.host}:#{sql_conn.port} is running MySQL #{version}")
report_service(
:host => rhost,
:port => rport,
:host => sql_conn.host,
:port => sql_conn.port,
:name => "mysql",
:info => version
)
Expand Down
18 changes: 11 additions & 7 deletions modules/auxiliary/scanner/mysql/mysql_writable_dirs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,21 @@ def initialize
# This function does not handle any errors, if you use this
# make sure you handle the errors yourself
def mysql_query_no_handle(sql)
res = @mysql_handle.query(sql)
res = self.mysql_conn.query(sql)
res
end

def run_host(ip)
print_warning("For every writable directory found, a file called #{datastore['FILE_NAME']} with the text test will be written to the directory.")
print_status("Login...")
print_status("Login...") unless session

unless mysql_login_datastore
print_error('Unable to login to the server.')
return
# If we have a session make use it
if session
print_status("Using existing session #{session.sid}")
self.mysql_conn = session.client
else
# otherwise fallback to attempting to login
return unless mysql_login_datastore
end

File.read(datastore['DIR_LIST']).each_line do |dir|
Expand All @@ -66,10 +70,10 @@ def check_dir(dir)
else
print_good("#{dir} is writeable")
report_note(
:host => rhost,
:host => mysql_conn.host,
:type => "filesystem.file",
:data => "#{dir} is writeable",
:port => rport,
:port => mysql_conn.port,
:proto => 'tcp',
:update => :unique_data
)
Expand Down
12 changes: 10 additions & 2 deletions modules/exploits/multi/mysql/mysql_udf_payload.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,16 @@ def password
end

def login_and_get_sys_exec
m = mysql_login(username,password,'mysql')
return if not m
# If we have a session make use it
if session
print_status("Using existing session #{session.sid}")
self.mysql_conn = session.client
else
# otherwise fallback to attempting to login
m = mysql_login(username,password,'mysql')
return unless m
end

@mysql_arch = mysql_get_arch
@mysql_sys_exec_available = mysql_check_for_sys_exec()
if !@mysql_sys_exec_available || datastore['FORCE_UDF_UPLOAD']
Expand Down
11 changes: 9 additions & 2 deletions modules/exploits/windows/mysql/mysql_mof.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,15 @@ def upload_file(bin, dest)
def exploit
print_status("Attempting to login as '#{datastore['USERNAME']}:#{datastore['PASSWORD']}'")
begin
m = mysql_login(datastore['USERNAME'], datastore['PASSWORD'])
return if not m
# If we have a session make use it
if session
print_status("Using existing session #{session.sid}")
self.mysql_conn = session.client
else
# otherwise fallback to attempting to login
m = mysql_login(datastore['USERNAME'], datastore['PASSWORD'])
return unless m
end
rescue ::Mysql::AccessDeniedError
print_error("Access denied.")
return
Expand Down
14 changes: 11 additions & 3 deletions modules/exploits/windows/mysql/mysql_start_up.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,22 @@ def exploit
fail_with(Failure::BadConfig, "STARTUP_FOLDER should start and end with '/' Ex: /programdata/microsoft/windows/start menu/programs/startup/")
end

print_status("Attempting to login as '#{datastore['USERNAME']}:#{datastore['PASSWORD']}'")
print_status("Attempting to login as '#{datastore['USERNAME']}:#{datastore['PASSWORD']}'") unless session
begin
m = mysql_login(datastore['USERNAME'], datastore['PASSWORD'])
# If we have a session make use it
if session
print_status("Using existing session #{session.sid}")
self.mysql_conn = session.client
else
# otherwise fallback to attempting to login
m = mysql_login(datastore['USERNAME'], datastore['PASSWORD'])
return unless m
end
rescue ::Mysql::AccessDeniedError
fail_with(Failure::NoAccess, "#{peer} - Access denied")
end

fail_with(Failure::NoAccess, "#{peer} - Unable to Login") unless m
fail_with(Failure::NoAccess, "#{peer} - Unable to Login") unless m || session

unless is_windows?
fail_with(Failure::NoTarget, "#{peer} - Remote host isn't Windows")
Expand Down

0 comments on commit 98f961e

Please sign in to comment.