Skip to content

Commit

Permalink
Land #18759, Updates MySQL modules to now support the new MySQL sessi…
Browse files Browse the repository at this point in the history
…on type
  • Loading branch information
adfoster-r7 authored Feb 8, 2024
2 parents d3bde6b + 02ae96e commit 8b71afd
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 76 deletions.
32 changes: 13 additions & 19 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 @@ -33,17 +37,13 @@ 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
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)
# Deprecating this in favor off `mysql_conn`
@mysql_handle = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :mysql_conn, :@mysql_handle, ActiveSupport::Deprecation.new)

rescue Errno::ECONNREFUSED
print_error("Connection refused")
Expand All @@ -62,20 +62,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
mysql_conn.close if mysql_conn
disconnect if sock
vprint_status "#{rhost}:#{rport} MySQL - Disconnected"
end

Expand All @@ -92,7 +86,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 +95,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
2 changes: 1 addition & 1 deletion lib/msf/core/optional_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def initialize(info = {})
[
Msf::OptInt.new('SESSION', [ false, 'The session to run this module on' ]),
Msf::Opt::RHOST(nil, false),
Msf::Opt::RPORT(nil, false)
Msf::Opt::RPORT(3306, false)
]
)
end
Expand Down
19 changes: 15 additions & 4 deletions modules/auxiliary/admin/mysql/mysql_enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::MYSQL
include Msf::OptionalSession

def initialize(info = {})
super(update_info(info,
Expand All @@ -16,6 +17,7 @@ def initialize(info = {})
},
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>' ],
'License' => MSF_LICENSE,
'SessionTypes' => %w[MySQL],
'References' =>
[
[ 'URL', 'https://cisecurity.org/benchmarks.html' ]
Expand Down Expand Up @@ -52,7 +54,16 @@ def report_cred(opts)
end

def run
return if not mysql_login_datastore
# If we have a session make use of it
if session
print_status("Using existing session #{session.sid}")
self.mysql_conn = session.client
self.sock = session.client.socket
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 @@ -111,8 +122,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 Expand Up @@ -238,6 +249,6 @@ def run
end
end

mysql_logoff
mysql_logoff unless session
end
end
14 changes: 12 additions & 2 deletions modules/auxiliary/admin/mysql/mysql_sql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::MYSQL
include Msf::OptionalSession

def initialize(info = {})
super(update_info(info,
Expand All @@ -14,7 +15,8 @@ def initialize(info = {})
against a MySQL instance given the appropriate credentials.
},
'Author' => [ 'Bernardo Damele A. G. <bernardo.damele[at]gmail.com>' ],
'License' => MSF_LICENSE
'License' => MSF_LICENSE,
'SessionTypes' => %w[MySQL]
))

register_options(
Expand All @@ -33,7 +35,15 @@ def cmd_select(*args)
end

def run
return if not mysql_login_datastore
# If we have a session make use of 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']) || []
res.each do |row|
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
29 changes: 18 additions & 11 deletions modules/auxiliary/scanner/mysql/mysql_file_enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::MYSQL
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
include Msf::OptionalSession

def initialize
super(
Expand All @@ -22,7 +23,8 @@ def initialize
[ 'URL', 'http://pauldotcom.com/2013/01/mysql-file-system-enumeration.html' ],
[ 'URL', 'http://www.digininja.org/projects/mysql_file_enum.php' ]
],
'License' => MSF_LICENSE
'License' => MSF_LICENSE,
'SessionTypes' => %w[MySQL]
)

register_options([
Expand All @@ -37,15 +39,20 @@ 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...")
vprint_status("Login...") unless session

if (not mysql_login_datastore)
return
# If we have a session make use of 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
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
21 changes: 14 additions & 7 deletions modules/auxiliary/scanner/mysql/mysql_hashdump.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::MYSQL
include Msf::Auxiliary::Report

include Msf::Auxiliary::Scanner
include Msf::OptionalSession

def initialize
super(
Expand All @@ -17,17 +17,24 @@ def initialize
hashes from a MySQL server and stores them for later cracking.
),
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE
'License' => MSF_LICENSE,
'SessionTypes' => %w[MySQL]
)
end

def run_host(ip)

return unless mysql_login_datastore
# If we have a session make use of 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: ip,
port: rport,
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
28 changes: 17 additions & 11 deletions modules/auxiliary/scanner/mysql/mysql_schemadump.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::MYSQL
include Msf::Auxiliary::Report

include Msf::Auxiliary::Scanner
include Msf::OptionalSession

def initialize
super(
Expand All @@ -19,7 +19,8 @@ def initialize
MySQL DB server.
},
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE
'License' => MSF_LICENSE,
'SessionTypes' => %w[MySQL]
)

register_options([
Expand All @@ -29,30 +30,35 @@ def initialize
end

def run_host(ip)

if (not mysql_login_datastore)
return
# If we have a session make use of 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
Loading

0 comments on commit 8b71afd

Please sign in to comment.