guidox / Security

Audit Changes to a Registry Key with a SACL

TL;DR

Enable **Audit Object Access** in the local policy, add an auditing entry to the key's SACL, then read event **4657** in the Security log for the process and account that changed it.

Before you start

Requirements

  • Admin rights
  • Windows Pro or higher for the policy editor
  • 20 minutes
Step by step

Instructions

01

Turn on object access auditing

The SACL only produces events when the policy allows the category. **auditpol** sets it without opening the policy editor.

auditpol /get /category:'Object Access'
auditpol /set /subcategory:'Registry' /success:enable /failure:enable
auditpol /get /subcategory:'Registry'
02

Confirm you can write the SACL

**SeSecurityPrivilege** is required to modify a SACL, and it is not part of the normal admin token by default. Check the token before blaming the key.

whoami /priv | Select-String -Pattern 'SeSecurityPrivilege|SeTakeOwnershipPrivilege'
Get-Acl 'HKLM:\SOFTWARE\Vendor\App' | Format-List
Get-Item 'HKLM:\SOFTWARE\Vendor\App' | Select-Object Name,Property
03

Add the auditing entry

**Set-AuditRule** writes an entry that records **SetValue** and **Delete** for everyone. Everyone is the right scope here: the goal is to catch the unknown writer.

$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey('SOFTWARE\Vendor\App', 'ReadWriteSubTree', 'ChangePermissions')
$acl = $key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::Audit)
$rule = New-Object System.Security.AccessControl.RegistryAuditRule('Everyone','SetValue,Delete','ContainerInherit','None','Success')
$acl.AddAuditRule($rule)
$key.SetAccessControl($acl)
(Get-Acl 'HKLM:\SOFTWARE\Vendor\App').Audit | Select-Object IdentityReference,RegistryRights
04

Reproduce the change

Wait for the setting to flip, or flip it yourself once as a control. The timestamp of the control event proves the audit path works.

Set-ItemProperty 'HKLM:\SOFTWARE\Vendor\App' -Name Setting -Value 1
Get-WinEvent -LogName Security -MaxEvents 50 | Where-Object Id -eq 4657 | Select-Object TimeCreated,Message -First 3
05

Read the event and identify the writer

Event **4657** names the object, the value, the new data and the **Process Name** with the account. That pair is the answer to who changed it.

$e = Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4657]]" -MaxEvents 10
$e | ForEach-Object { $x=[xml]$_.ToXml(); [pscustomobject]@{ Time=$_.TimeCreated; Process=($x.Event.EventData.Data | Where-Object Name -eq 'ProcessName').'#text'; User=($x.Event.EventData.Data | Where-Object Name -eq 'SubjectUserName').'#text'; Value=($x.Event.EventData.Data | Where-Object Name -eq 'ObjectValueName').'#text' } }
06

Remove the audit rule when you are done

An active SACL fills the Security log quickly. Remove the entry once the culprit is known and keep the policy only if you need ongoing auditing.

$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey('SOFTWARE\Vendor\App', 'ReadWriteSubTree', 'ChangePermissions')
$acl = $key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::Audit)
$acl.Audit | ForEach-Object { $acl.RemoveAuditRule($_) }
$key.SetAccessControl($acl)
(Get-Acl 'HKLM:\SOFTWARE\Vendor\App').Audit
Troubleshooting

Common issues

No 4657 events appear

The policy is off, the SACL is on the wrong key, or the change was made by SYSTEM which bypasses some checks. Verify both layers.

SetAccessControl throws UnauthorizedAccessException

The process lacks SeSecurityPrivilege. Run the shell as administrator and confirm with whoami /priv.

The event names a process but no user

The change was made by a service in another session. Match the process ID against the service list at that time.

The Security log fills up fast

Audit only the specific key, not the parent, and turn the subcategory off when the investigation ends.

FAQ

Frequently asked

Is this the same as auditing file access?

The mechanism is identical, the object type differs. File and folder auditing uses the same policy category and different events.

Does it work on Home?

auditpol works, but the policy editor is Pro only. The PowerShell path in this guide works on Home as administrator.

Can it catch a deletion?

Yes, if Delete is in the audit mask. The event records the attempt even when the delete fails.

How to undo this

Remove the audit entries, then run auditpol /set /subcategory:'Registry' /success:disable /failure:disable to stop the logging.

Changelog · 1 entry
  1. 2026-11-01
    First version
Interactive version: checklist, feedback, related guides →

Free · no account needed · tracks your progress