Merge branch 'feature/MSP-9716/mssql_crack' into staging/electro-release
commit
35c0ef0c68
2
Gemfile
2
Gemfile
|
@ -7,7 +7,7 @@ group :db do
|
|||
# Needed for Msf::DbManager
|
||||
gem 'activerecord', '>= 3.0.0', '< 4.0.0'
|
||||
# Metasploit::Credential database models
|
||||
gem 'metasploit-credential', git: 'github-metasploit-credential:rapid7/metasploit-credential.git', tag: 'v0.4.3-electro-release'
|
||||
gem 'metasploit-credential', git: 'github-metasploit-credential:rapid7/metasploit-credential.git', tag: 'v0.4.8-electro-release'
|
||||
# Database models shared between framework and Pro.
|
||||
gem 'metasploit_data_models', '~> 0.17.2.pre.metasploit.pre.data.pre.models.pre.search'
|
||||
# Needed for module caching in Mdm::ModuleDetails
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
GIT
|
||||
remote: github-metasploit-credential:rapid7/metasploit-credential.git
|
||||
revision: b861156ed09cd4069541c60a611d89e302389d4c
|
||||
tag: v0.4.3-electro-release
|
||||
revision: b7234221ce41e311947e3e32c03aa7b6474f4f4f
|
||||
tag: v0.4.8-electro-release
|
||||
specs:
|
||||
metasploit-credential (0.4.3.pre.electro.pre.release)
|
||||
metasploit-credential (0.4.8.pre.electro.pre.release)
|
||||
metasploit-concern (~> 0.1.0)
|
||||
metasploit-model (>= 0.24.1.pre.semantic.pre.versioning.pre.2.pre.0, < 0.25)
|
||||
metasploit_data_models (>= 0.17.2.pre.metasploit.pre.data.pre.models.pre.search, < 0.18)
|
||||
|
|
|
@ -115,7 +115,7 @@ module Metasploit
|
|||
# @return [Array] An array set up for {::IO.popen} to use
|
||||
def crack_command
|
||||
cmd_string = binary_path
|
||||
cmd = [ cmd_string, '--session=' + john_session_id, '--nolog', '--dupe-suppression' ]
|
||||
cmd = [ cmd_string, '--session=' + john_session_id, '--nolog' ]
|
||||
|
||||
if config.present?
|
||||
cmd << ( "--config=" + config )
|
||||
|
|
|
@ -34,7 +34,7 @@ module Auxiliary::JohnTheRipper
|
|||
OptBool.new('USE_CREDS', [false, 'Use existing credential data saved in the database', true]),
|
||||
OptBool.new('USE_DB_INFO', [false, 'Use looted database schema info to seed the wordlist', true]),
|
||||
OptBool.new('USE_DEFAULT_WORDLIST', [false, 'Use the default metasploit wordlist', true]),
|
||||
OptBool.new['USE_HOSTNAMES', [false, 'Seed the wordlist with hostnames from the workspace', true]],
|
||||
OptBool.new('USE_HOSTNAMES', [false, 'Seed the wordlist with hostnames from the workspace', true]),
|
||||
OptBool.new('USE_ROOT_WORDS', [false, 'Use the Common Root Words Wordlist', true])
|
||||
], Msf::Auxiliary::JohnTheRipper
|
||||
)
|
||||
|
@ -59,7 +59,7 @@ module Auxiliary::JohnTheRipper
|
|||
# @return [nilClass] if there is no active framework db connection
|
||||
# @return [Metasploit::Framework::JtR::Cracker] if it successfully creates a JtR Cracker object
|
||||
def new_john_cracker
|
||||
return nil unless framework.db.active?
|
||||
return nil unless framework.db.active
|
||||
Metasploit::Framework::JtR::Cracker.new(
|
||||
config: datastore['CONFIG'],
|
||||
john_path: datastore['JOHN_PATH'],
|
||||
|
@ -75,11 +75,10 @@ module Auxiliary::JohnTheRipper
|
|||
# @return [nilClass] if there is no active framework db connection
|
||||
# @return [Rex::Quickfile] if it successfully wrote the wordlist to a file
|
||||
def wordlist_file
|
||||
return nil unless framework.db.active?
|
||||
return nil unless framework.db.active
|
||||
wordlist = Metasploit::Framework::JtR::Wordlist.new(
|
||||
custom_wordlist: datastore['CUSTOM_WORDLIST'],
|
||||
mutate: datastore['MUTATE'],
|
||||
pot: datastore['POT'],
|
||||
use_creds: datastore['USE_CREDS'],
|
||||
use_db_info: datastore['USE_DB_INFO'],
|
||||
use_default_wordlist: datastore['USE_DEFAULT_WORDLIST'],
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/auxiliary/jtr'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
|
@ -28,62 +29,78 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run
|
||||
@wordlist = Rex::Quickfile.new("jtrtmp")
|
||||
@formats = Set.new
|
||||
cracker = new_john_cracker
|
||||
|
||||
@wordlist.write( build_seed().flatten.uniq.join("\n") + "\n" )
|
||||
@wordlist.close
|
||||
print_status("Cracking MSSQL Hashes")
|
||||
crack("mssql")
|
||||
print_status("Cracking MSSQL05 Hashes")
|
||||
crack("mssql05")
|
||||
#generate our wordlist and close the file handle
|
||||
wordlist = wordlist_file
|
||||
wordlist.close
|
||||
print_status "Wordlist file written out to #{wordlist.path}"
|
||||
cracker.wordlist = wordlist.path
|
||||
cracker.hash_path = hash_file
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def crack(format)
|
||||
|
||||
hashlist = Rex::Quickfile.new("jtrtmp")
|
||||
ltype= "#{format}.hashes"
|
||||
myloots = myworkspace.loots.where('ltype=?', ltype)
|
||||
unless myloots.nil? or myloots.empty?
|
||||
myloots.each do |myloot|
|
||||
begin
|
||||
mssql_array = CSV.read(myloot.path).drop(1)
|
||||
rescue Exception => e
|
||||
print_error("Unable to read #{myloot.path} \n #{e}")
|
||||
end
|
||||
mssql_array.each do |row|
|
||||
hashlist.write("#{row[0]}:0x#{row[1]}:#{myloot.host.address}:#{myloot.service.port}\n")
|
||||
end
|
||||
@formats.each do |format|
|
||||
# dupe our original cracker so we can safely change options between each run
|
||||
cracker_instance = cracker.dup
|
||||
cracker_instance.format = format
|
||||
print_status "Cracking #{format} hashes in normal wordlist mode..."
|
||||
cracker_instance.crack do |line|
|
||||
print_status line.chomp
|
||||
end
|
||||
hashlist.close
|
||||
|
||||
print_status("HashList: #{hashlist.path}")
|
||||
print_status("Trying Wordlist: #{@wordlist.path}")
|
||||
john_crack(hashlist.path, :wordlist => @wordlist.path, :rules => 'single', :format => format)
|
||||
print_status "Cracking #{format} hashes in single mode..."
|
||||
cracker_instance.rules = 'single'
|
||||
cracker_instance.crack do |line|
|
||||
print_status line.chomp
|
||||
end
|
||||
|
||||
print_status("Trying Rule: All4...")
|
||||
john_crack(hashlist.path, :incremental => "All4", :format => format)
|
||||
print_status "Cracking #{format} hashes in incremental mode (All4)..."
|
||||
cracker_instance.rules = nil
|
||||
cracker_instance.wordlist = nil
|
||||
cracker_instance.incremental = 'All4'
|
||||
cracker_instance.crack do |line|
|
||||
print_status line.chomp
|
||||
end
|
||||
|
||||
print_status("Trying Rule: Digits5...")
|
||||
john_crack(hashlist.path, :incremental => "Digits5", :format => format)
|
||||
print_status "Cracking #{format} hashes in incremental mode (Digits5)..."
|
||||
cracker_instance.incremental = 'Digits5'
|
||||
cracker_instance.crack do |line|
|
||||
print_status line.chomp
|
||||
end
|
||||
|
||||
cracked = john_show_passwords(hashlist.path, format)
|
||||
|
||||
print_status("#{cracked[:cracked]} hashes were cracked!")
|
||||
cracked[:users].each_pair do |k,v|
|
||||
print_good("Host: #{v[1]} Port: #{v[2]} User: #{k} Pass: #{v[0]}")
|
||||
report_auth_info(
|
||||
:host => v[1],
|
||||
:port => v[2],
|
||||
:sname => 'mssql',
|
||||
:user => k,
|
||||
:pass => v[0]
|
||||
)
|
||||
print_status "Cracked Passwords this run:"
|
||||
cracker_instance.each_cracked_password do |password_line|
|
||||
password_line.chomp!
|
||||
next if password_line.blank?
|
||||
fields = password_line.split(":")
|
||||
# If we don't have an expected minimum number of fields, this is probably not a hash line
|
||||
next unless fields.count >=3
|
||||
username = fields.shift
|
||||
core_id = fields.pop
|
||||
password = fields.join(':') # Anything left must be the password. This accounts for passwords with : in them
|
||||
print_good password_line
|
||||
create_cracked_credential( username: username, password: password, core_id: core_id)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def hash_file
|
||||
hashlist = Rex::Quickfile.new("hashes_tmp")
|
||||
Metasploit::Credential::NonreplayableHash.joins(:cores).where(metasploit_credential_cores: { workspace_id: myworkspace.id }, jtr_format: ['mssql', 'mssql05', 'mssql12']).each do |hash|
|
||||
# Track the formats that we've seen so we do not attempt a format that isn't relevant
|
||||
@formats << hash.jtr_format
|
||||
hash.cores.each do |core|
|
||||
user = core.public.username
|
||||
hash_string = "0x#{hash.data}"
|
||||
id = core.id
|
||||
hashlist.puts "#{user}:#{hash_string}:#{id}:"
|
||||
end
|
||||
end
|
||||
hashlist.close
|
||||
print_status "Hashes Written out to #{hashlist.path}"
|
||||
hashlist.path
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
@ -35,17 +35,57 @@ class Metasploit3 < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
|
||||
service_data = {
|
||||
address: ip,
|
||||
port: rport,
|
||||
service_name: 'mssql',
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
module_fullname: self.fullname,
|
||||
origin_type: :service,
|
||||
private_data: datastore['PASSWORD'],
|
||||
private_type: :password,
|
||||
username: datastore['USERNAME']
|
||||
}
|
||||
|
||||
if datastore['USE_WINDOWS_AUTHENT']
|
||||
credential_data[:realm_key] = Metasploit::Credential::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
|
||||
credential_data[:realm_value] = datastore['DOMAIN']
|
||||
end
|
||||
credential_data.merge!(service_data)
|
||||
|
||||
credential_core = create_credential(credential_data)
|
||||
|
||||
login_data = {
|
||||
core: credential_core,
|
||||
last_attempted_at: DateTime.now,
|
||||
status: Metasploit::Credential::Login::Status::SUCCESSFUL
|
||||
}
|
||||
login_data.merge!(service_data)
|
||||
|
||||
is_sysadmin = mssql_query(mssql_is_sysadmin())[:rows][0][0]
|
||||
|
||||
unless is_sysadmin == 0
|
||||
login_data[:access_level] = 'admin'
|
||||
end
|
||||
|
||||
create_credential_login(login_data)
|
||||
|
||||
#Grabs the Instance Name and Version of MSSQL(2k,2k5,2k8)
|
||||
instancename= mssql_query(mssql_enumerate_servername())[:rows][0][0].split('\\')[1]
|
||||
print_status("Instance Name: #{instancename.inspect}")
|
||||
version = mssql_query(mssql_sql_info())[:rows][0][0]
|
||||
version_year = version.split('-')[0].slice(/\d\d\d\d/)
|
||||
|
||||
mssql_hashes = mssql_hashdump(version_year)
|
||||
unless mssql_hashes.nil?
|
||||
report_hashes(mssql_hashes,version_year)
|
||||
unless is_sysadmin == 0
|
||||
mssql_hashes = mssql_hashdump(version_year)
|
||||
unless mssql_hashes.nil?
|
||||
report_hashes(mssql_hashes,version_year)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
@ -57,8 +97,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
when "2000"
|
||||
hashtype = "mssql"
|
||||
|
||||
when "2005", "2008", "2012", "2014"
|
||||
when "2005", "2008"
|
||||
hashtype = "mssql05"
|
||||
when "2012", "2014"
|
||||
hashtype = "mssql12"
|
||||
end
|
||||
|
||||
this_service = report_service(
|
||||
|
|
Loading…
Reference in New Issue