Kerberos.NET
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)
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:
- 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
- The issue is specific to the S4U flow. That is causing it do something unexpected.
- The issue is likely this line:
https://github.com/dotnet/Kerberos.NET/blob/bb10bd9f378fa30c5d746e5c5c2706944e7a0013/Kerberos.NET/Client/KerberosClient.cs#L583-L587
- 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.
- 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:
[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
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.
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:
- 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.
- Regular constrained delegation across domains within a forest works correctly.
- 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");
- 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
- Resource-based constrained delegation within a forest also works.
- 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.