SIDHistory
Summary
FSProtect ACL Alias
SIDHistory
Affected Object Types
Users, Groups, Computers
Exploitation Certainty
Unlikely
AD Attribute
SIDHistory
AD Attribute GUID
17eb4278-d167-11d0-b002-0000f80367c1
Description
The SIDHistory
attribute in Active Directory stores previous Security Identifiers (SIDs) when an account is migrated from one domain to another. This functionality preserves access to resources in the original domain without requiring administrators to update all access control lists. When properly configured, SIDHistory
facilitates seamless domain migrations and consolidations, allowing users to maintain their existing permissions during transition periods.
However, if an object has SIDHistory
values, it can introduce significant security risks to an Active Directory environment. When an attacker identifies objects with SIDHistory
, they can potentially exploit this attribute for privilege escalation. Because the security system processes and honors all SIDs in an account's SIDHistory
during access checks, a compromised account with high-privilege SIDs in its SIDHistory
(such as Enterprise Admins) effectively has those privileges across the domain, even though the account isn't directly a member of those groups.
Identification
PowerShell
Active Directory Module
Using the Active Directory PowerShell module, you can enumerate SIDHistory
entries.
1. Find-SidHistory function
function Find-SidHistory {
[CmdletBinding()]
param ( [string]$Target = $null,[string]$OutputPath = ".\SIDHistory.csv")
Import-Module ActiveDirectory -ErrorAction Stop
try {
if ($Target) {
Write-Host "Retrieving sidHistory for object: $Target"
$objects = Get-ADObject -Identity $Target -Properties Name, sidHistory
}
else {
Write-Host "Retrieving all objects with sidHistory..."
$objects = Get-ADObject -Filter { sidHistory -like "*" } -Properties Name, sidHistory
}
if ($objects) {
$results = foreach ($obj in $objects) {
if ($obj.sidHistory) {
foreach ($sid in $obj.sidHistory) {
$resolved = $null
try {
$resolved = (New-Object System.Security.Principal.SecurityIdentifier($sid)).Translate([System.Security.Principal.NTAccount]).Value
} catch {
$resolved = "Unresolved"
}
[PSCustomObject]@{
ObjectName = $obj.Name
SIDHistory = $sid
ResolvedName = $resolved
}
}
}
else {
[PSCustomObject]@{
ObjectName = $obj.Name
SIDHistory = $null
ResolvedName = $null
}
}
}
$results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "Results exported to $OutputPath"
}else { Write-Warning "No objects found with sidHistory." }
}
catch { Write-Error "An error occurred: $_" }
}
2. Scan all domain objects
Find-SidHistory
3. Scan a specific domain object
Find-SidHistory -Target "CN=_oldAdmin,CN=Users,DC=Forestall,DC=labs"
.NET Directory Services
By leveraging PowerShell’s built-in .NET DirectoryServices namespace, you can enumerate SIDHistory
entries without relying on any external modules or dependencies.
1. Find-SidHistorySimple function
function Find-SidHistorySimple {
[CmdletBinding()]
param ([string]$Target, [string]$OutputPath = ".\SIDHistory.csv")
try {
if ($Target) {
Write-Verbose "Binding directly to object: $Target"
try { $entries = @( New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Target") ) }
catch {Write-Error "Failed to bind to '$Target': $_"; return}
}else {
Write-Verbose "Binding to local RootDSE..."
try {
$root = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
$baseDN = $root.Properties["defaultNamingContext"].Value
$ldapPath = "LDAP://$baseDN"
$searchRoot= New-Object System.DirectoryServices.DirectoryEntry($ldapPath)
$searcher = [System.DirectoryServices.DirectorySearcher]::new($searchRoot)
$searcher.Filter = "(sidHistory=*)"
$searcher.PageSize = 1000
[void]$searcher.PropertiesToLoad.Add("distinguishedName")
[void]$searcher.PropertiesToLoad.Add("name")
[void]$searcher.PropertiesToLoad.Add("sidHistory")
$hits = $searcher.FindAll()
}catch { Write-Error "LDAP enumeration failed: $_"; return; }
$entries = foreach ($hit in $hits) {
try { $hit.GetDirectoryEntry() } catch { Write-Warning "Could not bind entry: $_"; continue }
}
}
$results = foreach ($entry in $entries) {
$name = $entry.Properties["name"].Value
$sids = $entry.Properties["sidHistory"]
if ($sids -and $sids.Count -gt 0) {
foreach ($sid in $sids) {
$resolved = $null
try {
$sidObj = New-Object System.Security.Principal.SecurityIdentifier($sid,0)
$resolved = $sidObj.Translate([System.Security.Principal.NTAccount]).Value
}
catch { $resolved = "Unresolved" }
[PSCustomObject]@{
ObjectName = $name
SIDHistory = $sidObj.Value
ResolvedName = $resolved}}}
else {
[PSCustomObject]@{
ObjectName = $name
SIDHistory = $null
ResolvedName = $null}} }
if ($results) {
$results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "Results exported to $OutputPath"
} else {Write-Host "No objects found with sidHistory."}
}catch { Write-Error "An error occurred: $_"}}
2. Scan all domain objects
Find-SidHistorySimple
3. Scan a specific domain object
Find-SidHistorySimple -Target "CN=_oldAdmin,CN=Users,DC=Forestall,DC=labs"
Active Directory Users and Computers
1. Open Active Directory Users and Computers
.
2. Double-click the object.
3. In the Properties window, navigate to the Attribute Editor
tab.
4. In the attributes list, locate sIDHistory
.
5. Click OK to close the dialogs.

Exploitation
When the SIDHistory
attribute contains a SID, it grants the account the same permissions as that SID. Therefore, if a user has the sIDHistory
of the Domain Administrator, this effectively means full domain compromise.
Example:
"ObjectName","SIDHistory","ResolvedName"
"_oldAdmin","S-1-5-21-3838874360-3982899950-1830233728-500","FORESTALL\Administrator"
This means that _oldAdmin
has Domain Admin privileges. For testing:
Windows
ls \\dc.forestall.labs\c$

Linux
Using netexec to check Domain Admin access
nxc smb <dcHost> -u '<user>' -p '<pass>'
Example:
nxc smb dc.forestall.labs -u '_oldAdmin' -p 'Temp123!@#'

Mitigation
To mitigate SIDHistory
, use PowerShell. Graphical tools such as Active Directory Users and Computers cannot modify this attribute.
Set-ADUser -Identity <User> -Remove @{sIDHistory="<SIDHistory Value>"}
Example:
Set-ADUser -Identity _oldAdmin -Remove @{sIDHistory="S-1-5-21-3838874360-3982899950-1830233728-500"}

Detection
Adding new access control entries (ACEs) on Active Directory objects changes the ntSecurityDescriptor
attribute of the objects themselves. These changes can be detected with the 5136 and 4662 Event IDs to identify potentially dangerous object modifications.
5136
A directory service object was modified.
ntSecurityDescriptor
https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5136
4662
An operation was performed on an object.
AccessList, AccessMask
https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662
References
Last updated
Was this helpful?