Kerberos multiple hops

You all remember the maximum 2 hops for Kerberos right.. well in Microsoft land it works a little different and it is possible to create a multiple tier Kerberos delegation structure.

 

Basically we want the following to happen:

 

Client->IIS1->IIS2->IIS3->IIS4 where all hops require Kerberos authentication

 

In this case, IIS1, IIS2 and IIS3 need to be trusted for delegation. In my test lab I’ve used (http://support.microsoft.com/kb/314404) for the setup..

 

Reach To IIS server on IIS1

User Id = ROOTDOMAINAdministratorThe Negotiate method was used!

The user was logged on using Kerberos.

 

Attempt to connect to http://IIS2.rootdomain.local/default.asp by using ServerXMLHTTP

Receiver Status Text: OK (200)

 

Reach To IIS server on IIS2

User Id = ROOTDOMAINAdministratorThe Negotiate method was used!

The user was logged on using Kerberos.

 

Attempt to connect to http://IIS3.rootdomain.local/default.asp by using ServerXMLHTTP

Receiver Status Text: OK (200)

 

Reach To IIS server on IIS3

User Id = ROOTDOMAINAdministratorThe Negotiate method was used!

The user was logged on using Kerberos.

 

Attempt to connect to http://IIS4.rootdomain.local/default.asp by using ServerXMLHTTP

Receiver Status Text: OK (200)

 

Reach To IIS server on IIS4

User Id = ROOTDOMAINAdministratorThe Negotiate method was used!

The user was logged on using Kerberos.

 

So the log file shows you it all works!.. please keep the above information in  mind when designing security for your applications!

 

 

(explanation: In Microsoft land, each trusted for delegation object requests tickets on behalf of the user instead of using the forwarded ticket from the user). If a trusted object requests a ticket for the next service which is also trusted for delegation the forwardable flag is not cleared, therefore the next hop can re-request tickets on behalf of the user for the next hop. )

 

 

So if we look at the traffic on IIS2 (called SQL01 here and IIS3 is called IS02).. you see IIS2 server requesting a ticket on behalf of the Administrator (user account) for the IIS3 service (IS02.rootdomain.local)

 

 

*****************************************************************

Constraint Delegation

So you want to set it up for constraint delegation.. users who’s computers are not on the domain must be able to login to the same application.. that’s where things go wrong!..

Reach To IIS server on SP01
User Id = ROOTDOMAINAdministratorThe Negotiate method was used!
The user was logged on using Kerberos.
Please do not refresh this page.
 �
Attempt to connect to http://SQL01.rootdomain.local/default.asp by using ServerXMLHTTP
Receiver Status Text: OK (200)

Reach To IIS server on SQL01
User Id = ROOTDOMAINAdministratorThe Negotiate method was used!
The user was logged on using Kerberos.
Please do not refresh this page.
  �
Attempt to connect to http://IS02.rootdomain.local/default.asp by using ServerXMLHTTP
Receiver Status Text: Unauthorized (401)
You are not authorized to view this page
 

looks like we can only have two hops.. lets have a look …..

We set the First IIS server to use Constraint Delegation (all services) towards the 2nd IIS server. Again, we take a look at the network..

In the network layer we see the challenge from the IIS webserver (highlighted).. and then NO Kerberos request for a new ticket on behalf of the user.. the servers reconnects to the IIS server and (192.168.10.3) cannot provide it credentials for Kerberos. The webserver switches over to NLTM (Challenge). The server can only answer to the NTLM challenge with the server’s key (or Service Account) and therefore breaking the delegation model. (access denied message)

So the question lies in what the first IIS webserver does when requesting a ticket on behalf of the user in Constraint and unConstraint delegation. Again we switch over to the network capture..

In a non constraint delegation model, the 1st webserver requests a ticket with options: 4081000 (forwardable, Renewable, Canonicalize) for the 2nd webserver (url sql01.rootdomain.local).

Now as we enable constraint delegation (allowing users to logon with another authentication protocol) we see the difference:

 

The ticket itself has the Contrained Delegation flag set! and that should probably be why this ticket cannot be used to get another ticket on behalf of the user on the 2nd webserver.

* The quest continues..

I did another packetsniff let’s take a look at what happens in order:

The client connects to the webserver and retrieves an unauthorized:
   17 -7172.250000 192.168.10.2 192.168.10.14 HTTP HTTP/1.1 401 Unauthorized  (text/html)
The client requests is Ticket Granting Ticket
   19 -7172.203125 192.168.10.14 192.168.10.1 KRB5 AS-REQ
         KDCOptions: 40810010 (Forwardable, Renewable, Canonicalize, Renewable OK)
         Client Name (Principal): Administrator
         Realm: ROOTDOMAIN.LOCAL
The client receives it’s AS
   20 -7172.171875 192.168.10.1 192.168.10.14 KRB5 AS-REP

Then the client wants it’s TGS (service ticket)
   21 -7172.156250 192.168.10.14 192.168.10.1 KRB5 TGS-REQ
         KDCOptions: 40800000 (Forwardable, Renewable)
         Server Name (Service and Instance): HTTP/sp01.rootdomain.local
         Name: HTTP
         Name: sp01.rootdomain.local
The client receives the ticket
   22 -7172.140625 192.168.10.1 192.168.10.14 KRB5 TGS-REP
And re-connect to the webserver with authorization
   24 -7172.109375 192.168.10.14 192.168.10.2 HTTP GET / HTTP/1.1�
      [truncated] Authorization: Negotiate IFCQYGKwYBBQUCoIIE/TCCBPmgJDAiBgkqhkiC

Let’s take it from there and switch to the SP01.rootdomain.local server
The first thing we see is our LDP_DEEPSPACE check (see other posting)
19 -7193.078125 192.168.10.2 192.168.10.1 TCP ltp-deepspace > kerberos [SYN] Seq=0 Win=65535 Len=0 MSS=1460

Although we gave the kerberos ticket for the user already, the next thing we see is another request:
   23 -7193.062500 192.168.10.2 192.168.10.1 KRB5 TGS-REQ
      KDCOptions: 40830000 (Forwardable, Renewable, Constrained Delegation, Canonicalize)
      Server Name (Service and Host): host/sp01.rootdomain.local
         Name-type: Service and Host (3)
         Name: host
         Name: sp01.rootdomain.local
      

Note that 192.168.10.2 IS the host itself and that the host is requesting a ticket for it’self!, lets compare that to the unconstraint request in another packet sniff: The first packet we see there is the request for the next hop, so the client itself is NOT requesting a host ticket when unconstraint delegation is used:>

   28 -7198.734375 192.168.10.2 192.168.10.1 KRB5 TGS-REQ
      KDCOptions: 40810000 (Forwardable, Renewable, Canonicalize)
      Server Name (Service and Instance): HTTP/sql01.rootdomain.local
         Name-type: Service and Instance (2)
         Name: HTTP
         Name: sql01.rootdomain.local

Followed by another request immediately:
   30 -7198.734375 192.168.10.2 192.168.10.1 KRB5 TGS-REQ
      KDCOptions: 60810010 (Forwardable, Forwarded, Renewable, Canonicalize, Renewable OK)
       Server Name (Service and Instance): krbtgt/ROOTDOMAIN.LOCAL�
         Name-type: Service and Instance (2)
         Name: krbtgt
         Name: ROOTDOMAIN.LOCAL

Back to the original constraint delegation packet.. after requesting a ticket for the host, an error is thrown back:�
   25 -7193.031250 192.168.10.1 192.168.10.2 KRB5 KRB Error: KRB5KDC_ERR_BADOPTION NT Status: STATUS_NO_MATCH
      error_code: KRB5KDC_ERR_BADOPTION (13)
      Server Name (Service and Host): host/sp01.rootdomain.local
         Name-type: Service and Host (3)
         Name: host
         Name: sp01.rootdomain.local
      e-data PA-PW-SALT
            Value: 720200C00000000003000000
            NT Status: STATUS_NO_MATCH (0xc0000272)

Appearantly we are not receiving the kerberos ticket, and there is no retry.. the next packet is the next hop request:
   34 -7192.640625 192.168.10.2 192.168.10.3 HTTP GET /default.asp HTTP/1.1
   37 -7192.062500 192.168.10.3 192.168.10.2 HTTP HTTP/1.1 401 Unauthorized  (text/html)

And the request for the next hop ticket:
   43 -7192.046875 192.168.10.2 192.168.10.1 KRB5 TGS-REQ
      KDCOptions: 40830000 (Forwardable, Renewable, Constrained Delegation, Canonicalize)
      Server Name (Service and Instance): HTTP/sql01.rootdomain.local
         Name-type: Service and Instance (2)
         Name: HTTP
         Name: sql01.rootdomain.local

The SQL01 hop tries the same request for the host ticket but receives the same error:
   
34 -7186.406250 192.168.10.1 192.168.10.3 KRB5 KRB Error: KRB5KDC_ERR_BADOPTION NT Status: STATUS_NO_MATCH

Now lets investigate the ERRor..

I’ve enabled kerberos error logging (debug level) and found:

(

How to turn on debug output

There are a number of ways to view the debug output from Kerberos. The easiest way is by logging the debug output to a file and then opening this file in Notepad.

 

1. Click Start, click Run, type regedit.exe, and then press ENTER.   

  Caution
Incorrectly editing the registry might severely damage your system. Before making changes to the registry, you should back up any valued data on the computer.

2. Open the following registry key:   

HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlLsaKerberosParameters

3. Create the following entry:   

Value: KerbDebugLevel

Type: DWORD

Data: c0000043 (this value will print the most standard set of debug messages. Try it first. If you still want to see more output, set it to ffffffff).

4. Create the following entry in the same registry location:   

Value: LogToFile

Type: DWORD

Data: 1

5. Reproduce the error
6. Open the file lsass.log, located in the System32 directory of your Windows folder. You can find the debug output inside this file.

 

)

384.488> Kerb-Trace: KerbCreateTokenFromTicket for ROOTDOMAINAdministrator, (null)
384.488> Kerb-Trace: SpAcceptLsaModeContext called KerbMapContext ContextAttributes 0x5, 0
384.500> Kerb-SPN: Found in SPN Cache 00111770 384.500> Kerb-Bnd: KerbInsertBinding binding cache disabled
384.500> Kerb-Bnd: Calling kdc 192.168.10.1 for realm ROOTDOMAIN.LOCAL
384.500> KSupp-Trace: Calling KDC: 192.168.10.1
384.472> Kerb-Trace: KerbCreateTokenFromTicket for ROOTDOMAINAdministrator, (null)
384.472> Kerb-LSess: KerbCreateLogonSessionFromTicket creating logon session for 0:0x29766, accepting 0:0x3e4, client Administrator@ROOTDOMAIN.LOCAL
384.472> Kerb-Trace: SpAcceptLsaModeContext called KerbMapContext ContextAttributes 0x5, 0
384.600> Kerb-Bnd: KerbInsertBinding binding cache disabled
384.600> Kerb-Bnd: Calling kdc 192.168.10.1 for realm ROOTDOMAIN.LOCAL
384.600> KSupp-Trace: Calling KDC: 192.168.10.1
384.480> Kerb-Cred: Acquiring cred, S4U required
384.600> Kerb-Warn: KerbGetTgsTicket failed to unpack KDC reply: 0x3c
384.600> KSupp-Warning: KerbUnpackData failed to unpack typed data, trying error method data
384.600> KSupp-Error: KerbUnpackErrorData received failure from kdc 0xd KLIN(0) NTSTATUS(0xc0000272)
384.484> Kerb-SPN: Found in SPN Cache 00111770 384.484> Kerb-S4u: Trying S4UProxy for ls 0009E9F8
384.600> Kerb-S4u: No match on S4UTarget
384.600> Kerb-Warn: Failed S4Uproxy request c0000272(8)
384.484> Kerb-Bnd: KerbInsertBinding binding cache disabled
384.484> Kerb-Bnd: Calling kdc 192.168.10.1 for realm ROOTDOMAIN.LOCAL
384.484> KSupp-Trace: Calling KDC: 192.168.10.1

So the request for a ticket on behalf of the client fails, therefore the next hop SQL01 can still be reached by the client’s initial ticket, however that ticket is limited to only 2 hops. This is also seen on the client (XP) side when requesting the active tickets:

Cached Tickets: (2)

   Server: krbtgt/ROOTDOMAIN.LOCAL@ROOTDOMAIN.LOCAL
      KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)
      End Time: 8/15/2008 21:07:02
      Renew Time: 8/22/2008 11:07:02
   Server: HTTP/sp01.rootdomain.local@ROOTDOMAIN.LOCAL
      KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)
      End Time: 8/15/2008 21:07:02
      Renew Time: 8/22/2008 11:07:02

So after the 2nd hop (SQL) the initial ticket is not valid anymore and cannot be forwarded to the next hop. Access Denied is the result..