Functions/SystemAudit/Get-SystemAuditFileSystem.ps1

<#
    .SYNOPSIS
        Get all audit events of the file system.
 
    .DESCRIPTION
        This function will show all file system audit event logs. This is only
        the security event with id 4663.
 
    .INPUTS
        None.
 
    .OUTPUTS
        SecurityFever.SystemAudit.Event.
 
    .EXAMPLE
        PS C:\> Get-SystemAuditFileSystem
        Get the local file system audit events.
 
    .LINK
        https://github.com/claudiospizzi/SecurityFever
#>

function Get-SystemAuditFileSystem
{
    [CmdletBinding()]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')]
    param
    (
        # Period of days to cover.
        [Parameter(Mandatory = $false)]
        [System.Int32]
        $DayPeriod = 7,

        # Show extended events, not only the important ones.
        [Parameter(Mandatory = $false)]
        [Switch]
        $Extended,

        # Hide the warning messages, don't test the prerequisites.
        [Parameter(Mandatory = $false)]
        [Switch]
        $HideWarning
    )

    Test-AdministratorRole -Throw

    Show-SystemAuditEventLogWarning -LogName 'Security' -DayPeriod $DayPeriod -HideWarning:$HideWarning.IsPresent
    Show-SystemAuditPolicyWarning -Category 'Object Access' -Subcategory 'File System' -Setting 'Success' -HideWarning:$HideWarning.IsPresent

    # Get all relevant event log records for the file system
    $filter = New-WinEventFilterXml -LogName 'Security' -EventId 4663 -NotBefore ([System.DateTime]::Now.AddDays(-1 * $DayPeriod))
    try
    {
        $records = Get-WinEvent -FilterXml $filter -ErrorAction 'Stop'
    }
    catch
    {
        if ($_.Exception.Message -ne 'No events were found that match the specified selection criteria.')
        {
            throw $_
        }
        else
        {
            $records = @()
        }
    }

    foreach ($record in $records)
    {
        $recordId = $record.Id

        # Reference:
        # https://system32.eventsentry.com/security/event/4663
        # https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4663

        # We need to split the access because it can contain multiple
        # values like '%%4417 %%4418', for such cases, we just return
        # multiple event object.
        $accessList = [System.String] $record.Properties[8].Value
        $accessList = $accessList.Split("`n ")
        foreach ($access in $accessList)
        {
            $access = $access.Trim()
            if ([System.String]::IsNullOrEmpty($access))
            {
                continue
            }

            switch ($access)
            {
                '%%4416' { $access = 'ReadData / ListDirectory' }
                '%%4417' { $access = 'WriteData / AddFile' }
                '%%4418' { $access = 'AppendData / AddSubdirectory / CreatePipeInstance' }
                '%%4419' { $access = 'ReadExtendedAttributes' }
                '%%4420' { $access = 'WriteExtendedAttributes' }
                '%%4421' { $access = 'Execute/Traverse' }
                '%%4422' { $access = 'DeleteChild' }
                '%%4423' { $access = 'ReadAttributes' }
                '%%4424' { $access = 'WriteAttributes' }
                '%%1537' { $access = 'Delete' }
                '%%1538' { $access = 'ReadAccessControl' }
                '%%1539' { $access = 'WriteAccessControl' }
                '%%1540' { $access = 'WriteOwner' }
                '%%1541' { $access = 'Synchronize' }
                '%%1542' { $access = 'AccessSysSec' }
                default  { Write-Warning "Access definition for '$access' not found." }
            }

            # Create a usable event by parsing the properties. But most of the
            # properties are currently not in use:
            # SubjectUserSid (0) = S-1-5-...
            # SubjectLogonId (3) = 0x12345678
            # ObjectServer (4) = Security
            # ObjectType (5) = File
            # ObjectName (6) =
            # HandleId (7) = 0x12345
            # AccessMask (9) = 0x1
            # ProcessId (10) = 12345
            # ProcessName (11) = C:\Windows\explorer.exe
            # ResourceAttributes (12) = S:AI
            $auditEvent = [PSCustomObject] @{
                PSTypeName  = 'SecurityFever.SystemAudit.Event'
                Timestamp   = $record.TimeCreated
                Machine     = $record.MachineName
                User        = Get-WinEventRecordUser -Record $record
                Component   = 'File System'
                Action      = $access
                Context     = [System.String] $record.Properties[6].Value
                Detail      = 'Subject: {0}\{1}' -f $record.Properties[2].Value, $record.Properties[1].Value
                SourcePath  = '/EventLog/Security/Record[@Id={0}]' -f $recordId
                SourceEvent = $record
            }

            Write-Output $auditEvent
        }
    }
}