Who deleted the files from the Windows file server?

Classic whodunit – the file is gone. Who deleted it?

Well, unless you’ve already prepared for this, Windows has no log for you. Sorry.
The good news is that this is an excellent time to prepare for the next time. So let’s do that.

Enable the auditing of file operations to the Windows Event Log

Netwrix has documented the procedure for this. The relevant steps for us are:

  1. Navigate to the file share, right-click it and select “Properties” Select the “Security” tab → “Advanced” button → “Auditing” tab → Click “Add” button:
    • Select Principal: “Everyone”; Select Type: “All”; Select Applies to: “This folder, subfolders and files”; Select the following “Advanced Permissions”: “Delete subfolders and files” and “Delete”.
  2. Run gpedit.msc, create and edit new GPO → Computer Configuration → Policies → Windows Settings → Security Settings → Go to Local Policies → Audit Policy:
    • Audit object access → Define → Success and Failures.
  3. Go to “Advanced Audit Policy Configuration” → Audit Policies → Object Access:
    • Audit File System → Define → Success and Failures
    • Audit Handle Manipulation → Define → Success and Failures.
  4. Link new GPO to File Server and force the group policy update.
  5. Open Event viewer and search Security log for event ID 4656 with “File System” or “Removable Storage” task category and with “Accesses: DELETE” string. “Subject: Security ID” will show you who has deleted a file.

Extract the logs

I wrote a short PowerShell script to do this. It’s not very efficient, but it does the job, and is relatively readable 🙂

"Chewing through the log files..."
Get-WinEvent -LogName "Security" | Where-Object { $_.Id -eq 4656 } | ForEach-Object { [xml]($_.ToXml()) } | Select -ExpandProperty "Event" | Where-Object `
    ( $_.EventData.Data | Where-Object { $_.Name -eq "AccessList" -and $_."#text" -like "*%%1537*" } ) -and `
    ( $_.EventData.Data | Where-Object { $_.Name -eq "ObjectName" -and $_."#text" -like "R:\*" } )
} | ForEach-Object `
    New-Object PSObject -Property (@{
        "TimeStamp"  = ($_.System.TimeCreated.SystemTime)
        "UserName"   = ($_.EventData.Data | Where-Object { $_.Name -eq "SubjectUserName" } | Select -ExpandProperty "#text")
        "UserDomain" = ($_.EventData.Data | Where-Object { $_.Name -eq "SubjectDomainName" } | Select -ExpandProperty "#text")
        "ObjectType" = ($_.EventData.Data | Where-Object { $_.Name -eq "ObjectType" } | Select -ExpandProperty "#text")
	"ObjectName" = ($_.EventData.Data | Where-Object { $_.Name -eq "ObjectName" } | Select -ExpandProperty "#text")
} | Format-Table TimeStamp,UserDomain,UserName,ObjectType,ObjectName -AutoSize
"Press <enter> to quit"
Read-Host | Out-Null

Pro tip: For faster printout (line by line) but worse formatting, remove the -AutoSize parameter to Format-Table

This script specifically looks for files on R:\, but you can change this to whatever you want, or remove that condition all together.

Sample output

And there you go! 🙂


  • Kerlc says:

    Nice, but when you “audit” a 2Tb file share server then usually auditing on share freeze server in a day…
    Or am I wrong ?
    Especialy when you audit whole disk drive with eg 3000 users in ad domain.

    • bolt says:

      I don’t see why it would freeze, given you have enough iron to back that amount of users, but I’d definitely say that if you have 3000 users with write access to a network share, you’re doing something very wrong, and should probably consider sharepoint or something, at least.

Leave a Reply

Your email address will not be published. Required fields are marked *