Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/ui/console/command_dispatcher/db/certs.rb
Views: 18756
1
# -*- coding: binary -*-
2
3
module Msf::Ui::Console::CommandDispatcher::Db::Certs
4
#
5
# Tab completion for the certs command
6
#
7
# @param str [String] the string currently being typed before tab was hit
8
# @param words [Array<String>] the previously completed words on the command line. words is always
9
# at least 1 when tab completion has reached this stage since the command itself has been completed
10
def cmd_certs_tabs(str, words)
11
tabs = []
12
13
case words.length
14
when 1
15
tabs = @@certs_opts.option_keys.select { |opt| opt.start_with?(str) }
16
when 2
17
tabs = if words[1] == '-e' || words[1] == '--export'
18
tab_complete_filenames(str, words)
19
else
20
[]
21
end
22
end
23
24
tabs
25
end
26
27
def cmd_certs_help
28
print_line 'List Pkcs12 certificate bundles in the database'
29
print_line 'Usage: certs [options] [username[@domain_upn_format]]'
30
print_line
31
print @@certs_opts.usage
32
print_line
33
end
34
35
@@certs_opts = Rex::Parser::Arguments.new(
36
['-v', '--verbose'] => [false, 'Verbose output'],
37
['-d', '--delete'] => [ false, 'Delete *all* matching pkcs12 entries'],
38
['-h', '--help'] => [false, 'Help banner'],
39
['-i', '--index'] => [true, 'Pkcs12 entry ID(s) to search for, e.g. `-i 1` or `-i 1,2,3` or `-i 1 -i 2 -i 3`'],
40
['-a', '--activate'] => [false, 'Activates *all* matching pkcs12 entries'],
41
['-A', '--deactivate'] => [false, 'Deactivates *all* matching pkcs12 entries'],
42
['-e', '--export'] => [true, 'The file path where to export the matching pkcs12 entry']
43
)
44
45
def cmd_certs(*args)
46
return unless active?
47
48
entries_affected = 0
49
mode = :list
50
id_search = []
51
username = nil
52
verbose = false
53
export_path = nil
54
@@certs_opts.parse(args) do |opt, _idx, val|
55
case opt
56
when '-h', '--help'
57
cmd_certs_help
58
return
59
when '-v', '--verbose'
60
verbose = true
61
when '-d', '--delete'
62
mode = :delete
63
when '-i', '--id'
64
id_search = (id_search + val.split(/,\s*|\s+/)).uniq # allows 1 or 1,2,3 or "1 2 3" or "1, 2, 3"
65
when '-a', '--activate'
66
mode = :activate
67
when '-A', '--deactivate'
68
mode = :deactivate
69
when '-e', '--export'
70
export_path = val
71
else
72
# Anything that wasn't an option is a username to search for
73
username = val
74
end
75
end
76
77
pkcs12_results = pkcs12_search(username: username, id_search: id_search)
78
79
print_line('Pkcs12')
80
print_line('======')
81
82
if mode == :delete
83
result = pkcs12_storage.delete(ids: pkcs12_results.map(&:id))
84
entries_affected = result.size
85
end
86
87
if mode == :activate || mode == :deactivate
88
pkcs12_results = set_pkcs12_status(mode, pkcs12_results)
89
entries_affected = pkcs12_results.size
90
end
91
92
if export_path
93
if pkcs12_results.empty?
94
print_error('No mathing Pkcs12 entry to export')
95
return
96
end
97
if pkcs12_results.size > 1
98
print_error('More than one mathing Pkcs12 entry found. Filter with `-i` and/or provide a username')
99
return
100
end
101
102
raw_data = Base64.strict_decode64(pkcs12_results.first.private_cred.data)
103
::File.binwrite(::File.expand_path(export_path), raw_data)
104
return
105
end
106
107
if pkcs12_results.empty?
108
print_line('No Pkcs12')
109
print_line
110
return
111
end
112
113
if verbose
114
pkcs12_results.each.with_index do |pkcs12_result, index|
115
print_line "Certificate[#{index}]:"
116
print_line pkcs12_result.openssl_pkcs12.certificate.to_s
117
print_line pkcs12_result.openssl_pkcs12.certificate.to_text
118
print_line
119
end
120
else
121
tbl = Rex::Text::Table.new(
122
{
123
'Columns' => ['id', 'username', 'realm', 'subject', 'issuer', 'ADCS CA', 'ADCS Template', 'status'],
124
'SortIndex' => -1,
125
'WordWrap' => false,
126
'Rows' => pkcs12_results.map do |pkcs12|
127
[
128
pkcs12.id,
129
pkcs12.username,
130
pkcs12.realm,
131
pkcs12.openssl_pkcs12.certificate.subject.to_s,
132
pkcs12.openssl_pkcs12.certificate.issuer.to_s,
133
pkcs12.adcs_ca,
134
pkcs12.adcs_template,
135
pkcs12_status(pkcs12)
136
]
137
end
138
}
139
)
140
print_line(tbl.to_s)
141
end
142
143
case mode
144
when :delete
145
print_status("Deleted #{entries_affected} #{entries_affected > 1 ? 'entries' : 'entry'}") if entries_affected > 0
146
when :activate
147
print_status("Activated #{entries_affected} #{entries_affected > 1 ? 'entries' : 'entry'}") if entries_affected > 0
148
when :deactivate
149
print_status("Deactivated #{entries_affected} #{entries_affected > 1 ? 'entries' : 'entry'}") if entries_affected > 0
150
end
151
end
152
153
154
# @param [String, nil] username Search for pkcs12 associated with this username
155
# @param [Array<Integer>, nil] id_search List of pkcs12 IDs to search for
156
# @param [Workspace] workspace to search against
157
# @option [Symbol] :workspace The framework.db.workspace to search against (optional)
158
# @return [Array<>]
159
def pkcs12_search(username: nil, id_search: nil, workspace: framework.db.workspace)
160
pkcs12_results = []
161
162
if id_search.present?
163
begin
164
pkcs12_results += id_search.flat_map do |id|
165
pkcs12_storage.pkcs12(
166
workspace: workspace,
167
id: id
168
)
169
end
170
rescue ActiveRecord::RecordNotFound => e
171
wlog("Record Not Found: #{e.message}")
172
print_warning("Not all records with the ids: #{id_search} could be found.")
173
print_warning('Please ensure all ids specified are available.')
174
end
175
elsif username.present?
176
realm = nil
177
if username.include?('@')
178
username, realm = username.split('@', 2)
179
end
180
pkcs12_results += pkcs12_storage.pkcs12(
181
workspace: workspace,
182
username: username,
183
realm: realm
184
)
185
else
186
pkcs12_results += pkcs12_storage.pkcs12(
187
workspace: workspace
188
)
189
end
190
191
pkcs12_results.sort_by do |pkcs12|
192
[pkcs12.realm, pkcs12.username]
193
end
194
end
195
196
197
private
198
199
# @return [Msf::Exploit::Remote::Kerberos::Ticket::Storage::ReadWrite]
200
def pkcs12_storage
201
@pkcs12_storage ||= Msf::Exploit::Remote::Pkcs12::Storage.new(framework: framework)
202
end
203
204
# Gets the status of a Pkcs12
205
#
206
# @param [Msf::Exploit::Remote::Pkcs12::Storage]
207
# @return [String] Status of the Pkcs12
208
def pkcs12_status(pkcs12)
209
if pkcs12.expired?
210
'>>expired<<'
211
elsif pkcs12.status.blank?
212
'active'
213
else
214
pkcs12.status
215
end
216
end
217
218
# Sets the status of the Pkcs12
219
#
220
# @param [Symbol] mode The status (:activate or :deactivate) to apply to the Pkcs12(s)
221
# @param [Array<StoredPkcs12>] tickets The Pkcs12 which statuses are to be updated
222
# @return [Array<StoredPkcs12>]
223
def set_pkcs12_status(mode, pkcs12)
224
if mode == :activate
225
pkcs12_storage.activate(ids: pkcs12.map(&:id))
226
elsif mode == :deactivate
227
pkcs12_storage.deactivate(ids: pkcs12.map(&:id))
228
end
229
end
230
end
231
232