'Using powershell to get the "Audit Policy" security setting value

I am trying to use Powershell (auditpol) to query the security setting values of the Audit Policy items. So far with all the auditpol commands, I only able to get the subcategories value instead.

auditpol /get /category:*

So far I could only get the list of the 9 items without the success/failure/no auditing values using:

auditpol /list/category

Could there be a command/flag that I might have left out for auditpol or is there any other command for me to retrieve the policies and its relevant security setting values?

Policy and values that I would like to query.



Solution 1:[1]

As you've found, auditpol only manages the settings that are in effect when the "Advanced Audit Policy Configuration" feature is enabled.

To query the "classic" audit policy, you will need to use the LSA Policy Win32 API to:

  1. Open the local security policy using LsaOpenPolicy()
  2. Query the audit settings using LsaQueryPolicyInformation()
  3. Translate the results to something readable.

The following example uses Add-Type to compile a C# type that in turn does all of the above:

$AuditPolicyReader = Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Linq;
using System.Collections.Generic;

public class AuditPolicyReader
{
    [Flags()]
    public enum AuditPolicySetting
    {
        Unknown =  -1,
        None    = 0x0,
        Success = 0x1,
        Failure = 0x2
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct LSA_UNICODE_STRING
    {
        public UInt16 Length;
        public UInt16 MaximumLength;
        public IntPtr Buffer;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct LSA_OBJECT_ATTRIBUTES
    {
        public int Length;
        public IntPtr RootDirectory;
        public LSA_UNICODE_STRING ObjectName;
        public UInt32 Attributes;
        public IntPtr SecurityDescriptor;
        public IntPtr SecurityQualityOfService;
    }

    public struct POLICY_AUDIT_EVENTS_INFO
    {
        public bool AuditingMode;
        public IntPtr EventAuditingOptions;
        public Int32 MaximumAuditEventCount;
    }

    [DllImport("advapi32.dll")]
    static extern uint LsaQueryInformationPolicy(IntPtr PolicyHandle, uint InformationClass, out IntPtr Buffer);

    [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
    static extern uint LsaOpenPolicy(ref LSA_UNICODE_STRING SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, uint DesiredAccess, out IntPtr PolicyHandle);

    [DllImport("advapi32.dll", SetLastError = true)]
    static extern uint LsaClose(IntPtr ObjectHandle);

    public static Dictionary<string, AuditPolicySetting> GetClassicAuditPolicy()
    {
        // Create dictionary to hold the audit policy settings (the key order here is important!!!)
        var settings = new Dictionary<string, AuditPolicySetting>
        {
            {"System", AuditPolicySetting.Unknown},
            {"Logon", AuditPolicySetting.Unknown},
            {"Object Access", AuditPolicySetting.Unknown},
            {"Privilige Use", AuditPolicySetting.Unknown},
            {"Detailed Tracking", AuditPolicySetting.Unknown},
            {"Policy Change", AuditPolicySetting.Unknown},
            {"Account Management", AuditPolicySetting.Unknown},
            {"Directory Service Access", AuditPolicySetting.Unknown},
            {"Account Logon", AuditPolicySetting.Unknown},
        };

        // Open local machine security policy
        IntPtr polHandle;
        LSA_OBJECT_ATTRIBUTES aObjectAttributes = new LSA_OBJECT_ATTRIBUTES();
        aObjectAttributes.Length = 0;
        aObjectAttributes.RootDirectory = IntPtr.Zero;
        aObjectAttributes.Attributes = 0;
        aObjectAttributes.SecurityDescriptor = IntPtr.Zero;
        aObjectAttributes.SecurityQualityOfService = IntPtr.Zero;

        var systemName = new LSA_UNICODE_STRING();
        uint desiredAccess = 2; // we only need the audit policy, no need to request anything else
        var res = LsaOpenPolicy(ref systemName, ref aObjectAttributes, desiredAccess, out polHandle);
        if (res != 0)
        {
            if(res == 0xC0000022)
            {
                // Access denied, needs to run as admin
                throw new UnauthorizedAccessException("Failed to open LSA policy because of insufficient access rights");
            }
            throw new Exception(string.Format("Failed to open LSA policy with return code '0x{0:X8}'", res));
        }
        try
        {
            // now that we have a valid policy handle, we can query the settings of the audit policy
            IntPtr outBuffer;
            uint policyType = 2; // this will return information about the audit settings
            res = LsaQueryInformationPolicy(polHandle, policyType, out outBuffer);
            if (res != 0)
            {
                throw new Exception(string.Format("Failed to query LSA policy information with '0x{0:X8}'", res));
            }

            // copy the raw values returned by LsaQueryPolicyInformation() to a local array;
            var auditEventsInfo = Marshal.PtrToStructure<POLICY_AUDIT_EVENTS_INFO>(outBuffer);
            var values = new int[auditEventsInfo.MaximumAuditEventCount];                
            Marshal.Copy(auditEventsInfo.EventAuditingOptions, values, 0, auditEventsInfo.MaximumAuditEventCount);

            // now we just need to translate the provided values into our settings dictionary
            var categoryIndex = settings.Keys.ToArray();
            for (int i = 0; i < values.Length; i++)
            {
                settings[categoryIndex[i]] = (AuditPolicySetting)values[i];
            }

            return settings;
        }
        finally
        {
            // remember to release policy handle
            LsaClose(polHandle);
        }
    }
}
'@ -PassThru |Where-Object Name -eq AuditPolicyReader

Now we can call GetClassicAuditPolicy() (remember to run this from an elevated prompt):

PS ~> $AuditPolicyReader::GetClassicAuditPolicy()
Key                                 Value
---                                 -----
System                               None
Logon                    Success, Failure
Object Access                        None
Privilige Use                        None
Detailed Tracking                    None
Policy Change                     Success
Account Management       Success, Failure
Directory Service Access             None
Account Logon                        None

Solution 2:[2]

auditpol only returns the Advanced audit policy configuration. These settings can be found in the UI under Security Settings > Advanced Audit Policy Configuration > System Audit Policies

The legacy audit policy your screenshot shows were mostly done away with after Windows Server 2003/Windows Vista. Note the warnings in the policy properties or on the MS compatibility page:

For advanced policies, you can use /r to get a csv-formatted table:

auditpol /get /category:'Account Logon' /r | ConvertFrom-Csv | 
Format-Table 'Policy Target',Subcategory,'Inclusion Setting'
Policy Target Subcategory                        Inclusion Setting
------------- -----------                        -----------------
System        Kerberos Service Ticket Operations No Auditing      
System        Other Account Logon Events         No Auditing      
System        Kerberos Authentication Service    No Auditing      
System        Credential Validation              No Auditing      

For legacy audit policies:

secedit.exe /export /areas SECURITYPOLICY /cfg filename.txt
[Event Audit]
AuditSystemEvents = 0
AuditLogonEvents = 0
AuditObjectAccess = 0
AuditPrivilegeUse = 0
AuditPolicyChange = 0
AuditAccountManage = 0
AuditProcessTracking = 0
AuditDSAccess = 0
AuditAccountLogon = 0

Requires that it hasn't been disabled. Check in the registry:

Get-ItemProperty HKLM:\System\CurrentControlSet\Control\Lsa -Name SCENoApplyLegacyAuditPolicy 

Solution 3:[3]

Here is a code that gives you a list of all categories and subcategories with their current audit-status. I made it a bit longer than really needed to add the local names of each object. Also see some usage-samples at the end of the code.

# getting the audit policy settings for each subcategory
# works for any OS language

cls
Remove-Variable * -ea 0
$ErrorActionPreference = 'stop'
#requires -runasadmin

$dll = [string]::Join("`r`n", '[DllImport("advapi32.dll")]', 'public static extern bool') 
$auditpol = Add-Type -Name 'AuditPol' -Namespace 'Win32' -PassThru -MemberDefinition "
$dll AuditEnumerateCategories(out IntPtr catList, out uint count);
$dll AuditLookupCategoryName(Guid catGuid, out string catName);
$dll AuditEnumerateSubCategories(Guid catGuid, bool all, out IntPtr subList, out uint count);
$dll AuditLookupSubCategoryName(Guid subGuid, out String subName);
$dll AuditQuerySystemPolicy(Guid subGuid, uint count, out IntPtr policy);
$dll AuditFree(IntPtr buffer);"

Add-Type -TypeDefinition "
using System;
public struct AUDIT_POLICY_INFORMATION {
    public Guid AuditSubCategoryGuid;
    public UInt32 AuditingInformation;
    public Guid AuditCategoryGuid;
}"

function getPolicyInfo($sub) {
    # get policy info for one subcategory:
    $pol = new-object AUDIT_POLICY_INFORMATION
    $size = $ms::SizeOf($pol)
    $ptr  = $ms::AllocHGlobal($size)
    $null = $ms::StructureToPtr($pol, $ptr, $false)
    $null = $auditpol::AuditQuerySystemPolicy($sub, 1, [ref]$ptr)
    $pol  = $ms::PtrToStructure($ptr, [type][AUDIT_POLICY_INFORMATION])
    $null = $ms::FreeHGlobal($ptr)
    [PsCustomObject]@{
        category = $pol.AuditCategoryGuid
        success  = [bool]($pol.AuditingInformation -band 1)
        failure  = [bool]($pol.AuditingInformation -band 2)
    }
}

# (optional) get GUID and local name of all categories:
$ms = [System.Runtime.InteropServices.Marshal]
$count = [uint32]0
$buffer = [IntPtr]::Zero
$size = $ms::SizeOf([type][guid])
$null = $auditpol::AuditEnumerateCategories([ref]$buffer,[ref]$count)
$ptr = [int64]$buffer
$name = [System.Text.StringBuilder]::new()
$catList = @{}
foreach($id in 1..$count) {
    $guid = $ms::PtrToStructure([IntPtr]$ptr,[type][guid])
    $null = $auditpol::AuditLookupCategoryName($guid,[ref]$name)
    $catList[$guid] = $name
    $ptr += $size
}
$null = $auditpol::AuditFree($buffer)

# get all subcategories (with optional name):
$guid = [guid]::Empty
$null = $auditpol::AuditEnumerateSubCategories($guid, $true, [ref]$buffer, [ref]$count)
$ptr = [int64]$buffer
$subList = @{}
foreach($id in 1..$count) {
    $guid = $ms::PtrToStructure([IntPtr]$ptr,[type][guid])
    $null = $auditpol::AuditLookupSubCategoryName($guid,[ref]$name)
    $pol  = getPolicyInfo $guid
    $data = [psCustomObject]@{
        category = $catList[$pol.category]
        subcategory = $name
        success = $pol.success
        failure = $pol.failure
    }
    $subList[$guid.guid] = $data
    $ptr += $size
}
$null = $auditpol::AuditFree($buffer)

# listing all subCategories and their audit settings:
$subList.Values | sort category, subcategory | ft -AutoSize

# gettings the audit-settings for a given subcategory-GUID (without '{}'):
$process_creation_guid = '0CCE922B-69AE-11D9-BED3-505054503030'
$subList[$process_creation_guid]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mathias R. Jessen
Solution 2 Cpt.Whale
Solution 3 Carsten