Active Directory Spotlight: Trusts — Part 1. The Mechanics

This spotlight is intended to shed some light on Active Directory Trusts, the value they bring, the risk they can contain and how to evaluate trust relationships. We are certainly not the first ones to look at Active Directory trust relationships, but we believe there are still some misconceptions out there about AD trusts, which we hopefully can clear up a bit.

A side note on the details and this text-heavy post:

This first part of this AD spotlight specially dedicated to describe how AD trusts work under the hood, determine what a trust relationship really consists of and to clarify common misconceptions with trusts. If you’re already know your bits about AD trusts feel free to jump into "Part 2 — The Operational Guide" to see all of this in action.

Alright let's get to it...

What are Active Directory Trusts and what do they look like?

In the Active Directory Domain Service resources and objects are managed within a Domain, which simply is a boundary container for all the resources that belong to a certain group, e.g. the fictional organization "SafeAlliance", which will be the target for this spotlight.

Within the Active Directory Domain of SafeAlliance, that we will name SafeAlliance.local, we can manage resources like users or computers and create rules for how these should be configured. For all of this AD Trusts are not needed, but there might come a time when the SafeAlliance organization grows, expands and needs to restructure in order to allow for effective workflows.

The organization decides to build sub-groups and creates a new Active Directory Domain for each subsidiary to allow for a separate, dedicated management environment for each department.

As a result of this the Active Directory environment (Forest), that used to consist of the single domain SafeAlliance.local is now expanded and contains two new Child-Domains, which we name "Shield.SafeAlliance.local" and "RD.SafeAlliance.local". The Active Directory Forest of the organization can be visualized as follows:

Basic Trust Relationship

By adding these two new Child domains the organization (implicitly) sets up the following two AD Trust relationships:

  • A ParentChild trust relationship between SafeAlliance.local and Shield.SafeAlliance.local
  • A ParentChild trust relationship between SafeAlliance.local and RD.SafeAlliance.local

Alright we made a first step with AD trusts by, but why the fuzz about it? These ParentChild relationships seem pretty straight forward and just what any SysAdmin deals with every day, so why care?

Well, actually these two relationships are: Forest-Wide Authentication, Transitive, SID History Enabled, TGT Delegation Enabled, ParentChild trusts... Or in other words: Default (unconfigured) ParentChild trusts. All of these new terms - which exist for all trust relationships - will be introduced as trust characteristics shortly and we’ll take a closer look at those.

So there’s more under the hood with trusts and those trusts characteristics (that were just mentioned) play a major role in how an attacker could abuse AD trusts to take control over certain resources or an entire forest.

Let’s get into the next gear and take a look under the hood of AD Trusts to understand how an attacker could leverage these trusts.

Trust Objects

When an AD trust is created, two new objects are created on both ends (meaning in both domains) of the relationship:

  • A Trust Account, which holds the so-called Trust Key, which is required for authentication attempts made across the trust boundary
  • A Trust Domain Object (TDO), which is an AD object that holds various information about the configuration of the trust.

Let’s start to unravel the inner workings of trusts by inspecting these two objects...

Trust Account

If a trust relationship is established, a Trust Account is created on each side of the trust.

Let’s make this visual and get a view on what this trust account looks like.

Trust Account

If we have a trust relationship between SafeAlliance.local and Shield.SafeAlliance.local, then we can find the trust account on the SafeAlliance.local side of the relationship within the "Users" Container of the Domain Controller, as shown above.

From this screenshot we can observe that:

  • The trust account is named after the trust partner (in this case Shield) followed by a "$"-sign.
  • The trust account is a regular Active Directory user object

This trust account is required for authentication requests made between the trust partners. Let me try to explain how this works based on the Kerberos authentication flow:

While you’re inside your domain you can request access to resources by using the krbtgt ticket (TGT) that was issued for your user by your Key Distribution Center (KDC) installed on your Domain Controller (DC), which is encrypted with the password hash of the krbtgt user. This ticket can then in turn be used to request service tickets, for example to access an SMB share.

Side note: If you’re not too familiar with the Kerberos authentication flow, you can find a fresh-up of the Kerberos flow in my post: Kerberos Authentication: A Wrap Up

This Kerberos flow also works across trust boundaries, for example when you want to access an SMB share within a domain that trusts you. However, in this scenario you can’t use the same TGT that you used to access a share in your domain, because your trust partner doesn't know the password of the krbtgt user from your domain. Therefore, a new entity is required that encrypts TGTs that your trust partner can process. This is what the trust account is used for. When you want to access a resource over a trust boundary you will get a so-called 'Inter-realm' TGT which is encrypted with the password hash of the trust account, which in this case is called SHIELD$.

Side note: The password of the trust account is also referred to as trust key.

To verbally complete the picture here: The other side of the trust also has a trust account (within its User-Container), which is named SafeAlliance$.

Trust Domain Object

Alongside with a trust account a so-called Trust Domain Object (TDO) is created on each side of the trust, that is stored within the "System"-Container:

Trust Domain Object

This Trust Domain Object holds various attributes that are required to build the characteristics of a trust (which we’ll dive into afterwards). One of the most relevant attributes of the TDO is the trustAttributes attribute, which can contain various flags that define how certain trust characteristics behave, for example the behavior of the SID Filtering characteristic.

We’ll get into the attributes and values of this object as we dive deeper into AD Trusts, but at this point the key takeaway here is:

A TDO exists on each side of the trust and each TDO builds the characteristics of the trust relationship on their respective sides

To once again complete the picture here: The domain Shield.SafeAlliance.local has its own TDO for this trust relationship that is stored under the common name (CN) SafeAlliance.local within the "System"-Container of the domain.

Trust Characteristics

Trusts can become quite complex - which you might agree to at the end of this spotlight- and there are a few terms and distinct features that are important to understand attack and defense strategies, so before diving any deeper let me put out a few - hopefully clarifying - words on what a trust relationship really consists of:

  • A trust is always established between two entities, e.g. SafeAlliance.local and Shield.SafeAlliance.local
  • Each side (in other words: each entity) has their own objects (trust account & TDO) that hold information about the characteristics of the trust relationship

These characteristics describe the trust and should ideally be identical on both ends (but to be clear: these are individually managed objects and could differ). A trust on each side has the following characteristics:

  • A Trust Partner (that points to the other side)
  • A Trust Type (or Flavor), e.g. "ParentChild trust"
  • A Trust Direction
  • An Authentication Level
  • A Transitivity status
  • A SID Filtering status
  • A TGT Delegation status

With that being said, the key takeaway here is:

Don't solely rely on the Trust Type when evaluating trust relationships, but enumerate all characteristics for both sides of the trust to develop an attack or defense strategy.

This introduction brought up many new terms, so let’s dive into these to get a thorough understanding of AD Trusts (no worries I recap all these terms when they come into action).

After we dig into the trust objects that are created for each trust, let’s dive into the trust characteristics to understand how trusts behave under the hood.

Trust Partner

A Trust relationship is formed by specifying that one entity trusts another.

As the name suggest the Trust partner specifies whom the entity that you’re looking at trusts (no big surprise here...).

The Trust-Partner attribute is pretty straight forward to understand, but we can use this to get comfy with two other terms that I will use regularly from now on throughout the coming sections: Trusting and trusted partner.

As before there is no big surprise behind these terms, I’ll clarify them nevertheless: If [A] trusts [B], then [A] is the trusting partner and [B] is the trusted partner. These are simple terms, but it is very important to re-iterate who trusts whom once we dig deeper.

Trust Types

There are 6 different types of trusts:

  • ParentChild Trusts
  • TreeRoot Trusts
  • Shortcut (internally named "CrossLink") Trusts
  • Forest Trusts
  • External Trusts
  • Realm Trusts (internally named "Kerberos") Trusts

The trust type describes a general classification of the trust, that can later be used as a first instance of decision making, for example when it comes to granting or denying access to resources. What this means is the following:

Let’s assume a certain characteristic - let’s randomly chose SID Filtering here for the moment - is evaluated to determine if a certain action is allowed or denied. The actual outcome (allow or deny), however, can depend on the trust type of the trust, meaning a certain value within the SID Filtering characteristic (we’ll get into what that means later on) could return a different outcome for a ParentChild trust then it would return for an External Trust.

To avoid confusion with Microsoft’s documentation and naming scheme I will from now on refer to these 6 different trust types as Trust Flavors, this is due to the fact that an AD trustType attribute exists, which however does not contain the value of any of the six named Flavors above (but just a value that narrows the options down).

Take away here:

Start enumerating a Trust relationship with identifying the Trust Flavor on both ends of the relationship. The trust flavor can make difference for the outcome of other trust characteristics.

Side note and just to emphasize this again: The trust flavor should ideally be the same on both ends of the relationship, but technically there could be different trust flavors on each end, which could open up attack paths.

Trust Direction

There are 3 different types of Trust Directions:

  • InBound
  • OutBound
  • BiDirectional

A Trust relationship is formed by specifying that one entity trusts another.

If SafeAlliance.local specifies that it trusts Shield.SafeAlliance.local, that in itself specifies and OutBound Trust - let’s mark this down as ScenarioA for this section. If SafeAlliance.local additional adds that the trust Partner, which in this case is Shield.SafeAlliance.local, should also trust itself, then that specifies a BiDirectional trust - let’s mark this down as ScenarioB for this section.

A Trust relationship is only functional if both sides of the relationship have been set up, which means that in both of these Scenarios the other side of the relationship (Shield.SafeAlliance.local) must also set up the trust relationship.

In ScenarioA Shield.SafeAlliance.local would (ideally) specify that SafeAlliance.local trusts itself, which specifies an InBound trust. In ScenarioB Shield.SafeAlliance.local would (ideally) specify that it trusts SafeAlliance.local and that SafeAlliance.local also trusts itself, which specifies a BiDirectional trust.

In short: The Trust Direction specifies who the trusting partner and who the trusted partner is.

Add that information to the initial trust graph would result in the following:

TrustDirection

It’s important to note that the direction of trust that is specified implies the opposite direction flow of access.

That’s not an easy statement to digest so let’s clear this up: In ScenarioA SafeAlliance.local specifies that it trusts 'Shield.SafeAlliance.local' (OutBound trust), this implies that Shield.SafeAlliance.local could issue access requests to SafeAlliance.local, because if someone trusts you then you could ask for stuff.

There are a few misconceptions with this statement, so let me add a few more notes to this ScenarioA:

  • Just because SafeAlliance.local trusts Shield.SafeAlliance.local does not automatically mean that Shield.SafeAlliance.local will be granted access to anything in SafeAlliance.local. There are additional factors that need to check out (which we'll get our hands on in the next section).<br> Note: Just because someone trusts you does not mean you inherit the right to access any of their resources.
  • The above Statement said that Shield.SafeAlliance.local could issue requests to access resources from SafeAlliance.local.<br> Just to have this cleared out: Technically there is nothing that stops SafeAlliance.local to also issue access requests. However in ScenarioA Shield.SafeAlliance.local does not trust SafeAlliance.local, therefore the authority evaluating these access requests (which is the Domain Controller), will reject those requests right away (regardless of other factors).

Additionally: In ScenarioB SafeAlliance.local set up a BiDirectional trust, but technically that does not mean that Shield.SafeAlliance.local set up the same TrustDirection.

Always evaluate both sides of the trust relationship.

Trust Authentication Level

There are two different types Authentication Levels:

  • Forest-Wide Authentication
  • Selective Authentication

The Authentication Level of a trust relationship defines how the authentication authority (which is the requested Domain Controller) should handle access requests that are sent across a trust boundary.

The Trust Authentication can also be found in the Authentication-Tab of a trust relationship.

TrustAuthentication

Let’s create a simple scenario to understand what this means:

Let’s say *SafeAlliance.local* trusts Shield.SafeAlliance.local and this trust is set up to use Forest-Wide Authentication.
A user located on a client machine within the domain Shield.SafeAlliance.local now tries to access the SYSVOL share of SafeAlliance.local.

In this scenario the user reaches out to the Domain Controller of SafeAlliance.local and asks for access to the SYSVOL share. As the trust relationship in this scenario is configured with Forest-Wide Authentication, the Domain Controller (DC) of SafeAlliance.local will "authenticate" the user, meaning the user will be considered part of the "Authenticated Users" group and access will be granted as members of the "Authenticated Users" are allowed to access the SYSVOL share. In this scenario I have omitted the technical bits of the authentication & authorization flow (Kerberos/NTLM flows) as the important aspect here is that Forest-Wide Authentication trusts are automatically authenticated as "Authenticated Users".

One thing is important to note here

The user in this scenario got access to the SYSVOL share, because the DACL of this share allows access to the "Authenticated Users" group.

If the user would had requested access to another resource, the DC would grant the user a service ticket (I'm playing this scenario with Kerberos), the user would then present this service ticket to the server holding the resource and this server would then in turn grant or deny access based on the DACL of the requested resource.

Forest-Wide Authentication does not grant access to a resource, it specifies that a valid user of the trusted domain is allowed to authenticate.

If the trust would have been set up with Selective Authentication then the DC will not consider the requesting user as part of the "Authenticated Users" group, but instead checks if the user is Allowed-To-Authenticate with the Computer that holds the requested resource. The SYSVOL share is hold by the Domain Controller itself and as our scenario user is not Allowed-To-Authenticate against the DC (which is the default), the access request is rejected.

As with the above let me double emphasize this here:

Selective Authentication does not enforce denied (or granted) access, it specifies that a user must explicitly be Allowed-To-Authenticate or otherwise the Domain Controller will reject the access request right away.

Trust Transitivity

Trust Transitivity describes the extension of a trust relationship over the boundaries of the two parties that established the trust relationship.

A trust relationship can either be transitive or non-transitive. The effect of this can best be described with a simple figure:

[A] <---- trusts [B] <---- trusts [C]

Without any transitive trusts [C] would trust [B] and [B] would trust [A]. If the two shown trust relationships would be transitive, then [C] will also trust [A], and users of [A] could potentially access resources in [C] (depending on the Authentication Level and resource DACL of [C]).

Note: This characteristic is basically only a switch to control transitivity behavior.

TGT Delegation

This characteristic allows to control the usual Kerberos delegation behavior across a trust boundary.

It is as simple (or as complicated) as that. Diving into Kerberos delegation would drive us too far away from trusts, therefore if you want a more thorough understanding have a read through my post: Kerberos Delegation: A Wrap Up. Nevertheless, I’ll try to give a very brief introduction to what this could mean for the easiest Kerberos delegation case (which is Unconstrained Delegation).

A quick recap of Delegation: Delegation is a term to describe an authentication bridge, that enables someone else to pretend to be you (authenticate as your user). In other words: A system trusted for delegation can authenticate as your user towards another system and thereby impersonate your user.

A trust relationship can either have TGT Delegation enabled or not (disabled). The status of the TGT Delegation characteristic determines if a Kerberos delegation ticket is allowed to be sent across a trust boundary. This can best be described in a simple scenario:

Let’s assume there is a trust relationship between [A] and [B] for which TGT Delegation is enabled.Furthermore a user (let’s name it UserA) logged onto a machine on domain [A] wants to connect to a server on domain [B] (let’s call it ServerB).Lastly let’s specify that ServerB is set up with Unconstrained Delegation (as this is easiest case to explain).

If UserA now connects to ServerB over the trust relationship, a TGT delegation ticket will be send over the trust boundary and ServerB can use this delegation ticket to impersonate UserA.

If TGT Delegation would have been disabled for the trust, then the delegation ticket would not be send over the trust boundary and ServerB would not be able to impersonate UserA.

Note: This characteristic is basically only a switch to control Kerberos Delegation behavior.

SID Filtering

We finally came down to the last Trust characteristic: SID Filtering. So far there were already a few terms and concepts to understand, so I’ll promise this one is the last for this first part of this spotlight. I’ll do my best to explain it in an understandable but condense manner... so bear with me for this last section.

SID Filtering is a security mechanism to counter privilege escalation attacks exploiting the SID History attribute.

In order to understand why SID Filtering is needed and what the impacts of misconfigurations are, let me briefly describe where the SID History attribute came from and what it is needed for.

The Windows operating system identifies a user not by its name, but by its Security Identifier (SID), which is a string that could for example look like this: S-1-5-21-2884053423-3431565070-78458365 (the only relevant part to note here is that a SID contains an identification string of the domain (domain ID) that the user is part of, which in this case is 2884053423-3431565070-78458365).

In order to get access to a resource the SID of the requesting user as well as the SIDs of the groups that this user is a member of are checked against the SIDs that are specified in the DACL of the requested resource.

This concept works pretty well in every day access checks, but at some point in an organization’s life it might happen that users need to be migrated to a new domain. Remember from the beginning that a new domain was created for the "Shield" group. If a user is migrated to a new domain the SID of this user (and of its groups) will change as the new domain will have a new domain ID. The resources that the user used to have access to will not be updated (cause they stay in the previous/old domain) and as a result of that all migrated users will loose access to resources that they used to work with. To fix this an admin would need to add the new SIDs of the relevant users and groups to all resource ACEs in the old domain. That is a massive workload and Microsoft came up with a solution to "ease" these migration processes: Instead of adjusting a lot of ACEs in the old domain, the old SID of the users are saved in a newly created attribute, the SID History attribute.... and when I say "newly" created I mean newly created with Windows 2000 ;)

So after Windows 2000 users that are migrated from one domain to another will have their old SID saved within the user’s SID History attribute. This old SID is then used by the old domain to retrieve the user’s groups in that old domain and include these SIDs in authorization checks.

So after Windows 2000 users that are migrated from one domain to another will have their old SID saved within the user’s SID History attribute. This old SID is then used by the old domain to retrieve the user’s groups in that old domain and include these SIDs in authorization checks.

Sounds like a good solution, but here’s the risk: If an attacker can add (or change) a SID within the SID History attribute, then the attacker can impersonate any given user in the "old" domain. Assume for example an attacker finds a way to add the SID of a domain admin of the "old" domain to his own SID History and then requests a resource in this "old" domain. The Domain controller of the "old" domain will see the SID of the domain admin and will then believe this user is a domain admin of its domain.

Of course this attack only makes sense if there are two domains, where a (potentially malicious) user of one domain can access resources in the other domain. A trust relationship between two domains presents exactly this scenario.

Sounds like there should be some sort of protection against this type of attack, which exists... and there are actually two layers of protection against this type of attack:

  • There is SID Filtering that can be enabled or disabled for a trust relationship. If SID Filtering is enabled then only SIDs from the trusted Domain are processed.<br> To emphasize this: Not SIDs from the trusting domain are processed (which would make this protection useless), but only SIDs from the trusted domain.
  • On top of this there are (currently) five rules that filter out certain SIDs based on their value (independently from the SID Filtering status of the trust relationship). These rules range from "Always filter a certain SID", which for example is the case for the "Local System" SID (S-1–5–18) to "Never filter a certain SID", which for example is the case for "Other Organization" SIDs (S-1–5–1000-*). All of these rules are documented within section 4.1.2.2 of [MS-PAC].

As SID Filtering was invented to counter exploitation attempts of the SID History attribute, these two terms are often mixed up when talking about trusts:

  • When speaking about a trust relationship some might say that SID Filtering is enabled or disabled for this relationship; Whereas
  • Some might like to speak of an enabled or disabled SID History for a certain trust relationship

While both approaches might try to express the same desire or configuration for a trust it should be noted that SID Filtering is a characteristic of a trust relationship (that can be enabled or disabled), whereas the SID History is an attribute of an Active Directory object (that can have a value or not). So if you’re being asked "how the SID History of a trust can be disabled", you want to translate that into "how the SID Filtering of a trust can be enabled".

The key takeaway here is:

The SID Filtering characteristic can be used to control which SIDs are allowed to pass to the trusting partner for authentication.

Sadly the actual behavior of the SID Filtering characteristic is often times not quite easy to understand, as it depends on the TrustFlavor of the trust and the trustAttributes of the Trust Domain Object (TDO). I will get into the details of that and add a lookup table for the most common scenarios in the part 2 of this spotlight.

Wrap up and next steps

In this first part of this Active Directory spotlight, the inner mechanics of AD Trusts were introduced to get a grip on the terminology and get a glimpse into how AD Trusts work under the hood.

The main parts to remember are:

There are two objects that are created when a trust is established: A trust account and a Trust Domain Object (TDO). Both of these objects are created on each side of the trust.

There are 7 Trust Characteristics that should be known:

  • Trust Partner
  • Trust Types (aka. Trust Flavors)
  • Trust Direction
  • Trust Authentication Level
  • Trust Transitivity
  • TGT Delegation
  • SID Filtering

For those of us that prefer visual representations of things to remember take this:

Summary.png

What has not been touched yet is the operational part of AD trusts, which covers:

  • How are the described trust characteristics put in place and how are they evaluated?
  • How can a Red Team exploit Trust relationships?
  • How can a Blue Team enumerate Trusts and detect weak spots?

All of this will be covered in Part 2:

Active Directory Spotlight: Trusts — Part 2. Operational Guidance

Carsten Sandker
Carsten is part of our Offensive Security Team, helping our clients identify vulnerabilities and preventing attacks. He is specialized in Active Directory security and scenario-based attacks.