Massive rework of the storage/notes/reporting

bug/bundler_fix
Stuart Morgan 2015-12-05 21:18:29 +00:00
parent 66ba204c11
commit 09c58e4097
1 changed files with 86 additions and 34 deletions

View File

@ -29,13 +29,16 @@ class Metasploit3 < Msf::Auxiliary
this module will record authorized public keys and hosts so you can
track your process.
Key files may be a single public (unencrypted) key, or several public
keys concatenated together as an ASCII text file. Non-key data should be
silently ignored. Private keys will only utilize the public key component
stored within the key file.
},
'Author' => ['todb', 'hdm'],
'Author' => [
'todb',
'hdm',
'Stuart Morgan <stuart.morgan[at]mwrinfosecurity.com>', # Reworked the storage (db, credentials, notes, loot) only
],
'License' => MSF_LICENSE
)
@ -116,11 +119,14 @@ class Metasploit3 < Msf::Auxiliary
keepers = []
keys.each do |key|
if key =~ /ssh-(dss|rsa)/
keepers << key
# A public key has been provided
keepers << { :public => key, :private => "" }
next
else # Use the mighty SSHKey library from James Miller to convert them on the fly.
else
# Use the mighty SSHKey library from James Miller to convert them on the fly.
# This is where a PRIVATE key has been provided
ssh_version = SSHKey.new(key).ssh_public_key rescue nil
keepers << ssh_version if ssh_version
keepers << { :public => ssh_version, :private => key } if ssh_version
next
end
@ -130,8 +136,8 @@ class Metasploit3 < Msf::Auxiliary
next unless key =~ /\n-----END [RD]SA (PRIVATE|PUBLIC) KEY-----\x0d?\x0a?$/m
# Shouldn't have binary.
next unless key.scan(/[\x00-\x08\x0b\x0c\x0e-\x1f\x80-\xff]/).empty?
# Add more tests to taste.
keepers << key
# Add more tests to test
keepers << { :public => key, :private => "" }
end
if keepers.empty?
print_error "#{ip}:#{rport} SSH - No valid keys found"
@ -142,9 +148,9 @@ class Metasploit3 < Msf::Auxiliary
def pull_cleartext_keys(keys)
cleartext_keys = []
keys.each do |key|
next unless key
next if key =~ /Proc-Type:.*ENCRYPTED/
this_key = key.gsub(/\x0d/,"")
next unless key[:public]
next if key[:private] =~ /Proc-Type:.*ENCRYPTED/
this_key = { :public => key[:public].gsub(/\x0d/,""), :private => key[:private] }
next if cleartext_keys.include? this_key
cleartext_keys << this_key
end
@ -182,26 +188,26 @@ class Metasploit3 < Msf::Auxiliary
@alerted_with_msg = true
end
cleartext_keys.each_with_index do |key_data,key_idx|
key_info = ""
if key_data =~ /ssh\-(rsa|dss)\s+([^\s]+)\s+(.*)/
cleartext_keys.each_with_index do |key_data,key_idx|
key_info = ""
if key_data[:public] =~ /ssh\-(rsa|dss)\s+([^\s]+)\s+(.*)/
key_info = "- #{$3.strip}"
end
accepted = []
opt_hash = {
:auth_methods => ['publickey'],
:msframework => framework,
:msfmodule => self,
:port => port,
:key_data => key_data,
:key_data => key_data[:public],
:disable_agent => true,
:record_auth_info => true,
:skip_private_keys => true,
:config =>false,
:accepted_key_callback => Proc.new {|key| accepted << key },
:accepted_key_callback => Proc.new {|key| accepted << { :data => key_data, :key => key } },
:proxies => datastore['Proxies']
}
@ -244,35 +250,63 @@ class Metasploit3 < Msf::Auxiliary
end
accepted.each do |key|
print_brute :level => :good, :msg => "Accepted: '#{user}' with key '#{key[:fingerprint]}' #{key_info}"
do_report(ip, rport, user, key, key_data)
print_brute :level => :good, :msg => "Public key accepted: '#{user}' with key '#{key[:key][:fingerprint]}' #{key_info}"
do_report(ip, rport, user, key, key_data[:public], key_info)
end
end
end
def do_report(ip, port, user, key, key_data)
def do_report(ip, port, user, key, key_data, key_info)
return unless framework.db.active
keyfile_path = store_keyfile(ip,user,key[:fingerprint],key_data)
cred_hash = {
:host => ip,
:port => rport,
:sname => 'ssh',
:user => user,
:pass => keyfile_path,
:source_type => "user_supplied",
:type => 'ssh_pubkey',
:proof => "KEY=#{key[:fingerprint]}",
:duplicate_ok => true,
:active => true
}
this_cred = report_auth_info(cred_hash)
# Store a note relating to the public key test
note_information = {
user: user,
public_key: key_data,
private_key: (key[:data][:private]) ? 'Yes' : 'No',
info: key_info
}
report_note(host: ip, port: port, type: "ssh.publickey", data: note_information, update: :unique_data)
if key[:data][:private]
# Store these keys in loot
public_keyfile_path = store_public_keyfile(ip,user,key[:fingerprint],key_data)
private_keyfile_path = store_private_keyfile(ip,user,key[:fingerprint],key[:data][:private])
# Use the proper credential method to store credentials that we have
service_data = {
address: ip,
port: port,
service_name: 'ssh',
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
module_fullname: self.fullname,
origin_type: :service,
private_data: key[:data][:private],
private_type: :ssh_key,
username: key[:key][:user],
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
last_attempted_at: DateTime.now,
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: private_keyfile_path
}.merge(service_data)
create_credential_login(login_data)
end
end
def existing_loot(ltype, key_id)
framework.db.loots(myworkspace).where(ltype: ltype).select {|l| l.info == key_id}.first
end
def store_keyfile(ip,user,key_id,key_data)
def store_public_keyfile(ip,user,key_id,key_data)
safe_username = user.gsub(/[^A-Za-z0-9]/,"_")
ktype = key_data.match(/ssh-(rsa|dss)/)[1] rescue nil
return unless ktype
@ -291,6 +325,24 @@ class Metasploit3 < Msf::Auxiliary
return keyfile_path
end
def store_private_keyfile(ip,user,key_id,key_data)
safe_username = user.gsub(/[^A-Za-z0-9]/,"_")
ktype = key_data.match(/-----BEGIN ([RD]SA) (?:PRIVATE|PUBLIC) KEY-----/)[1].downcase rescue nil
return unless ktype
ltype = "host.unix.ssh.#{user}_#{ktype}_private"
keyfile = existing_loot(ltype, key_id)
return keyfile.path if keyfile
keyfile_path = store_loot(
ltype,
"application/octet-stream", # Text, but always want to mime-type attach it
ip,
(key_data + "\n"),
"#{safe_username}_#{ktype}.private",
key_id
)
return keyfile_path
end
def run_host(ip)
# Since SSH collects keys and tries them all on one authentication session, it doesn't
# make sense to iteratively go through all the keys individually. So, ignore the pass variable,