Как узнать, кто удалил файл на файловом сервере Windows | Windows для системных администраторов

Простая система аудита удаления файлов и папок для Windows Server

Любой администратор Windows сталкивался с ситуацией, когда разъяренные пользователи хотят узнать, кто именно удалил мега важный файл с годовым отчетом в общей папке на файловом сервере. Эту информацию можно получить только при условии ведения аудита удаления файлов и папок на файловом сервере, иначе останется только восстановить удаленный файл из резервной копии (а вы их уже делаете?)  и развести руками.

Но, даже при включенном аудите удаления файлов, найти что-то в логах бывает проблематично. Во-первых, найти нужную запись среди тысячи событий довольно сложно (в Windows отсутствуют вменяемые средства поиска интересующего события с возможностью гибкой фильтрации), а во-вторых, если файл был удален давно, это событие может просто отсутствовать в журнале, т.к. было перезатерто более новыми.

В этой статье мы покажем пример организации на встроенных средствах Windows системы аудита удаления файлов и папок в общем сетевом каталоге (файловом сервере) с записью событий в отдельную базу данных на MySQL.

Благодаря наличию БД с информацией обо всех удаленных файлах администратор сможет дать ответы на вопросы:

  • Кто и когда удалил файл
  • Из какого приложения удален файл
  • На какой момент времени нужно восстанавливать бэкап

В первую очередь на файловом сервере Windows нужно включить аудит событий, обеспечивающий запись информации об удалении файлов в журнал системы. Эту процедуру мы уже рассматривали в статье Аудит доступа к файлам и папкам в Windows.

Аудит может быть включен через общую политику Audit Object Access в разделе политик Security Settings -> Local Policy -> Audit Policy

Групповая политика аудита доступа к объектам файловой системыИли (предпочтительнее) через расширенные политики аудита в GPO: Security Settings -> Advanced Audit Policy Configuration -> Object Access -> Audit File System.

Audit File System - аудит доступа к файлам

Совет. Ведения аудита накладывает дополнительные расходы на ресурсы системы. Нужно с осторожностью применять его, особенно для высоконагруженных файловых серверов.

В свойствах общей сетевой  папки (Security -> Advanced -> Auditing), удаление файлов в котором мы хотим отслеживать, для группы Everyone включим аудит событий удаления папок и файлов (Delete subfolders and files).

Аудит удаления файлов в каталоге

Совет. Аудит удаления файлов в конкретной папке можно включить и через PowerShell:

$Path = "D:\Public"
$AuditChangesRules = New-Object System.Security.AccessControl.FileSystemAuditRule('Everyone', 'Delete,DeleteSubdirectoriesAndFiles', 'none', 'none', 'Success')
$Acl = Get-Acl -Path $Path
$Acl.AddAuditRule($AuditChangesRules)
Set-Acl -Path $Path -AclObject $Acl

При успешном удалении файла в журнале безопасности системы появляется событие Event ID 4663 от источника Microsoft Windows security auditing. В описании события есть информация об имени удаленного файла, учетной записи из-под которой было выполнено удаление и имени процесса.

Событие 4663 - файл был удален

Итак, интересующие нас события пишутся в журнал, настала пора создать на сервере MySQL таблицу, состоящую из следующих полей:

  • Имя сервера
  • Имя удаленного файла
  • Время удаления
  • Имя пользователя, удалившего файл

MySQL запрос на создание такой таблицы будет выглядеть так:

CREATE TABLE track_del (id INT NOT NULL AUTO_INCREMENT, server VARCHAR(100), file_name VARCHAR(255), dt_time  DATETIME, user_name VARCHAR(100),  PRIMARY KEY (ID));

Примечание. Особенности работы с MySQL базой мы подробно рассматривали в статье Работаем с базой данных MySQL из PowerShell

Скрипт сбора информации из журнала событий. Мы фильтруем журнал по событию с ID 4663 за текущий день

$today = get-date -DisplayHint date -UFormat %Y-%m-%d
Get-WinEvent -FilterHashTable @{LogName="Security";starttime="$today";id=4663} | Foreach {
$event = [xml]$_.ToXml()
if($event)
{
$Time = Get-Date $_.TimeCreated -UFormat "%Y-%m-%d %H:%M:%S"
$File = $event.Event.EventData.Data[6]."#text"
$User = $event.Event.EventData.Data[1]."#text"
$Computer = $event.Event.System.computer
}
}

Имеющиеся данные в событии удаления файла

Следующий скрипт запишет полученные данные в БД MySQL на удаленном сервере:

Set-ExecutionPolicy RemoteSigned
Add-Type –Path ‘C:\Program Files (x86)\MySQL\MySQL Connector Net 6.9.8\Assemblies\v4.5\MySql.Data.dll'
$Connection = [MySql.Data.MySqlClient.MySqlConnection]@{ConnectionString='server=10.7.7.13;uid=posh;pwd=P@ssw0rd;database=aduser'}
$Connection.Open()
$sql = New-Object MySql.Data.MySqlClient.MySqlCommand
$sql.Connection = $Connection
$today = get-date -DisplayHint date -UFormat %Y-%m-%d
Get-WinEvent -FilterHashTable @{LogName="Security";starttime="$today";id=4663} | Foreach {
$event = [xml]$_.ToXml()
if($event)
{
$Time = Get-Date $_.TimeCreated -UFormat "%Y-%m-%d %H:%M:%S"
$File = $event.Event.EventData.Data[6]."#text"

$File = $File.Replace(‘\’,’|’)
$User = $event.Event.EventData.Data[1]."#text"
$Computer = $event.Event.System.computer
$sql.CommandText = "INSERT INTO track_del  (server,file_name,dt_time,user_name ) VALUES ('$Computer','$File','$Time','$User')"
$sql.ExecuteNonQuery()
}
}
$Reader.Close()
$Connection.Close()

Теперь, чтобы узнать, кто удалил файл «document1 — Copy.DOC», достаточно в консоли PowerShell выполнить следующий скрипт.

$DeletedFile = "%document1 - Copy.DOC%"
Set-ExecutionPolicy RemoteSigned
Add-Type –Path ‘C:\Program Files (x86)\MySQL\MySQL Connector Net 6.9.8\Assemblies\v4.5\MySql.Data.dll'
$Connection = [MySql.Data.MySqlClient.MySqlConnection]@{ConnectionString='server=10.7.7.13;uid=posh;pwd=P@ssw0rd;database=aduser'}
$Connection.Open()
$MYSQLCommand = New-Object MySql.Data.MySqlClient.MySqlCommand
$MYSQLDataAdapter = New-Object MySql.Data.MySqlClient.MySqlDataAdapter
$MYSQLDataSet = New-Object System.Data.DataSet
$MYSQLCommand.Connection=$Connection
$MYSQLCommand.CommandText="SELECT user_name,dt_time    from  track_del where file_name LIKE '$DeletedFile'"
$MYSQLDataAdapter.SelectCommand=$MYSQLCommand
$NumberOfDataSets=$MYSQLDataAdapter.Fill($MYSQLDataSet, "data")
foreach($DataSet in $MYSQLDataSet.tables[0])
{
write-host "User:" $DataSet.user_name "at:" $DataSet.dt_time
}
$Connection.Close()

В консоли получаем имя пользователя и время удаления файла.

Запрос PowerShell к mysql БД для получении инфомации о пользователе, удалившем файл

Примечание. Т.к. была обнаружена проблема, чир символ «\» не записывается в БД, мы заменили его на «|». Соответственно если нужно указать вывести полный путь к файлу , при выборке из базы можно выполнить обратную замену $DataSet.file_name.Replace(‘|’,’\’). Спасибо Alex Kornev за замечание!

Скрипт сброса данных из журнала в БД можно выполнять один раз в конце дня по планировщику или повесить на событие удаления (On Event), что более ресурсоемко. Все зависит от требования к системе.

Совет. Нужно убедиться, что журнал безопасности имеет достаточный размер, чтобы в него помещались все события за день. Иначе придется запускать задания сброса данных из журнала в базу чаще, чем 1 раз в день, или вообще по триггеру. Для рабочих станция Maximum Log Size как правило стоит задать не менее 64 Мб, на северах – 262 Мб. Опцию перезаписи оставляем включенной (Overwrite events as needed).

При желании по аналогии можно реагировать простую веб страницу на php для получения информации о виновниках удаления файлов в более удобном виде. Задача решается силами любого php программиста за 1-2 часа.

Важный совет. При наличии в журнале информации об удалении файла пользователем не спешите однозначно интерпретировать его как преднамеренное или даже злонамеренное. Многие программы (особенно этим грешат программы пакета MS Office), при сохранении данных сначала создают временный файл, сохраняют документ в него, а старую версию файла удаляют.  В этом случае имеет смысл дополнительной записи в БД имени процесса, которым было выполнено удаление файла (поле ProcessName события), и вести анализ удаления файлов с учетом этого факта. Либо совсем радикально отсеивать события от таких мусорных процессов, например, winword.exe,  excel.exe и пр.

Итак, мы предложили идею и некий общий каркас системы аудита и хранения информации об удаленных файлах в сетевых шарах, при желании ее с лёгкостью можно будет модифицировать под ваши нужды.

Еще записи по теме: Windows Server 2012 R2
Понравилась статья? Скажи спасибо и расскажи друзьям!
Назад:
Вперед:

Комментариев: 17

Оставить комментарий
  1. Alex Kornev | 04.05.2016

    Предполагается, видимо, что MySQL уже установлен и настроен?
    100 лет уже не имел дела с «мускулом»…

    Ответить
    • itpro | 04.05.2016

      Да, конечно. Причем он даже под Windows ставится несколькими кликами.
      В общем-то код и всю технику можно легко адаптировать и под MSSql, но с точки зрения зрения легкости, отдаю предпочтению мусклу.

      Ответить
  2. Alex Kornev | 04.05.2016

    Извини если туплю, но из статьи НЕ очевтдно

    куда засунуть и как запустить скрипт
    «Скрипт сбора информации из журнала событий. Мы фильтруем журнал по событию с ID 4663 за текущий день»

    а за ним все остальные

    Ответить
    • itpro | 04.05.2016

      Подразумевал, что раз в сутки планировщик (допустим в 23:58) запускает powershell скрипт, который получает все события за прошедшие сутки и пишет из в базу.
      Т.е. в создаем задание планировщика, которое запускет программу C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe с аргументом в виде имени файла со вторым скриптом C:\myscripts\WriteDelEventDB.PS1

      «Скрипт сбора информации из журнала событий» — это просто пример работы с журналом событий, фильтрации и получения значений определенных полей. Он используется внутри следующего скрипта.

      Ответить
  3. Alex Kornev | 04.05.2016

    Что-то пошло не так. Или — что я сделал не так?
    На первой же строке скрипта вылезает матершина:
    (все делается под одним из Domain Admin аккаунтов)

    PS C:\Users\admin> Get-WinEvent -FilterHashTable @{LogName=»Security»;starttime=»$today»;id=4663}

    Get-WinEvent : Could not retrieve information about the Security log. Error: Attempted to perform
    an unauthorized operation..
    At line:1 char:1
    + Get-WinEvent -FilterHashTable @{LogName=»Security»;starttime=»$today»;id=4663}
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-WinEvent], Exception
    + FullyQualifiedErrorId : LogInfoUnavailable,Microsoft.PowerShell.Commands.GetWinEventCommand

    Get-WinEvent : There is not an event log on the localhost computer that matches «Security».
    At line:1 char:1
    + Get-WinEvent -FilterHashTable @{LogName=»Security»;starttime=»$today»;id=4663}
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (Security:String) [Get-WinEvent], Exception
    + FullyQualifiedErrorId : NoMatchingLogsFound,Microsoft.PowerShell.Commands.GetWinEventCommand

    Get-WinEvent : The parameter is incorrect
    At line:1 char:1
    + Get-WinEvent -FilterHashTable @{LogName=»Security»;starttime=»$today»;id=4663}
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-WinEvent], EventLogException
    + FullyQualifiedErrorId : System.Diagnostics.Eventing.Reader.EventLogException,Microsoft.Power
    Shell.Commands.GetWinEventCommand

    Ответить
  4. Alex Kornev | 04.05.2016

    Пардону просим
    Run As Administrator забыл сделать Ж-)

    Ответить
  5. sidiys@t-blog.ru | 04.05.2016

    Здравствуйте, отличная статья. Решал подобную задачу для сбора статистики с KMS сервера, но использовал связку PS и MSSQL Express. Через год работы наткнулся на проблему, что превысил лимит базы данных в 10Гб, в итоге сделал ротацию данных в БД Наверное в вашем случае тоже было бы неплохо предусмотреть удаление старых данных? Могу поделиться наработками, если интересно. :)

    Ответить
    • itpro | 05.05.2016

      Приветствую,
      Насчет удаления старых данных пока не думал. Сложно оценить насколько будет большой база и за какой период нужна информация. Здесь больше идея рассматривается, какие-то выводы и усовершенствования можно делать только после опыта более продолжительного использования.

      А инфой конечно, делитесь! Можно оформить отдельной статьей, думаю тема сбора и хранения статистики использования KMS сервера будет многим интересна.
      Мой емайл есть на странице _http://winitpro.ru/index.php/o-bloge/

      Ответить
  6. Alex Kornev | 05.05.2016

    Странно как-то себя MySQL повел ….
    Поставил, для удобства, Navicat Premium Enterprise 11.2.4
    (http://nnm.me/blogs/MANS0RY/premiumsoft-navicat-premium-11-2-4-enterprise/)
    и получается как-то странно
    если делать, этим Navicat, Export всей таблицы из базы, то в поле file_name попадает имя файла целиком (правда отделенное пробелом почему-то от всего остального)

    «id» «server» «file_name» «dt_time» «user_name»
    «1440» «AUDITTEST» «DeviceHarddiskVolume2TEST 0000.txt» «5.5.2016 12:12:24″ «name»

    а когда делаешь select … то имя файла обрезается до полного пути до него
    SELECT track_del.file_name,track_del.dt_time,track_del.user_name FROM track_del
    а на выходе
    file_name dt_time user_name
    DeviceHarddiskVolume2TEST 2016-05-05 12:12:24 name

    Если присмотреться, то в поле file_name попадается пробел. И, походу, по нему и режется. А пока оно так себя ведет, дальше ничего не работает. То есть невозможно сделать LIKE по значению, которое не показывается

    Ответить
    • Alex Kornev | 05.05.2016

      Починил.
      В скрипте, который заполняет таблицу в БД, вставил строку, которая меняет в имени файла, получаемого из Security Even Log’a все ‘\’ на ‘|’ Вот здесь:

      $File = $event.Event.EventData.Data[6].»#text»
      $File = $File.Replace(‘\’,’|’)

      После этого имя файла перестало «теряться» и попадает в БД как надо :-)

      И потом, слегка «доработал» скрипт, который выдает результат. Мне плказалось так и красивее, и правильнее. Добавлен вывод имени файла с полным путем. с обратной заменой ‘|’ на ‘\’

      foreach($DataSet in $MYSQLDataSet.tables[0])
      {
      write-host «File:» $DataSet.file_name.Replace(‘|’,’\’) — «User:» DataSet.user_name «at:» $DataSet.dt_time
      }

      Ответить
      • Alex Kornev | 06.05.2016

        А ведь это был _очень_неприятный баг MySQL, на самом деле

        Файл, на котором я «тренировался», я назвал 00000.txt (5 нулей) и, в итоге, в базу из Event Log’a попадало вот что
        DeviceHarddiskVolume2TEST 0000.txt
        То есть при выполнении INSERT MySQL «выгрызал» все символы ‘\’ и, более того делал из подстроки 0000.txt такое вот "[SPACE]0000.txt" типа пытался «интерпретировать по своему»?
        А после того как я, предварительно, заменял все ‘\’ на ‘|’ — все проходило как надо

        Дмитрий, думаю не лишним будет или упомянуть об этой «особенности» или поправить скрипт, который заносит имя файла в таблицу БД.

        Ответить
        • itpro | 06.05.2016

          Видимо проблема в сочетании "\"+"0", которое как-то интерпретируется mysql, то ли пустая строка, то ли что-то в этом роде.
          Согласен, в скрипте лучше всего заменить ‘\’ на ‘|’, а при выборке делать обратную замену.
          Сейчас внесу изменения в статью. Спасибо!

          Ответить
  7. Alexandr | 06.05.2016

    Коллеги кто нибуть настраивал данную возможность с помощью SCOM?
    Есть ли информация по данному вопросу,может быть кто то уже имел опыт,заранее спасибо за ответ.

    Ответить
  8. Alex Kornev | 06.05.2016

    Странная команда в скрите
    «Следующий скрипт запишет полученные данные в БД MySQL на удаленном сервере:»
    вот эта

    $Reader.Close()

    PowerShell ругается на нее. Наверное и правильно. Ведь закрывается то, что не открывалось.

    Ответить
  9. Lev | 25.11.2016

    А для Win2k как сделать подобное?

    В нем аудит естьт, политика безопасности — есть, а события 4663 не выдается (((

    Может кто сталкивался?

    Ответить
    • itpro | 29.11.2016

      ID события скорее всего другие, да и не факт что PowerShell вообще заведется на W2k..
      ЗЫ. Закапывайте мамонта

      Ответить
Полные правила комментирования на сайте winitpro.ru. Вопросы, не связанные с содержимым статьи или ее обсуждением удаляются.

Сказать Спасибо! можно на этой странице или (еще лучше) поделиться с друзями ссылкой на понравившуюся статью в любимой социальной сети(специально для этого на сайте присуствуют кнопки популярных соц. сетей).

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Я не робот( Обязательно отметьте)



MAXCACHE: 0.28MB/0.00112 sec