Skip to content

Commit a98de2d

Browse files
committed
Land rapid7#9358, Support password protected key files
2 parents a1d43c8 + f2a8d68 commit a98de2d

File tree

1 file changed

+8
-85
lines changed

1 file changed

+8
-85
lines changed

modules/auxiliary/scanner/ssh/ssh_login_pubkey.rb

Lines changed: 8 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,19 @@ def initialize
2828
this module will record successful logins and hosts so you can
2929
track your access.
3030
31-
Note that password-protected key files will not function with this
32-
module -- it is designed specifically for unencrypted (passwordless)
33-
keys.
34-
35-
Key files may be a single private (unencrypted) key, or several private
36-
keys concatenated together as an ASCII text file. Non-key data should be
37-
silently ignored.
31+
Key files may be a single private key, or several private keys in a single
32+
directory. Only a single passphrase is supported however, so it must either
33+
be shared between subject keys or only belong to a single one.
3834
},
39-
'Author' => ['todb'],
35+
'Author' => ['todb', 'RageLtMan'],
4036
'License' => MSF_LICENSE
4137
)
4238

4339
register_options(
4440
[
4541
Opt::RPORT(22),
4642
OptPath.new('KEY_PATH', [true, 'Filename or directory of cleartext private keys. Filenames beginning with a dot, or ending in ".pub" will be skipped.']),
43+
OptString.new('KEY_PASS', [false, 'Passphrase for SSH private key(s)']),
4744
], self.class
4845
)
4946

@@ -63,10 +60,6 @@ module -- it is designed specifically for unencrypted (passwordless)
6360

6461
end
6562

66-
def key_dir
67-
datastore['KEY_DIR']
68-
end
69-
7063
def rport
7164
datastore['RPORT']
7265
end
@@ -75,71 +68,6 @@ def ip
7568
datastore['RHOST']
7669
end
7770

78-
def read_keyfile(file)
79-
if file == :keyfile_b64
80-
keyfile = datastore['SSH_KEYFILE_B64'].unpack("m*").first
81-
elsif file.kind_of? Array
82-
keyfile = ''
83-
file.each do |dir_entry|
84-
next unless File.readable? dir_entry
85-
keyfile << File.open(dir_entry, "rb") {|f| f.read(f.stat.size)}
86-
end
87-
else
88-
keyfile = File.open(file, "rb") {|f| f.read(f.stat.size)}
89-
end
90-
keys = []
91-
this_key = []
92-
in_key = false
93-
keyfile.split("\n").each do |line|
94-
in_key = true if(line =~ /^-----BEGIN [RD]SA PRIVATE KEY-----/)
95-
this_key << line if in_key
96-
if(line =~ /^-----END [RD]SA PRIVATE KEY-----/)
97-
in_key = false
98-
keys << (this_key.join("\n") + "\n")
99-
this_key = []
100-
end
101-
end
102-
if keys.empty?
103-
print_error "#{ip}:#{rport} SSH - No keys found."
104-
end
105-
return validate_keys(keys)
106-
end
107-
108-
# Validates that the key isn't total garbage. Also throws out SSH2 keys --
109-
# can't use 'em for Net::SSH.
110-
def validate_keys(keys)
111-
keepers = []
112-
keys.each do |key|
113-
# Needs a beginning
114-
next unless key =~ /^-----BEGIN [RD]SA PRIVATE KEY-----\x0d?\x0a/m
115-
# Needs an end
116-
next unless key =~ /\n-----END [RD]SA PRIVATE KEY-----\x0d?\x0a?$/m
117-
# Shouldn't have binary.
118-
next unless key.scan(/[\x00-\x08\x0b\x0c\x0e-\x1f\x80-\xff]/).empty?
119-
# Add more tests to taste.
120-
keepers << key
121-
end
122-
if keepers.empty?
123-
print_error "#{ip}:#{rport} SSH - No valid keys found"
124-
end
125-
return keepers
126-
end
127-
128-
def pull_cleartext_keys(keys)
129-
cleartext_keys = []
130-
keys.each do |key|
131-
next unless key
132-
next if key =~ /Proc-Type:.*ENCRYPTED/
133-
this_key = key.gsub(/\x0d/,"")
134-
next if cleartext_keys.include? this_key
135-
cleartext_keys << this_key
136-
end
137-
if cleartext_keys.empty?
138-
print_error "#{ip}:#{rport} SSH - No valid cleartext keys found"
139-
end
140-
return cleartext_keys
141-
end
142-
14371
def session_setup(result, ssh_socket, fingerprint)
14472
return unless ssh_socket
14573

@@ -196,6 +124,7 @@ def run_host(ip)
196124

197125
keys = KeyCollection.new(
198126
key_path: datastore['KEY_PATH'],
127+
password: datastore['KEY_PASS'],
199128
user_file: datastore['USER_FILE'],
200129
username: datastore['USERNAME'],
201130
)
@@ -289,7 +218,7 @@ def valid!
289218
end
290219

291220
def valid_key?(key_data)
292-
!!(key_data.match(/BEGIN [RD]SA PRIVATE KEY/) && !key_data.match(/Proc-Type:.*ENCRYPTED/))
221+
!!(key_data.match(/BEGIN [RECD]SA PRIVATE KEY/) && !key_data.match(/Proc-Type:.*ENCRYPTED/))
293222
end
294223

295224
def each
@@ -321,13 +250,7 @@ def each_key
321250

322251
def read_key(filename)
323252
@cache ||= {}
324-
unless @cache[filename]
325-
data = File.open(filename, 'rb') { |fd| fd.read(fd.stat.size) }
326-
#if data.match
327-
328-
@cache[filename] = data
329-
end
330-
253+
@cache[filename] ||= Net::SSH::KeyFactory.load_data_private_key(File.read(key_path), password, false, key_path).to_s
331254
@cache[filename]
332255
end
333256

0 commit comments

Comments
 (0)