dotnet/Kerberos.NET

Resource-based constrained delegation fails across forests (was 'Invalid checksum' when requesting a ticket for a service in a trusted forest)

raandree opened this issue · 6 comments

Describe the bug
When requesting a ticket for a service in another forest, decrypting the referral ticket results in this exception:

System.AggregateException
  HResult=0x80131500
  Message=One or more errors occurred.
  Source=mscorlib
  StackTrace:
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at System.Threading.Tasks.Task`1.get_Result()
   at KerbTest.Program.Main(String[] args) in C:\Users\Install.forest2\Desktop\KerbTest\KerbTest\Program.cs:line 14

  This exception was originally thrown at this call stack:
    [External Code]
    KerbTest.Program.MainAsync(string[]) in Program.cs

Inner Exception 1:
SecurityException: Invalid checksum

To Reproduce

//constrained delegation within the same forest works
var rst = new RequestServiceTicket();
rst.ServicePrincipalName = "MSSQLSvc/F2SQL1.forest2.net";
rst.S4uTicket = tgsUserForKerbTestService.Ticket;
var tgsServiceForF2SQL1 = await clientService.GetServiceTicket(rst);

//requesting a ticket for a resoruce in a trusted forest does not work (resource-based delegation)
rst = new RequestServiceTicket();
rst.ServicePrincipalName = "MSSQLSvc/F1SQL1.forest1.net";
rst.S4uTicket = tgsUserForKerbTestService.Ticket;
var tgsServiceForF1SQL1 = await clientService.GetServiceTicket(rst);

I have uploaded the full test project to https://github.com/raandree/KerbTest.

Expected behavior
Being able to request a ticket for a resource in another forest and follow the referral ticket.

Screenshots
I added two network traces, good and bad, to https://github.com/raandree/KerbTest.

Additional context
If you want to relay this in a ready-build lab, you may want to use the lab scripts provided in https://github.com/raandree/KerbTest. The require AutomatedLab.

Some preliminary notes:

  1. This is not an issue with generic cross-forest requests, as those work fine. E.g. this shows it's fine (I just tested against a set of prod forests).
    a. bruce> kinit user@domain.com
    b. bruce> klist get cifs/share.otherforest.com
  2. The issue is specific to the S4U flow. That is causing it do something unexpected.
  3. The issue is likely this line:

encKdcRepPart = serviceTicketCacheEntry.KdcResponse.EncPart.Decrypt(
serviceTicketCacheEntry.SessionKey.AsKey(),
serviceTicketCacheEntry.SessionKey.Usage,
d => KrbEncTgsRepPart.DecodeApplication(d)
);

  1. With any luck it's just the wrong usage type, though I admit I don't know why it would be, so this requires a bit of investigation.
  2. This automated lab thing you're using looks amazing 😊

Okay, more progress. I've got an environment up and running and the following constrained delegation works:

user@forest1.net => http/web.forest2.net => sql/sql.forest2.net e.g. a => b => b

This is plain old Server 2003 style constrained delegation where the middle box cannot cross forests. I'm guessing this works for you?

I'm guessing it's this scenario that does not?

user@forest1.net => http/webforest2.net => sql/forest1.net e.g. a => b => a

Thanks for your work on this. The demo code I have shared on https://github.com/raandree/KerbTest/ makes use of two delegation scenarios. The first one uses asks is:

dev@forest2.net => http/kerbtest.forest2.net => MSSQLSvc/F2SQL1.forest2.net

This is the code:

var rst = new RequestServiceTicket();
rst.ServicePrincipalName = "MSSQLSvc/F2SQL1.forest2.net";
rst.S4uTicket = tgsUserForKerbTestService.Ticket;
var tgsServiceForF2SQL1 = await clientService.GetServiceTicket(rst);

The second scenario is:

dev@forest2.net => http/kerbtest.forest2.net => MSSQLSvc/F1SQL1.forest1.net
rst = new RequestServiceTicket();
rst.ServicePrincipalName = "MSSQLSvc/F1SQL1.forest1.net";
rst.S4uTicket = tgsUserForKerbTestService.Ticket;
var tgsServiceForF1SQL1 = await clientService.GetServiceTicket(rst);

This still fails with version 4.5.140.

BWT, I have forgotten to add the lab setup script. Now available 01 Kerberos Lab.ps1. If you think that AutomatedLab can help you, there are a lot more Sample Scripts.

I've been using the multi-AD forest lab with great success for this problem. Big fan of this tooling.

Here's the current state of things:

  1. The crypto exception was likely unrelated to any constrained delegation logic and actually caused by the internal caching behavior for realm referrals. That's since been fixed.
  2. Regular constrained delegation across domains within a forest works correctly.
  3. I've enhanced the API so it's much easier to use:
var authenticator = new KerberosAuthenticator(this.ServicePrincipalSamAccountName, keytab, config, logger);

var identity = await authenticator.Authenticate(serviceTicket.ApReq.EncodeGssApi()) as KerberosIdentity;

var backend = await identity.GetDelegatedServiceTicket("host/backend");
  1. I've added a new bruce tool to let you work with delegation a bit easier. Basically, move left to right, get a ticket to http/web, decrypt it using the --spn-sam account where the value is the sAMAccountName and --spn-pass as it's account password, and then --spn-sam will get a delegated ticket to MSSQLSvc/sql.
bruce>kcd --spn http/web.forest2.net --spn-sam web --spn-pass P@ssw0rd! --delegated MSSQLSvc/sql.forest2.net

E.g.

Invoke-LabCommand -ActivityName 'Create Forest 2 service users' -ScriptBlock {
    $password = "P@ssw0rd!" | ConvertTo-SecureString -AsPlainText -Force
    $su = New-ADUser -Name "web" -AccountPassword $password -Enabled $true -PassThru
    $su | Set-ADUser -Add @{
        servicePrincipalName = 'http/web.forest2.net', 'http/web', 'host/web.forest2.net'
    } -Replace @{
        'msds-SupportedEncryptionTypes' = 28
    }
} -ComputerName $f2dc
  1. Resource-based constrained delegation within a forest also works.
  2. Cross-forest RBCD does not work. It requires a fundamentally different implementation of the protocol. The error you receive will at least be from the KDC instead of a random cryptographic error. I will at some point add support, but I have to learn a bit more about how it works first because it's painfully complicated.

Thanks for the hard work. I can confirm that the KDC command in bruce works perfectly as long as the delegation scenario does not leave the forest.