-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add certs command & use pkinit if kerberos tickets are not available …
…in cache
- Loading branch information
1 parent
7df6dbc
commit 2245958
Showing
8 changed files
with
324 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
module Msf::Exploit::Remote::Pkcs12 | ||
|
||
class Storage | ||
include Msf::Auxiliary::Report | ||
|
||
# @!attribute [r] framework | ||
# @return [Msf::Framework] the Metasploit framework instance | ||
attr_reader :framework | ||
|
||
# @!attribute [r] framework_module | ||
# @return [Msf::Module] the Metasploit framework module that is associated with the authentication instance | ||
attr_reader :framework_module | ||
|
||
def initialize(framework: nil, framework_module: nil) | ||
@framework = framework || framework_module&.framework | ||
@framework_module = framework_module | ||
end | ||
|
||
# Get stored pkcs12 matching the options query. | ||
# | ||
# @param [Hash] options The options for matching pkcs12's. | ||
# @option options [Integer, Array<Integer>] :id The identifier of the pkcs12 (optional) | ||
# @option options [String] :realm The realm of the pkcs12 (optional) | ||
# @option options [String] :username The username of the pkcs12 (optional) | ||
# @return [Array<StoredPkcs12>] | ||
def pkcs12(options = {}, &block) | ||
stored_pkcs12_array = filter_pkcs12(options).map do |pkcs12_entry| | ||
StoredPkcs12.new(pkcs12_entry) | ||
end | ||
|
||
stored_pkcs12_array.each do |stored_pkcs12| | ||
block.call(stored_pkcs12) if block_given? | ||
end | ||
|
||
stored_pkcs12_array | ||
end | ||
|
||
# Return the raw stored pkcs12. | ||
# | ||
# @param [Hash] options See the options hash description in {#pkcs12}. | ||
# @return [Array<Metasploit::Credential::Core>] | ||
def filter_pkcs12(options) | ||
return [] unless active_db? | ||
|
||
filter = {} | ||
filter[:id] = options[:id] if options[:id].present? | ||
filter[:user] = options[:username] if options[:username].present? | ||
filter[:realm] = options[:realm] if options[:realm].present? | ||
|
||
creds = framework.db.creds( | ||
workspace: options.fetch(:workspace) { workspace }, | ||
type: 'Metasploit::Credential::Pkcs12', | ||
**filter | ||
).select do |cred| | ||
cred.private.type == 'Metasploit::Credential::Pkcs12' | ||
end | ||
|
||
creds.each do |stored_cred| | ||
block.call(stored_cred) if block_given? | ||
end | ||
end | ||
|
||
def delete_pkcs12(options = {}) | ||
if options.keys == [:ids] | ||
# skip calling #filter_pkcs12 which issues a query when the IDs are specified | ||
ids = options[:ids] | ||
else | ||
ids = filter_pkcs12(options).map(&:id) | ||
end | ||
|
||
framework.db.delete_credentials(ids: ids).map do |stored_pkcs12| | ||
StoredPkcs12.new(stored_pkcs12) | ||
end | ||
end | ||
|
||
# @return [String] The name of the workspace in which to operate. | ||
def workspace | ||
if @framework_module | ||
return @framework_module.workspace | ||
elsif @framework&.db&.active | ||
return @framework.db.workspace&.name | ||
end | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
module Msf::Exploit::Remote::Pkcs12 | ||
|
||
class StoredPkcs12 | ||
def initialize(pkcs12) | ||
@pkcs12 = pkcs12 | ||
end | ||
|
||
def id | ||
@pkcs12.id | ||
end | ||
|
||
def openssl_pkcs12 | ||
private_cred.openssl_pkcs12 | ||
end | ||
|
||
def ca | ||
private_cred.ca | ||
end | ||
|
||
def adcs_template | ||
private_cred.adcs_template | ||
end | ||
|
||
def private_cred | ||
@pkcs12.private | ||
end | ||
|
||
def username | ||
@pkcs12.public.username | ||
end | ||
|
||
def realm | ||
@pkcs12.realm.value | ||
end | ||
end | ||
|
||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
# -*- coding: binary -*- | ||
|
||
module Msf::Ui::Console::CommandDispatcher::Db::Certs | ||
# | ||
# Tab completion for the certs command | ||
# | ||
# @param str [String] the string currently being typed before tab was hit | ||
# @param words [Array<String>] the previously completed words on the command line. words is always | ||
# at least 1 when tab completion has reached this stage since the command itself has been completed | ||
def cmd_certs_tabs(str, words) | ||
if words.length == 1 | ||
@@certs_opts.option_keys.select { |opt| opt.start_with?(str) } | ||
end | ||
end | ||
|
||
def cmd_certs_help | ||
print_line 'List Pkcs12 certificate bundles in the database' | ||
print_line 'Usage: certs [options] [username[@domain_upn_format]]' | ||
print_line | ||
print @@certs_opts.usage | ||
print_line | ||
end | ||
|
||
@@certs_opts = Rex::Parser::Arguments.new( | ||
['-v', '--verbose'] => [false, 'Verbose output'], | ||
['-d', '--delete'] => [ false, 'Delete *all* matching pkcs12 entries'], | ||
['-h', '--help'] => [false, 'Help banner'], | ||
['-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`'], | ||
) | ||
|
||
def cmd_certs(*args) | ||
return unless active? | ||
|
||
entries_affected = 0 | ||
mode = :list | ||
id_search = [] | ||
username = nil | ||
verbose = false | ||
@@certs_opts.parse(args) do |opt, _idx, val| | ||
case opt | ||
when '-h', '--help' | ||
cmd_certs_help | ||
return | ||
when '-v', '--verbose' | ||
verbose = true | ||
when '-d', '--delete' | ||
mode = :delete | ||
when '-i', '--id' | ||
id_search = (id_search + val.split(/,\s*|\s+/)).uniq # allows 1 or 1,2,3 or "1 2 3" or "1, 2, 3" | ||
else | ||
# Anything that wasn't an option is a username to search for | ||
username = val | ||
end | ||
end | ||
|
||
pkcs12_results = pkcs12_search(username: username, id_search: id_search) | ||
|
||
print_line('Pkcs12') | ||
print_line('======') | ||
|
||
if mode == :delete | ||
result = pkcs12_storage.delete_pkcs12(ids: pkcs12_results.map(&:id)) | ||
entries_affected = result.size | ||
end | ||
|
||
if pkcs12_results.empty? | ||
print_line('No Pkcs12') | ||
print_line | ||
return | ||
end | ||
|
||
if verbose | ||
pkcs12_results.each.with_index do |pkcs12_result, index| | ||
print_line "Certificate[#{index}]:" | ||
print_line pkcs12_result.openssl_pkcs12.certificate.to_s | ||
print_line pkcs12_result.openssl_pkcs12.certificate.to_text | ||
print_line | ||
end | ||
else | ||
tbl = Rex::Text::Table.new( | ||
{ | ||
'Columns' => ['id', 'username', 'realm', 'subject', 'issuer', 'CA', 'ADCS Template'], | ||
'SortIndex' => -1, | ||
'WordWrap' => false, | ||
'Rows' => pkcs12_results.map do |pkcs12| | ||
[ | ||
pkcs12.id, | ||
pkcs12.username, | ||
pkcs12.realm, | ||
pkcs12.openssl_pkcs12.certificate.subject.to_s, | ||
pkcs12.openssl_pkcs12.certificate.issuer.to_s, | ||
pkcs12.ca, | ||
pkcs12.adcs_template | ||
] | ||
end | ||
} | ||
) | ||
print_line(tbl.to_s) | ||
end | ||
|
||
if mode == :delete | ||
print_status("Deleted #{entries_affected} #{entries_affected > 1 ? 'entries' : 'entry'}") if entries_affected > 0 | ||
end | ||
end | ||
|
||
|
||
# @param [String, nil] username Search for pkcs12 associated with this username | ||
# @param [Array<Integer>, nil] id_search List of pkcs12 IDs to search for | ||
# @param [Workspace] workspace to search against | ||
# @option [Symbol] :workspace The framework.db.workspace to search against (optional) | ||
# @return [Array<>] | ||
def pkcs12_search(username: nil, id_search: nil, workspace: framework.db.workspace) | ||
pkcs12_results = [] | ||
|
||
if id_search.present? | ||
begin | ||
pkcs12_results += id_search.flat_map do |id| | ||
pkcs12_storage.pkcs12( | ||
workspace: workspace, | ||
id: id | ||
) | ||
end | ||
rescue ActiveRecord::RecordNotFound => e | ||
wlog("Record Not Found: #{e.message}") | ||
print_warning("Not all records with the ids: #{id_search} could be found.") | ||
print_warning('Please ensure all ids specified are available.') | ||
end | ||
elsif username.present? | ||
realm = nil | ||
if username.include?('@') | ||
username, realm = username.split('@', 2) | ||
end | ||
pkcs12_results += pkcs12_storage.pkcs12( | ||
workspace: workspace, | ||
username: username, | ||
realm: realm | ||
) | ||
else | ||
pkcs12_results += pkcs12_storage.pkcs12( | ||
workspace: workspace | ||
) | ||
end | ||
|
||
pkcs12_results.sort_by do |pkcs12| | ||
[pkcs12.realm, pkcs12.username] | ||
end | ||
end | ||
|
||
# @return [Msf::Exploit::Remote::Kerberos::Ticket::Storage::ReadWrite] | ||
def pkcs12_storage | ||
@pkcs12_storage ||= Msf::Exploit::Remote::Pkcs12::Storage.new(framework: framework) | ||
end | ||
|
||
end |