Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
raandree opened this issue Jan 6, 2022 · 7 comments

Comments

@raandree
Copy link

raandree commented Jan 6, 2022

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 raandree added the bug label Jan 6, 2022
@SteveSyfuhs
Copy link
Collaborator

SteveSyfuhs commented Jan 8, 2022

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 😊

@SteveSyfuhs
Copy link
Collaborator

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

@raandree
Copy link
Author

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.

@raandree
Copy link
Author

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.

@SteveSyfuhs
Copy link
Collaborator

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 SteveSyfuhs changed the title 'Invalid checksum' when requesting a ticket for a service in a trusted forest Resource-based constrained delegation fails across forests (was 'Invalid checksum' when requesting a ticket for a service in a trusted forest) Jan 12, 2022
@raandree
Copy link
Author

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.

@DanielMGoldberg
Copy link

Hey! Just checking in to see if there are any updates on the fix for the resource-based constrained delegation issues across forests. I know it’s a tricky problem, but I’m curious if there’s a timeline for when it might be resolved. Thanks for all your work on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants