Active Directory Security Blindspots: Domain Local Memberships
One day, it came to my attention that "Domain Local" groups needed to be reported for Active Directory memberships in a program I created, Account Ninja Enterprise.
Part of the problem a Service Desk experiences is troubleshooting permissions to folders, recently we've been going through an Amalgamation and User Accounts from Other Domains have been needing to access resources on our Domain, which meant folders on our file servers.
Microsoft has added some features to LDAP to recursively get memberships for a user. It is documented as the LDAP_MATCHING_RULE_IN_CHAIN rule. The rule is used when you add its OID after the member attribute in an LDAP query. Ex. (member**:1.2.840.113556.1.4.1941:**= XXXXX ). However, this does not find Domain Local group memberships from other domains that the account is considered a part of when it attempts to access resources on the other domains.
This is a large security blindspot.
Another Security Blindspot is the support of <SID> Binding String versus <GUID> Binding String when it comes to accounts from other domains. On it's own domain, it works as expected, you can put in an Account GUID or Account SID for the following LDAP query and it would work:
- (&(objectCategory=group) (member**:1.2.840.113556.1.4.1941:**=<SID=XXXXXX>) )
- (&(objectCategory=group) (member**:1.2.840.113556.1.4.1941:**=<GUID=XXXXXX>) )
However, when the same thing is tried using the SID or GUID of an Account from another domain, it will not resolve. The GUID is understandable, the Foreign Security Principal will (and should always) have a different GUID than the Account it represents from another domain. But unfortunately, the SID will not resolve despite being the same on the Account on its own Domain and the Foreign Security Principal on the Other Domain you are trying to find memberships.
After much experimentation, you can use the Foreign Security Principal GUID or the Foreign Security Principal DistinguishedName to have the LDAP query work, however, it should be noted this is an extra step you will have to perform.
But what about the super duper can't possibly fall short Get-ADPrincipalGroupMembership PowerShell Command?
It falls short.
The command can only get you Direct Memberships, I tested and verified this myself just to make sure as I could not find documentation on this anywhere. The problem is you can add a Global/Universal Group from Domain A to the Domain Local group on Domain B. It will create the Foreign Security Principal on Domain B, but only the direct member added which is the group. This means any user account in that Domain A group, will also get a membership on the Domain Local group in Domain B when accessing resources in Domain B, however, the user account will not have a Foreign Security Principal created on Domain B.
I have thoroughly tested this PowerShell Command and observed that finding the Domain B memberships of GroupA where GroupA was directly added into worked, while trying to find the Domain B memberships of a member of GroupA would return nothing:
- Get-ADPrincipalGroupMembership -Identity "GroupA" -Server DomainA.ca -ResourceContextServer DomainB.ca
- Get-ADPrincipalGroupMembership -Identity "UserAInGroupA" -Server DomainA.ca -ResourceContextServer DomainB.ca
So is all lost, is this a doomed mission, a losing battle never to be won?
No, it is possible to win despite us not being able to rely on the PowerShell command to show you what memberships a user has on another domain when they are nested (meaning their account is not directly added into the Domain Local group, but a group they are in is added) or from current LDAP limitations as discussed above.
But how?
Currently, I've laid out the following master plan:
- Get all memberships of an account on its own domain
- Using all of those sids including the account sid, query and find ForeignSecurityPrincipals on the Other Domain
- Using each returned ForeignSecurityPrincipal, get the GUIDs and generate the LDAP query for getting direct and also the LDAP query to recursively get memberships which will both use the GUID Binding String to make use of the GUIDs
This would need to be done on every "Other Domain" you want memberships from as each domain would have its own ForeignSecurityPrincipals, if any, which would need to be searched for and found.
It was hard deciding whether it would be better to generate a SINGLE BIG LDAP query for the list of GUIDS or to instead make an LDAP query for each GUID. I've heard Active Directory can handle 10MB worth of text in the LDAP string, so as long as an account isn't added into around 30 million groups, I think it will be acceptable to make two BIG LDAP queries.
This has all been tested on Domain Functional Level of Active Directory 2008 R2.... I doubt the latest version addresses these blind spots either.