Skip to content

Commit 35c0ef0

Browse files
committed
Merge branch 'feature/MSP-9716/mssql_crack' into staging/electro-release
2 parents 99b1702 + 06da2d8 commit 35c0ef0

File tree

6 files changed

+120
-62
lines changed

6 files changed

+120
-62
lines changed

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ group :db do
77
# Needed for Msf::DbManager
88
gem 'activerecord', '>= 3.0.0', '< 4.0.0'
99
# Metasploit::Credential database models
10-
gem 'metasploit-credential', git: 'github-metasploit-credential:rapid7/metasploit-credential.git', tag: 'v0.4.3-electro-release'
10+
gem 'metasploit-credential', git: 'github-metasploit-credential:rapid7/metasploit-credential.git', tag: 'v0.4.8-electro-release'
1111
# Database models shared between framework and Pro.
1212
gem 'metasploit_data_models', '~> 0.17.2.pre.metasploit.pre.data.pre.models.pre.search'
1313
# Needed for module caching in Mdm::ModuleDetails

Gemfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
GIT
22
remote: github-metasploit-credential:rapid7/metasploit-credential.git
3-
revision: b861156ed09cd4069541c60a611d89e302389d4c
4-
tag: v0.4.3-electro-release
3+
revision: b7234221ce41e311947e3e32c03aa7b6474f4f4f
4+
tag: v0.4.8-electro-release
55
specs:
6-
metasploit-credential (0.4.3.pre.electro.pre.release)
6+
metasploit-credential (0.4.8.pre.electro.pre.release)
77
metasploit-concern (~> 0.1.0)
88
metasploit-model (>= 0.24.1.pre.semantic.pre.versioning.pre.2.pre.0, < 0.25)
99
metasploit_data_models (>= 0.17.2.pre.metasploit.pre.data.pre.models.pre.search, < 0.18)

lib/metasploit/framework/jtr/cracker.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def crack
115115
# @return [Array] An array set up for {::IO.popen} to use
116116
def crack_command
117117
cmd_string = binary_path
118-
cmd = [ cmd_string, '--session=' + john_session_id, '--nolog', '--dupe-suppression' ]
118+
cmd = [ cmd_string, '--session=' + john_session_id, '--nolog' ]
119119

120120
if config.present?
121121
cmd << ( "--config=" + config )

lib/msf/core/auxiliary/jtr.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def initialize(info = {})
3434
OptBool.new('USE_CREDS', [false, 'Use existing credential data saved in the database', true]),
3535
OptBool.new('USE_DB_INFO', [false, 'Use looted database schema info to seed the wordlist', true]),
3636
OptBool.new('USE_DEFAULT_WORDLIST', [false, 'Use the default metasploit wordlist', true]),
37-
OptBool.new['USE_HOSTNAMES', [false, 'Seed the wordlist with hostnames from the workspace', true]],
37+
OptBool.new('USE_HOSTNAMES', [false, 'Seed the wordlist with hostnames from the workspace', true]),
3838
OptBool.new('USE_ROOT_WORDS', [false, 'Use the Common Root Words Wordlist', true])
3939
], Msf::Auxiliary::JohnTheRipper
4040
)
@@ -59,7 +59,7 @@ def john_lm_upper_to_ntlm(pwd, hash)
5959
# @return [nilClass] if there is no active framework db connection
6060
# @return [Metasploit::Framework::JtR::Cracker] if it successfully creates a JtR Cracker object
6161
def new_john_cracker
62-
return nil unless framework.db.active?
62+
return nil unless framework.db.active
6363
Metasploit::Framework::JtR::Cracker.new(
6464
config: datastore['CONFIG'],
6565
john_path: datastore['JOHN_PATH'],
@@ -75,11 +75,10 @@ def new_john_cracker
7575
# @return [nilClass] if there is no active framework db connection
7676
# @return [Rex::Quickfile] if it successfully wrote the wordlist to a file
7777
def wordlist_file
78-
return nil unless framework.db.active?
78+
return nil unless framework.db.active
7979
wordlist = Metasploit::Framework::JtR::Wordlist.new(
8080
custom_wordlist: datastore['CUSTOM_WORDLIST'],
8181
mutate: datastore['MUTATE'],
82-
pot: datastore['POT'],
8382
use_creds: datastore['USE_CREDS'],
8483
use_db_info: datastore['USE_DB_INFO'],
8584
use_default_wordlist: datastore['USE_DEFAULT_WORDLIST'],

modules/auxiliary/analyze/jtr_mssql_fast.rb

Lines changed: 65 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66

77
require 'msf/core'
8+
require 'msf/core/auxiliary/jtr'
89

910
class Metasploit3 < Msf::Auxiliary
1011

@@ -28,62 +29,78 @@ def initialize
2829
end
2930

3031
def run
31-
@wordlist = Rex::Quickfile.new("jtrtmp")
32-
33-
@wordlist.write( build_seed().flatten.uniq.join("\n") + "\n" )
34-
@wordlist.close
35-
print_status("Cracking MSSQL Hashes")
36-
crack("mssql")
37-
print_status("Cracking MSSQL05 Hashes")
38-
crack("mssql05")
32+
@formats = Set.new
33+
cracker = new_john_cracker
34+
35+
#generate our wordlist and close the file handle
36+
wordlist = wordlist_file
37+
wordlist.close
38+
print_status "Wordlist file written out to #{wordlist.path}"
39+
cracker.wordlist = wordlist.path
40+
cracker.hash_path = hash_file
41+
42+
@formats.each do |format|
43+
# dupe our original cracker so we can safely change options between each run
44+
cracker_instance = cracker.dup
45+
cracker_instance.format = format
46+
print_status "Cracking #{format} hashes in normal wordlist mode..."
47+
cracker_instance.crack do |line|
48+
print_status line.chomp
49+
end
3950

40-
end
51+
print_status "Cracking #{format} hashes in single mode..."
52+
cracker_instance.rules = 'single'
53+
cracker_instance.crack do |line|
54+
print_status line.chomp
55+
end
4156

57+
print_status "Cracking #{format} hashes in incremental mode (All4)..."
58+
cracker_instance.rules = nil
59+
cracker_instance.wordlist = nil
60+
cracker_instance.incremental = 'All4'
61+
cracker_instance.crack do |line|
62+
print_status line.chomp
63+
end
4264

65+
print_status "Cracking #{format} hashes in incremental mode (Digits5)..."
66+
cracker_instance.incremental = 'Digits5'
67+
cracker_instance.crack do |line|
68+
print_status line.chomp
69+
end
4370

71+
print_status "Cracked Passwords this run:"
72+
cracker_instance.each_cracked_password do |password_line|
73+
password_line.chomp!
74+
next if password_line.blank?
75+
fields = password_line.split(":")
76+
# If we don't have an expected minimum number of fields, this is probably not a hash line
77+
next unless fields.count >=3
78+
username = fields.shift
79+
core_id = fields.pop
80+
password = fields.join(':') # Anything left must be the password. This accounts for passwords with : in them
81+
print_good password_line
82+
create_cracked_credential( username: username, password: password, core_id: core_id)
83+
end
84+
end
4485

45-
def crack(format)
86+
end
4687

47-
hashlist = Rex::Quickfile.new("jtrtmp")
48-
ltype= "#{format}.hashes"
49-
myloots = myworkspace.loots.where('ltype=?', ltype)
50-
unless myloots.nil? or myloots.empty?
51-
myloots.each do |myloot|
52-
begin
53-
mssql_array = CSV.read(myloot.path).drop(1)
54-
rescue Exception => e
55-
print_error("Unable to read #{myloot.path} \n #{e}")
56-
end
57-
mssql_array.each do |row|
58-
hashlist.write("#{row[0]}:0x#{row[1]}:#{myloot.host.address}:#{myloot.service.port}\n")
59-
end
60-
end
61-
hashlist.close
62-
63-
print_status("HashList: #{hashlist.path}")
64-
print_status("Trying Wordlist: #{@wordlist.path}")
65-
john_crack(hashlist.path, :wordlist => @wordlist.path, :rules => 'single', :format => format)
66-
67-
print_status("Trying Rule: All4...")
68-
john_crack(hashlist.path, :incremental => "All4", :format => format)
69-
70-
print_status("Trying Rule: Digits5...")
71-
john_crack(hashlist.path, :incremental => "Digits5", :format => format)
72-
73-
cracked = john_show_passwords(hashlist.path, format)
74-
75-
print_status("#{cracked[:cracked]} hashes were cracked!")
76-
cracked[:users].each_pair do |k,v|
77-
print_good("Host: #{v[1]} Port: #{v[2]} User: #{k} Pass: #{v[0]}")
78-
report_auth_info(
79-
:host => v[1],
80-
:port => v[2],
81-
:sname => 'mssql',
82-
:user => k,
83-
:pass => v[0]
84-
)
88+
def hash_file
89+
hashlist = Rex::Quickfile.new("hashes_tmp")
90+
Metasploit::Credential::NonreplayableHash.joins(:cores).where(metasploit_credential_cores: { workspace_id: myworkspace.id }, jtr_format: ['mssql', 'mssql05', 'mssql12']).each do |hash|
91+
# Track the formats that we've seen so we do not attempt a format that isn't relevant
92+
@formats << hash.jtr_format
93+
hash.cores.each do |core|
94+
user = core.public.username
95+
hash_string = "0x#{hash.data}"
96+
id = core.id
97+
hashlist.puts "#{user}:#{hash_string}:#{id}:"
8598
end
8699
end
100+
hashlist.close
101+
print_status "Hashes Written out to #{hashlist.path}"
102+
hashlist.path
87103
end
88104

105+
89106
end

modules/auxiliary/scanner/mssql/mssql_hashdump.rb

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,57 @@ def run_host(ip)
3535
return
3636
end
3737

38+
service_data = {
39+
address: ip,
40+
port: rport,
41+
service_name: 'mssql',
42+
protocol: 'tcp',
43+
workspace_id: myworkspace_id
44+
}
45+
46+
credential_data = {
47+
module_fullname: self.fullname,
48+
origin_type: :service,
49+
private_data: datastore['PASSWORD'],
50+
private_type: :password,
51+
username: datastore['USERNAME']
52+
}
53+
54+
if datastore['USE_WINDOWS_AUTHENT']
55+
credential_data[:realm_key] = Metasploit::Credential::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
56+
credential_data[:realm_value] = datastore['DOMAIN']
57+
end
58+
credential_data.merge!(service_data)
59+
60+
credential_core = create_credential(credential_data)
61+
62+
login_data = {
63+
core: credential_core,
64+
last_attempted_at: DateTime.now,
65+
status: Metasploit::Credential::Login::Status::SUCCESSFUL
66+
}
67+
login_data.merge!(service_data)
68+
69+
is_sysadmin = mssql_query(mssql_is_sysadmin())[:rows][0][0]
70+
71+
unless is_sysadmin == 0
72+
login_data[:access_level] = 'admin'
73+
end
74+
75+
create_credential_login(login_data)
76+
3877
#Grabs the Instance Name and Version of MSSQL(2k,2k5,2k8)
3978
instancename= mssql_query(mssql_enumerate_servername())[:rows][0][0].split('\\')[1]
4079
print_status("Instance Name: #{instancename.inspect}")
4180
version = mssql_query(mssql_sql_info())[:rows][0][0]
4281
version_year = version.split('-')[0].slice(/\d\d\d\d/)
4382

44-
mssql_hashes = mssql_hashdump(version_year)
45-
unless mssql_hashes.nil?
46-
report_hashes(mssql_hashes,version_year)
83+
unless is_sysadmin == 0
84+
mssql_hashes = mssql_hashdump(version_year)
85+
unless mssql_hashes.nil?
86+
report_hashes(mssql_hashes,version_year)
87+
end
4788
end
48-
4989
end
5090

5191

@@ -57,8 +97,10 @@ def report_hashes(mssql_hashes, version_year)
5797
when "2000"
5898
hashtype = "mssql"
5999

60-
when "2005", "2008", "2012", "2014"
100+
when "2005", "2008"
61101
hashtype = "mssql05"
102+
when "2012", "2014"
103+
hashtype = "mssql12"
62104
end
63105

64106
this_service = report_service(

0 commit comments

Comments
 (0)