Delegation is an often misunderstood technology. Here's a look at how it works.

Like all good things this is mostly correct, with a few details fuzzier than others for reasons: a) details are hard on twitter; b) details are fudged for greater clarity; c) maybe I'm just dumb.

Fundamentally the idea is simple. The user projects their identity to some remote application (i.e. authenticate). The app can't do anything with that projection other than accept it. Delegation is the act of granting that app the right to project the identity further downstream.

There's a fundamental requirement with this though. The method of projecting (authing) the user has to rely on a trusted third party. In other words using something like federation or protocols like Kerberos. This is in contrast to just passing around creds. You might wonder why.

It's because if the app in the middle has your password it can just use your password for downstream stuff, ad infinitum. Trusted third party auth protocols by their nature like having 1:1 relationships with all parties. Unfettered projection complicates things.

I keep saying projecting when I technically mean authenticating. You might wonder why. Authentication is the act of verifying a credential, where that credential could be a password, or a token, or a ticket. Most people associate it with just the first step: the user password.

With delegation there's usually multiple forms of credentials in play. However, the identity represented by those credentials remains the same. Hence projecting the identity.

There's another fundamental requirement with delegation. The middle app must be explicitly granted the right to delegate to other things. This is, I hope, somewhat obvious to everyone that it's for enforcing security. Let me explain.

Authentication is about building a trusted relationship (for some definition of trust) between two parties. When I ask Active Directory for a ticket to a web server I'm relying on the web server to trust AD, and by extension trust me. I project my identity to that server.

If the web server could take that projection and act as me for other services, that original trust is broken because AD hasn't blessed the relationship between me and that other service.

This right to delegate can come in many forms and is primarily dictated by the protocol in play. There are two basic types though. 

First, there's the right to delegate to anyone. Meaning this middle app can project to any app whatsoever and there's no (or little) limit on it.

Second, there's the right to delegate to specific apps. Meaning the middle app is only allowed to project your identity to a known list of apps. This particular method is preferred for, again I hope, obvious security reasons. Most delegation services stick to this form nowadays.

There are other forms, but they all kinda fit the above patterns.

There are different implementations of delegation. Active Directory calls it delegation. Federation protocols like WS-Fed/Trust call it ActAs. OAuth calls it delegation or impersonation, Azure AD calls it on-behalf-of. Let's look at AD and Azure AD.

Many folks know about delegation because of this screen in Active Directory. Many people have come to hate this screen. This screen applies to that middle web server.

User A projects their identity to web server B, and B projects A's identity to web service C. A => B => C. The screen from before applies to B. That screen lets you control what form of delegation you want to allow.

The first option is obvious. B isn't allowed to delegate to anyone, period.

The second option is mode 1: B can delegate to whoever it wants. Could be C, could be D, could be Y, could be Z.

The third option is mode 2: B can only delegate to C if it's in the list below it.

But the third option has a catch: you can only delegate when using Kerberos. So option 3b was added: you can delegate to whoever is in this list without authenticating. Whaaaaaat the eff? We'll come back to this.

So AD relies on Kerberos for delegation. The first option is called unconstrained delegation. This is the easiest mode to wrap your head around because what you're doing is giving your client TGT to the web server, so the server can do whatever it wants with it.

That's literally all it's doing. The client is informed that the server uses unconstrained delegation so the client copies the user TGT, stuffs it into the AP-REQ authenticator, and fires it off to the target server.

Once the server receives the AP-REQ, it decrypts it and creates a logon session and ticket cache. The TGT is stuffed into the cache. When the server needs to make a call as the user it uses the TGT to request a ticket to whatever is next. How Windows Single Sign-On Works (

Incidentally this is why Credential Guard blocks unconstrained delegation on the client. Unconstrained delegation is Stealing-your-TGT-as-a-Service. How Windows Defender Credential Guard Works (

Next is what's called constrained delegation. You provide a list of services web server B is allowed to project the identity to. You might think okay, it's just like unconstrained where they copy the TGT over, and the web server has to ask what it can project to, but it's not.

We don't live in a world where we can blindly trust web server B. If B is even a little untrustworthy and it managed to get a hold of a TGT for the user, it's game over for that user. We in no uncertain terms want that TGT loose on the network. So what do?

Well, we don't do Kerberos. 😲

More precisely what we do is use a protocol that is an extension to Kerberos called Service-for-User [MS-SFU] or s4u. [MS-SFU]: Kerberos Protocol Extensions: Service for User and Constrained Delegation Protocol | Microsoft Docs

S4u is a way for web server B to safely request a ticket to service C on behalf of the user. It does this by using the service ticket user A handed to server B as an evidence ticket. Huh?

Imagine you're web server B. You have an identity "webserverb$". You have a password, and AD knows who you are. You can receive Kerberos service tickets. AD encrypts them to your password. Nothing particularly fancy about this. It's basic Kerberos. A bit about Kerberos (

Now, you receive a ticket from user A and decrypt it. You create the SSO logon session for the user and tell the web app it's user A. Now the web app wants to communicate to service C as that user so it asks for a ticket to service C.

Web server B has a logon session for user A, so web server B checks for a TGT. No TGT because we're constrained. Okay, let's try S4U. To do this, the web server makes a request to the domain controller for a service ticket to C.

The server has an identity on the network, therefore it has a TGT, and it's allowed to ask for service tickets. Again, plain old Kerberos. However, the identity in the service ticket would be server B. So server B adds the service ticket it received from A as an evidence ticket.

Well now this is interesting to the domain controller. It receives a ticket request from B for C and also a service ticket from A FOR B, plus some flags to say do S4U. So the DC checks the delegation list. Is B allowed to delegate to C? Yes, okay give B a ticket to C as A.

I promise you aren't having a stroke. The way to follow this is that B needs to prove it's allowed to get the ticket to C on behalf of A, otherwise B could be evil and impersonate whoever they want.

Once web server B receives the ticket, it bubbles it up to the web app, and the web app stuffs the ticket into the request for web service C. C receives it, decrypts it, and sees it's for user A. And we're back to how SSO works. How Windows Single Sign-On Works (

But remember how I said there's that other mode, where it doesn't require authentication? Yeah about that. We call it Protocol Transition. This exists so web server B can authenticate users using a protocol Active Directory doesn't understand, while still impersonating them.

Consider if web server B supports SAML. AD doesn't understand SAML, but the web app wants to impersonate the identity projected through SAML so when they call web service C they're acting as user A. This can work through protocol transition.

It uses the same protocol extension -- S4U. The difference is that the evidence ticket isn't real. Or rather, it's an evidence ticket issued to the server itself. Huh?

There's a sub-protocol in S4U called service-for-user-to-self or s4u-self. This is an incredibly useful protocol, because an application can request a service ticket to *itself* on behalf of any user. It's useless for auth, but still contains the user identity. Give it a try.

new-object ""

It's a TGS-REQ to me, with some bits that says please get on behalf of bob.

So for just a name you get a ticket to yourself. A ticket to yourself can get your constrained delegation. Put the two together and you have s4u-proxy, AKA protocol transition!

Of course, here's the problem: protocol transition is increeeedibly dangerous. You've just granted web server B the rights to impersonate whoever they want. That's worse than unconstrained delegation. Oops. As such you've effectively granted web server B the right to act as a DC.

This is, incidentally why selecting that option tends to make things "just work".

Please, please, pleeeeeease be incredibly careful if you choose to use it. Any server configured for protocol transition should have the same security as a DC. No joke.

Anyway, there's one more thing with AD delegation that isn't on that screen. It's something called Resource-Based Constrained Delegation. Huh?

It's basically regular constrained delegation, but the approval list is flipped. The idea is that instead of saying web server B is allowed to delegate to web service C, it says web service C is allowed to be delegated from web server B.

This actually makes more sense from a security perspective. The thing you're protecting is web service C, so why are the controls on B? The armed money delivery transports don't dictate which vaults they can go in to, the banks dictate which transports are allowed into the vault.

The other useful thing about RBCD is more of a practical one. Regular constrained delegation doesn't work across forests because of how the protocol happens to work. There isn't enough authz details in it to identify the service because SPNs are only unique within forests.

RBCD relies on SIDs. Web service C has a list of SIDs that can delegate to it, and when web server B makes a request to the other forest, it includes web server B's SID.

But enough about that. Let's look at another form of delegation: Azure AD on-behalf-of.

Azure AD-based apps are an evolution of AD-based apps. They still have similar, or higher, security requirements. As web server B I want to project user A's identity to service C.

The difference is in the choice of protocols. Azure AD relies on OAuth2 for authentication and authorization. A client authenticates user A and gets a JWT access token signed by AAD to web server B. Web server B validates the ticket and the app now knows it's user A, so says AAD.

But the web app wants to access another resource, say Microsoft Graph. The web app can use it's own identity, or it can use the identity of the user. You could keep it simple and use the app identity, but then you need to grant that identity access to all users resources.

That's a fairly broad set of permissions, and attacking that app gets you everything in Graph. That's bad.

But if you use the user identity, the app doesn't need anything special, and the app can operate as just that single user. That's good.

Protocol-wise this is an extension to OAuth called on-behalf-of. Microsoft identity platform and OAuth2.0 On-Behalf-Of flow - Microsoft identity platform | Microsoft Docs

Not surprisingly it kinda sorta acts a lot like s4u. User A gets a token to server B from AAD. Server B has it's own identity and authenticates to AAD, providing user A's access token to B as an evidence token (assertion).

AAD checks the list of apps that B is allowed to delegate to, and if C is in the list, it issues a token to B on behalf of A for C.

This is actually pretty cool. First, it's way easier to follow along because it's all HTTP instead of binary encoded ASN.1 goop. Second, it supports multiple token types. Ergo it supports multiple protocols.

That means you can transition between OAuth and (say) SAML without turning your app into a major target, because every exchange and token is still authenticated. Awesome.

Third, you can enforce consent. This shifts authorization a bit so the user is now in control over who can project and forward their identity. That's a nice privacy win.

So obviously there's been a delegation evolution. From AD unconstrained, all the way to OAuth on-behalf-of. Hopefully it's gotten easier and more secure along the way. /fin

User Account Control is a local authorization mechanism. Can you bypass it by going through the network? No. Here's why.

Like all good things this is mostly correct, with a few details fuzzier than others for reasons: a) details are hard on twitter; b) details are fudged for greater clarity; c) maybe I'm just dumb.



You might be saying "well duh! That's what UAC does!" 

But we're talking SMB, so it's over the network, therefore it's Kerberos, and Kerberos can't doesn't do UAC over the network because Kerberos doesn't contain local authz information, and UAC is all about local authz.

Note: SMB loopback actually uses NTLM. But let's not let details get in the way of a good explainer. (NTLM does work similarly though)

What's the difference between \\localmachine\c$ and \\othermachine\c$ then? Actually, nothing.

Some background: when a client needs a kerb ticket to something it asks Windows. Windows creates an AP-REQ, which is the requested ticket plus authenticator.

The ticket is created by the KDC. The client can't see inside it, and can't manipulate it. It's opaque. However, the client can ask the KDC to include extra bits in the ticket. See here for fiddly details: A bit about Kerberos (

These extra bits are just a way to carry information from the client to the target service during authentication. As it happens one of the things the client always asks to include is a machine nonce.

See, when the client asks the client Kerberos stack for a ticket, the stack creates a random bit of data and stashes it in LSA and associates it to the currently logged on user. This is the nonce. This nonce is also stuck in the ticket, and then received by the target service.

The target service, i.e. the same Windows Kerberos stack, knows about this nonce and asks LSA if it happens to have this nonce stashed somewhere. If it doesn't, well, then it's another machine and just carry on as usual.

However, if it does have this nonce, LSA will inform the Kerberos stack that it originally came from user so and so, and most importantly that the user was *not* elevated at the time.

What does that mean? Not elevated?

This is UAC -- User Account Control. UAC is the thing that *tries* to prevent you from doing something stupid with admin privileges.

See, when you log onto Windows you're granted a set of local privileges. Some of these give you lots of control over the system, like local administrator. UAC is a way of preventing you from doing silly things as administrator unless you reeeeeeally want to.

The way it works is conceptually simple. During login you're given two NT tokens. The first is your high privilege token. It contains all those special privileges. The second is your low privilege token. Everything you do uses your low privilege token by default.

So when you do need to do something with higher privileges you need to ask Windows "Hey! I need the high privilege token, pleeeeeeeeeeeeease".


Anyway, LSA knows all about these tokens, so when it receives this Kerberos ticket and contains that nonce and finds the user, voila it has a low privilege token.

Now the Kerberos stack makes a copy of the ticket contents for it's own SSO session and whenever you do anything in that session, say try write to c$\windows\system32, the authorization system checks the privileges and in this case says nooooope. How Windows Single Sign-On Works (

So as a result you don't get to bypass UAC by going through the network. /fin

Like all good things this is mostly correct, with a few details fuzzier than others for reasons: a) details are hard on twitter; b) details are fudged for greater clarity; c) maybe I'm just dumb. Note this thread was created a while back. Just catching up.

You log into Windows with your FIDO key. Our cloud auth package in Windows (CloudAP) knows how to handle FIDO auth and does the handshake with That returns an OAuth PRT and a special cloud-minted Kerberos Ticket Granting Ticket.

The TGT is encrypted using the session key agreed to earlier, so only the client machine can process it. Windows knows this key, and knows that this TGT is special, so it triggers a TGS-REQ to a nearby on-prem KDC using this special TGT as the evidence ticket.

Prior to all this you had to register your domain with AAD so it could use FIDO, and in doing so what we did was we created a special Read-Only Domain Controller and RODC krbtgt secret. That secret got synced to AAD and is what we use to encrypt the special TGT.

When your on-prem KDC receives this special TGT it knows that it's special and uses the RODC krbtgt_### secret to decrypt it. The KDC exchanges it for a real TGT and returns it to the client. The client is now in a steady state as far as Kerberos is concerned.

But what about NTLM? Guh. Well, it turns out we can't kill NTLM yet, so when the client had this special TGT in hand and knew to exchange it for a real TGT, it included a special flag on the TGS-REQ to ask the KDC to include some additional SSO creds in response.

Those SSO creds are returned to the client and handed off to the various SSO packages in Windows, one being NTLM, and those packages handle them as they would any other credential. Voila, now they know how to handle NTLM and any other supported SSO protocols. Magic!

Can't forget the pups.