# 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

```powershell
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

```powershell
Find-SidHistory
```

**3.** Scan a specific domain object

```powershell
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

```powershell
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

```powershell
Find-SidHistorySimple
```

**3.** Scan a specific domain object

```powershell
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.

![Active Directory Users and Computers](/files/C5Jq5zsZIujyybG6XwLm)

## 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:

```csv
"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

```powershell
ls \\dc.forestall.labs\c$
```

![Check access on DC using PowerShell](/files/OxaveXccFZ5uOTRBThlG)

### Linux

#### Using netexec to check Domain Admin access

```bash
nxc smb <dcHost> -u '<user>' -p '<pass>'
```

Example:

```bash
nxc smb dc.forestall.labs -u '_oldAdmin' -p 'Temp123!@#'
```

![Check access on DC using netexec](/files/Ai5nYeozMfL2HxLrWTgj)

## Mitigation

To mitigate `SIDHistory`, use PowerShell. Graphical tools such as Active Directory Users and Computers cannot modify this attribute.

```powershell
Set-ADUser -Identity <User> -Remove @{sIDHistory="<SIDHistory Value>"}
```

Example:

```powershell
Set-ADUser -Identity _oldAdmin -Remove @{sIDHistory="S-1-5-21-3838874360-3982899950-1830233728-500"}
```

![Remove SIDHistory with PowerShell](/files/H5vzbtZ87027Ks7w6zNv)

## 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.

| Event ID | Description                              | Fields/Attributes      | References                                                                                 |
| -------- | ---------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------ |
| 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

* [sIDHistory Attribute - Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/adschema/a-sidhistory)
* [SID History Persistence - The Hacker Recipes](https://www.thehacker.recipes/ad/persistence/sid-history)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestall.io/fsprotect/edges/ad/sidhistory.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
