Registry auditing for disconnected users

3

1

What I would like to get is Windows events for registry changes. Now, I found this manual where in one of the steps of setting the events, the audit configuration of the registry key must be edited (find the key -> right click -> permissions -> advanced -> audit).

Now, say I want to set this monitoring for the branch:

HKCU\Software\Classes

I must find all the regular users under HKU and set the auditing for every one of them, the thing is that the user data is loaded only when it is logged in. I can load the registry key manually and add the auditing with:

reg load HKU\Steven C:\Users\Steven\ntuser.dat

But the problem is that not all keys are available there (for instance Classes is not available), and to be honest, it seems like a crude solution. Plus there is the dilemma of what happens if a user is added after the installation?

On a Windows 10, is there any way to add auditing configurations for all users?

Anton.P

Posted 2018-12-25T07:55:39.367

Reputation: 185

Answers

2

When a user first logs on and their profile folder is created, their user hive is initialized as a copy of the default user hive, stored here:

C:\Users\Default\NTUSER.DAT

After the profile is created, the copies are independent, so there is no convenient way to edit all users' profiles in one shot. If, however, you load and edit that hive, the changes will be reflected in any subsequently created profiles.

The Classes key is stored in a separate hive that you can load and edit separately:

%USERPROFILE%\AppData\Local\Microsoft\Windows\UsrClass.dat

Unfortunately, there is no default classes hive; it appears to be initialized to an empty hive when a profile is created.


To make dealing with all this a little less tedious for you, I wrote a PowerShell script to an audit rule on each of the given subtrees loaded under HKEY_USERS. But first, we need to enable the auditing privilege, which requries a surprising amount of code (adapted from Lee Holmes):

param(    
    ## The privilege to adjust.
    $Privilege,
    ## The process on which to adjust the privilege. Defaults to the current process.
    $ProcessId = $pid,
    ## Switch to disable the privilege, rather than enable it.
    [Switch] $Disable
)

## Taken from P/Invoke.NET with minor adjustments.
$definition = @'
using System;
using System.Runtime.InteropServices;
public class AdjPriv
{

    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
    ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);

    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);

    [DllImport("advapi32.dll", SetLastError = true)]
    internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);

    [StructLayout(LayoutKind.Sequential, Pack = 1)]

    internal struct TokPriv1Luid
    {
        public int Count;
        public long Luid;
        public int Attr;
    }

    internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
    internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
    internal const int TOKEN_QUERY = 0x00000008;
    internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;

    public static bool EnablePrivilege(long processHandle, string privilege, bool disable)
    {
        bool retVal;
        TokPriv1Luid tp;
        IntPtr hproc = new IntPtr(processHandle);
        IntPtr htok = IntPtr.Zero;
        retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
        tp.Count = 1;
        tp.Luid = 0;

        if(disable)
        {
            tp.Attr = SE_PRIVILEGE_DISABLED;
        }
        else
        {
            tp.Attr = SE_PRIVILEGE_ENABLED;
        }

        retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
        retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
        return retVal;
    }
}

'@

$processHandle = (Get-Process -id $ProcessId).Handle
try { 
  Add-Type $definition 
} catch {} # Silent failure on re-registration

[AdjPriv]::EnablePrivilege($processHandle, $Privilege, $Disable)

Save that as privs.ps1. Then we can use it in our script, which should go in the same directory:

Param (
    [string[]]$Subkeys
)
$rights = 'ChangePermissions,CreateSubKey,CreateLink,Delete,SetValue,TakeOwnership'
.\privs.ps1 -Privilege SeSecurityPrivilege
$users = [Microsoft.Win32.RegistryKey]::OpenBaseKey('Users', 'Default')
$Subkeys | % {
    $key = $users.OpenSubKey($_, $true)
    $acl = $key.GetAccessControl()
    $rule = [System.Security.AccessControl.RegistryAuditRule]::new([System.Security.Principal.NTAccount]::new('Everyone'), $rights, 'ContainerInherit,ObjectInherit', 'None', 'Success')
    $acl.AddAuditRule($rule)
    $key.SetAccessControl($acl)
    $key.Close()
}

For each of the provided subkeys, it creates an rule that audits successful changes (defined by the $rights variable near the top, which you can change) in that key or subkeys. Save it as a PS1 file, e.g. regaudit.ps1. If you haven't already, enable PowerShell script execution as directed in the PowerShell tag wiki. Then load all necessary hives under HKEY_USERS. You can run the script from an elevated PowerShell prompt like so:

.\regaudit.ps1 -Subkeys 'Steven\Control Panel', 'Steven\Software\Microsoft'

Note that you should not include HKCU or any such root. You can pass as many subkeys as you like. Alternatively, you can create a text file with one subkey per line and use that like so:

.\regaudit.ps1 -Subkeys (gc .\toaudit.txt)

N.B. Audit events will not appear in the Security log unless "Audit object access" is enabled in the Audit Policy section of the Local Security Policy.

Further reading: Access Control in .NET

Ben N

Posted 2018-12-25T07:55:39.367

Reputation: 32 973