Security Focus: sIDHistory / sID Filtering Sanity Check - Part 1 - aka Post #100!

 
100 posts! Who’d have thought it? I started out wanting to evangelise PowerShell. 100 posts later, that desire is as strong as ever! Along the way, it seems that I have helped some folks out, made others laugh, confused some, talked about PoSh chickens... and had a jolly good time of it, too!

Anyway, enough of that. Time for some PowerShell and what better way to mark a 100th post than to combine two of my passions, PowerShell and Security?

 

 A Modicum of sIDHistory History

Ah, sIDHistory. A useful, TEMPORARY migration feature.

It’s an attribute in Active Directory that can be populated with the security identifiers (SIDs) of other security principals, e.g. User A in domain A can have the SID of User B in domain B associated with their account. When it comes to authorisation, User A then has the same access as User B. The idea is to ease the pain of migrations: User B can be migrated from domain B to domain A... becoming User A in domain A and, with sIDHistory, maintaining all of User B’s access back in domain B - all of this without updating any permissions on the resources in domain B.

And, therein, lies the rub: whilst sIDHistory helps ease the pain of migration, it quite often never gets cleaned up as part of the migration project and then contributes to problems such as ‘token bloat’. Additionally, the persistence of sIDHistory also poses a security risk.

In essence, the risk is this: a user in a domain can have the sID of a privileged user or group maliciously added to their sIDHistory attribute*… at next logon they then have the same level of access as the high privileged security principal and can access resources as that principal. Typically, this will have been done to elevate privilege across a trust, when something called sID FIltering is disabled on the trust. However, it can also be used to elevate privilege in the same domain.

*NB - injecting a sID into sIDHistory requires existing privileged access, e.g. Domain Admins membership.

 

Using and Abusing sIDHIstory

There are some checks you can perform to understand your sIDHIstory usage.

First up, check whether any users or groups have the sIDHistory attribute populated in the forest:

(Get-ADForest).Domains | ForEach-Object {

Get-ADUser -Filter {sIDHistory -like "*"} -Properties sIDHistory -Server $_

Get-ADGroup -Filter {sIDHistory -like "*"} -Properties sIDHistory -Server $_

}

 

In the above example, I've ran the one-liner and piped the output into Measure-Object. Seems I have 2976 objects in my forest with sIDHistory configured! Ouch.

If you thought your forest was sIDHistory free and you find some, then that's an obvious avenue of investigation! Likewise, if you find sIDHistory in an unexpected domain or set on unauthorised users or groups, then you also need to follow up.

Now, if you do have users with sIDHistory, check that you don’t have sIDs from the same domain in your users’ sIDHistory attribute. There's no legitimate need for this and it is likely to be indicative of a wider problem. Here's how:

(Get-ADForest).Domains | ForEach-Object {

$DomainSid = (Get-ADDomain -Server $_).DomainSid.Value

Get-ADUser -Filter {sIDHistory -like "*"} -Properties sIDHistory -Server $_ |

Where-Object {$_.sIDHistory -like "$DomainSid*"}

}

  

 

Hmmm... so I have one account with a sIDHistory entry from the same domain. Time for some further investigation. First up, for peace of mind, I'll make use of a function I wrote a while back to check if this user is a member of a high privileged group.

The Test-ADUserHighPrivilegeGroupMembership*  function is described here and can be downloaded here.

*And, now there's the new and improved Test-ADPrincipalProtectedGroupMembership function!

 

Nothing returned - not a high privileged user then. 

Curiouser and curiouser said PoSh Chap! Further investigation required...

Let's put that same-domain one-liner into a function and add some additional code to look up the objects within the sIDHistory attribute.

function Get-ADForestPersistentSidHistory {

(Get-ADForest).Domains | ForEach-Object {

$Domain = $_

$DomainSid = (Get-ADDomain -Server $_).DomainSid.Value

$User = Get-ADUser -Filter {sIDHistory -like "*"} -Properties sIDHistory -Server $Domain |

Where-Object {$_.sIDHistory -like "$DomainSid*"}

if ($User) {

$User

$User.sIDHistory | ForEach-Object {

$SID = $_.value

Get-ADObject -Filter {objectsID -like $sID} -Server $Domain -Properties objectsID

}

}

}

}

  

 

This output shows a user object (Joe Bloke) that has same-domain sIDHistory set and then the object (Domain Admins!) that relates to the sID in the sIDHistory attribute.

Not good. Time to start asking discrete questions...

 

Removing sIDHistory

Remember, always tidy up sIDHistory after a migration. The tidy-up will involve creating new groups and adding new permissions to resources. sIDHistory can then be emptied.

Here's how to remove a single sIDHistory entry from a user with PowerShell.

$sID = "S-1-5-21-3751808635-4005808669-142463309-4103"

Set-ADObject -Identity "CN=Joe Bloke,OU=HR,OU=User Accounts,DC=contoso,DC=com" -Remove @{sIDHistory=$sID}

   

In Part 2 I'll talk about checking the status of sID Filtering on trusts...