Kerberos.NET icon indicating copy to clipboard operation
Kerberos.NET copied to clipboard

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

Open raandree opened this issue 2 years ago • 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.

raandree avatar Jan 06 '22 21:01 raandree

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 [email protected] 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:

https://github.com/dotnet/Kerberos.NET/blob/bb10bd9f378fa30c5d746e5c5c2706944e7a0013/Kerberos.NET/Client/KerberosClient.cs#L583-L587

  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 😊

SteveSyfuhs avatar Jan 08 '22 21:01 SteveSyfuhs

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

[email protected] => 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?

[email protected] => http/webforest2.net => sql/forest1.net e.g. a => b => a

SteveSyfuhs avatar Jan 09 '22 03:01 SteveSyfuhs

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:

[email protected] => 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:

[email protected] => 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.

raandree avatar Jan 11 '22 00:01 raandree

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.

raandree avatar Jan 11 '22 00:01 raandree

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.

SteveSyfuhs avatar Jan 12 '22 00:01 SteveSyfuhs

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.

raandree avatar Jan 14 '22 20:01 raandree