Skip to content

Commit 961c7eb

Browse files
committed
Fix ssh login pubkey module
1 parent 8ad35c0 commit 961c7eb

File tree

6 files changed

+86
-61
lines changed

6 files changed

+86
-61
lines changed

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ source 'https://rubygems.org'
33
# spec.add_runtime_dependency '<name>', [<version requirements>]
44
gemspec name: 'metasploit-framework'
55

6+
gem 'metasploit-credential', git: 'https://github.com/dwelch-r7/metasploit-credential', branch: 'fix-private-key-missing-fingerprint'
7+
68
# separate from test as simplecov is not run on travis-ci
79
group :coverage do
810
# code coverage for tests

Gemfile.lock

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
GIT
2+
remote: https://github.com/dwelch-r7/metasploit-credential
3+
revision: 6edcd13a7553706a3da0128d7c2dff8291f201a8
4+
branch: fix-private-key-missing-fingerprint
5+
specs:
6+
metasploit-credential (6.0.17)
7+
bigdecimal
8+
csv
9+
drb
10+
metasploit-concern
11+
metasploit-model
12+
metasploit_data_models (>= 5.0.0)
13+
mutex_m
14+
net-ssh
15+
pg
16+
railties
17+
rex-socket
18+
rubyntlm
19+
rubyzip
20+
121
PATH
222
remote: .
323
specs:
@@ -43,7 +63,6 @@ PATH
4363
lru_redux
4464
metasm
4565
metasploit-concern
46-
metasploit-credential
4766
metasploit-model
4867
metasploit-payloads (= 2.0.221)
4968
metasploit_data_models (>= 6.0.7)
@@ -327,20 +346,6 @@ GEM
327346
mutex_m
328347
railties (~> 7.0)
329348
zeitwerk
330-
metasploit-credential (6.0.16)
331-
bigdecimal
332-
csv
333-
drb
334-
metasploit-concern
335-
metasploit-model
336-
metasploit_data_models (>= 5.0.0)
337-
mutex_m
338-
net-ssh
339-
pg
340-
railties
341-
rex-socket
342-
rubyntlm
343-
rubyzip
344349
metasploit-model (5.0.4)
345350
activemodel (~> 7.0)
346351
activesupport (~> 7.0)
@@ -669,6 +674,7 @@ DEPENDENCIES
669674
fivemat
670675
license_finder (= 5.11.1)
671676
memory_profiler
677+
metasploit-credential!
672678
metasploit-framework!
673679
octokit
674680
pry-byebug

lib/metasploit/framework/login_scanner/ssh.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ def attempt_login(credential)
6868
:key_data => credential.private,
6969
)
7070
end
71+
opt_hash[:passphrase] = cred_details.password
7172

7273
result_options = {
7374
credential: credential

lib/msf/core/auxiliary/report_summary.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def create_credential_and_login(credential_data)
120120
# @param [Msf::Sessions::<SESSION_CLASS>] sess
121121
# @return [Msf::Sessions::<SESSION_CLASS>]
122122
def start_session(obj, info, ds_merge, crlf = false, sock = nil, sess = nil)
123-
return super unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS) && datastore['ShowSuccessfulLogins']
123+
return super unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS) && datastore['ShowSuccessfulLogins'] && @report
124124

125125
result = super
126126
@report[rhost] ||= {}

metasploit-framework.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Gem::Specification.new do |spec|
6767
# Metasploit::Concern hooks
6868
spec.add_runtime_dependency 'metasploit-concern'
6969
# Metasploit::Credential database models
70-
spec.add_runtime_dependency 'metasploit-credential'
70+
# spec.add_runtime_dependency 'metasploit-credential'
7171
# Database models shared between framework and Pro.
7272
spec.add_runtime_dependency 'metasploit_data_models', '>= 6.0.7'
7373
# Things that would normally be part of the database model, but which

modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb

Lines changed: 60 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -177,17 +177,20 @@ def run_host(ip)
177177
case result.status
178178
when Metasploit::Model::Login::Status::SUCCESSFUL
179179
print_brute :level => :good, :ip => ip, :msg => "Success: '#{result.credential}' '#{result.proof.to_s.gsub(/[\r\n\e\b\a]/, ' ')}'"
180-
credential_core = create_credential(credential_data)
181-
credential_data[:core] = credential_core
182-
create_credential_login(credential_data)
183-
tmp_key = result.credential.private
184-
ssh_key = SSHKey.new tmp_key
180+
ssh_key = Net::SSH::KeyFactory.load_data_private_key(credential_data[:private_data], datastore['key_pass'], false)
181+
182+
begin
183+
credential_core = create_credential(credential_data)
184+
credential_data[:core] = credential_core
185+
create_credential_login(credential_data)
186+
rescue ::StandardError => e
187+
print_error("Failed to create credential: #{e.class} #{e}")
188+
credential_core = nil
189+
end
190+
185191
if datastore['CreateSession']
186-
if credential_core.is_a? Metasploit::Credential::Core
187-
session_setup(result, scanner, ssh_key.fingerprint, credential_core.private_id)
188-
else
189-
session_setup(result, scanner, ssh_key.fingerprint, nil)
190-
end
192+
cred_id = credential_core.is_a?(Metasploit::Credential::Core) ? credential_core.private_id : nil
193+
session_setup(result, scanner, ssh_key.public_key.fingerprint, cred_id)
191194
end
192195
if datastore['GatherProof'] && scanner.get_platform(result.proof) == 'unknown'
193196
msg = "While a session may have opened, it may be bugged. If you experience issues with it, re-run this module with"
@@ -224,7 +227,7 @@ class KeyCollection < Metasploit::Framework::CredentialCollection
224227

225228
# Override CredentialCollection#has_privates?
226229
def has_privates?
227-
!@key_data.empty?
230+
@key_data.present?
228231
end
229232

230233
def realm
@@ -235,49 +238,62 @@ def valid?
235238
@error_list = []
236239
@key_data = Set.new
237240

238-
unless @private_key.present? || @key_path.present?
241+
if @private_key.present?
242+
results = validate_private_key(@private_key)
243+
elsif @key_path.present?
244+
results = validate_key_path(@key_path)
245+
else
246+
@error_list << "No key path or key provided"
239247
raise RuntimeError, "No key path or key provided"
240248
end
241249

242-
if @key_path.present?
243-
if File.directory?(@key_path)
244-
@key_files ||= Dir.entries(@key_path).reject { |f| f =~ /^\x2e|\x2epub$/ }
245-
@key_files.each do |f|
246-
begin
247-
data = read_key(File.join(@key_path, f))
248-
@key_data << data if valid_key?(data)
249-
rescue StandardError => e
250-
@error_list << "#{File.join(@key_path, f)}: #{e}"
251-
end
252-
end
253-
elsif File.file?(@key_path)
254-
begin
255-
data = read_key(@key_path)
256-
@key_data << data if valid_key?(data)
257-
rescue StandardError => e
258-
@error_list << "#{@key_path} could not be read, #{e}"
259-
end
260-
else
261-
raise RuntimeError, "Invalid key path"
262-
end
250+
if results[:key_data].present?
251+
@key_data.merge(results[:key_data])
252+
else
253+
@error_list.concat(results[:error_list]) if results[:error_list].present?
263254
end
264255

265-
if @private_key.present?
266-
data = Net::SSH::KeyFactory.load_data_private_key(@private_key, @password, false).to_s
267-
if valid_key?(data)
268-
@key_data << data
269-
else
270-
raise RuntimeError, "Invalid private key"
256+
@key_data.present?
257+
end
258+
259+
def validate_private_key(private_key)
260+
key_data = Set.new
261+
error_list = []
262+
begin
263+
if Net::SSH::KeyFactory.load_data_private_key(private_key, @password, false).present?
264+
key_data << private_key
271265
end
266+
rescue StandardError => e
267+
error_list << "Error validating private key: #{e}"
272268
end
273-
274-
!@key_data.empty?
269+
{key_data: key_data, error_list: error_list}
275270
end
276271

277-
def valid_key?(key_data)
278-
!!(key_data.match(/BEGIN [RECD]SA PRIVATE KEY/) && !key_data.match(/Proc-Type:.*ENCRYPTED/))
272+
def validate_key_path(key_path)
273+
key_data = Set.new
274+
error_list = []
275+
276+
if File.file?(key_path)
277+
key_files = [key_path]
278+
elsif File.directory?(key_path)
279+
key_files = Dir.entries(key_path).reject { |f| f =~ /^\x2e|\x2epub$/ }.map { |f| File.join(key_path, f) }
280+
else
281+
return {key_data: nil, error: "#{key_path} Invalid key path"}
282+
end
283+
284+
key_files.each do |f|
285+
begin
286+
if read_key(f).present?
287+
key_data << File.read(f)
288+
end
289+
rescue StandardError => e
290+
error_list << "#{f}: #{e}"
291+
end
292+
end
293+
{key_data: key_data, error_list: error_list}
279294
end
280295

296+
281297
def each
282298
prepended_creds.each { |c| yield c }
283299

@@ -307,7 +323,7 @@ def each_key
307323

308324
def read_key(file_path)
309325
@cache ||= {}
310-
@cache[file_path] ||= Net::SSH::KeyFactory.load_data_private_key(File.read(file_path), password, false, key_path).to_s
326+
@cache[file_path] ||= Net::SSH::KeyFactory.load_private_key(file_path, password, false)
311327
@cache[file_path]
312328
end
313329
end

0 commit comments

Comments
 (0)