Пользователь
0,0
рейтинг
29 августа 2012 в 14:56

Администрирование → Аудит удаления и доступа к файлам и запись событий в лог-файл средствами Powershell tutorial


Я думаю, многие сталкивались с задачей, когда к Вам приходят и спрашивают: «У нас тут файл пропал на общем ресурсе, был и не стало, похоже кто-то удалил, Вы можете проверить кто это сделал?» В лучшем случае вы говорите, что у вас нет времени, в худшем пытаетесь найти в логах упоминание данного файла. А уж когда включен файловый аудит на файловом сервере, логи там, мягко говоря «ну очень большие», и найти что-то там — нереально.
Вот и я, после очередного такого вопроса (ладно бекапы делаются несколько раз в день) и моего ответа, что: «Я не знаю кто это сделал, но файл я Вам восстановлю», решил, что меня это в корне не устраивает…

Начнем.


Для начала включим к групповых политиках возможность аудита доступа к файлам и папкам.
Локальные политики безопасности->Конфигурация расширенной политики безопасности->Доступ к объектам
Включим «Аудит файловой системы» на успех и отказ.
После этого на необходимые нам папки необходимо настроить аудит.
Проходим в свойства папки общего доступа на файловом сервере, переходим в закладку «Безопасность», жмем «Дополнительно», переходим в закладку «Аудит», жмем «Изменить» и «Добавить». Выбираем пользователей для которых вести аудит. Рекомендую выбрать «Все», иначе бессмысленно. Уровень применения «Для этой папки и ее подпапок и файлов».
Выбираем действия над которыми мы хотим вести аудит. Я выбрал «Создание файлов/дозапись данных» Успех/Отказ, «Создание папок/дозапись данных» Успех/отказ, Удаление подпапок и файлов и просто удаление, так же на Успех/Отказ.
Жмем ОК. Ждем применения политик аудита на все файлы. После этого в журнале событий безопасности, будет появляться очень много событий доступа к файлам и папкам. Количество событий прямопропорционально зависит от количества работающих пользователей с общим ресурсом, и, конечно же, от активности использования.

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

#Задаем период, в течении которого мы будем запускать один раз скрипт, и искать нужные нам события. Здесь период задан - 1 час. Т.е. проверяются все события за последний час.
$time =  (get-date) - (new-timespan -min 60)

#$BodyL - переменная для записи в лог-файл
$BodyL = ""

#$Body - переменная, в которую записываются ВСЕ события с нужным ID. 
$Body = Get-WinEvent -FilterHashtable @{LogName="Security";ID=4663;StartTime=$Time}|where{ ([xml]$_.ToXml()).Event.EventData.Data |where {$_.name -eq "ObjectName"}|where {($_.'#text') -notmatch ".*tmp"} |where {($_.'#text') -notmatch ".*~lock*"}|where {($_.'#text') -notmatch ".*~$*"}} |select TimeCreated, @{n="Файл_";e={([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq "ObjectName"} | %{$_.'#text'}}},@{n="Пользователь_";e={([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq "SubjectUserName"} | %{$_.'#text'}}} |sort TimeCreated

#Далее в цикле проверяем содержит ли событие определенное слово (к примеру название шары, например: Secret)	
foreach ($bod in $body){
	if ($Body -match ".*Secret*"){

#Если содержит, то пишем в переменную $BodyL данные в первую строчку: время, полный путь файла, имя пользователя. И #в конце строчки переводим каретку на новую строчку, чтобы писать следующую строчку с данными о новом файле. И так #до тех пор, пока переменная $BodyL не будет содержать в себе все данные о доступах к файлам пользователей.

		$BodyL=$BodyL+$Bod.TimeCreated+"`t"+$Bod.Файл_+"`t"+$Bod.Пользователь_+"`n"
	}
}

#Т.к. записей может быть очень много (в зависимости от активности использования общего ресурса), то лучше разбить лог #на дни. Каждый день - новый лог. Имя лога состоит из Названия AccessFile и даты: день, месяц, год.
$Day = $time.day
$Month = $Time.Month
$Year = $Time.Year
$name = "AccessFile-"+$Day+"-"+$Month+"-"+$Year+".txt"
$Outfile = "\serverServerLogFilesAccessFileLog"+$name

#Пишем нашу переменную со всеми данными за последний час в лог-файл.
$BodyL | out-file $Outfile -append


А теперь очень интересный скрипт.



Скрипт пишет лог об удаленных файлах.

#Переменная $Time тут имеет такое же назначение как в предыдущем скрипте.

$time =  (get-date) - (new-timespan -min 60)

#$Events - содержит время и порядковый номер записи евента с ID=4660. И сортируем по порядковому номеру.
#!!!!Это важное замечание!!! При удалении файла создается сразу 2 записи, с ID=4660 и ID=4663.
$Events = Get-WinEvent -FilterHashtable @{LogName="Security";ID=4660;StartTime=$time} | Select TimeCreated,@{n="Запись";e={([xml]$_.ToXml()).Event.System.EventRecordID}} |sort Запись

#Самые важные команды поиска. Опишу принцип ниже, после листинга скрипта.
$BodyL = ""
$TimeSpan = new-TimeSpan -sec 1
foreach($event in $events){
	$PrevEvent = $Event.Запись
	$PrevEvent = $PrevEvent - 1
	$TimeEvent = $Event.TimeCreated
	$TimeEventEnd = $TimeEvent+$TimeSpan
	$TimeEventStart = $TimeEvent- (new-timespan -sec 1)
	$Body = Get-WinEvent -FilterHashtable @{LogName="Security";ID=4663;StartTime=$TimeEventStart;EndTime=$TimeEventEnd} |where {([xml]$_.ToXml()).Event.System.EventRecordID -match "$PrevEvent"}|where{ ([xml]$_.ToXml()).Event.EventData.Data |where {$_.name -eq "ObjectName"}|where {($_.'#text') -notmatch ".*tmp"} |where {($_.'#text') -notmatch ".*~lock*"}|where {($_.'#text') -notmatch ".*~$*"}} |select TimeCreated, @{n="Файл_";e={([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq "ObjectName"} | %{$_.'#text'}}},@{n="Пользователь_";e={([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq "SubjectUserName"} | %{$_.'#text'}}} 
	if ($Body -match ".*Secret*"){
		$BodyL=$BodyL+$Body.TimeCreated+"`t"+$Body.Файл_+"`t"+$Body.Пользователь_+"`n"
	}
}


$Month = $Time.Month
$Year = $Time.Year
$name = "DeletedFiles-"+$Month+"-"+$Year+".txt"
$Outfile = "\serverServerLogFilesDeletedFilesLog"+$name


$BodyL | out-file $Outfile -append


Как оказалось при удалении файлов и удалении дескрипторов создается одно и тоже событие в логе, под ID=4663. При этом в теле сообщения могут быть разные значения «Операции доступа»: Запись данных (или добавление файла), DELETE и т.д.
Конечно же нас интересует операция DELETE. Но и это еще не все. Самое интересное, то что, при обычном переименовании файла создается 2 события с ID 4663, первое с Операцией доступа: DELETE, а второе с операцией: Запись данных (или добавление файла). Значит если просто отбирать 4663 то мы будем иметь очень много недостоверной информации: куда попадут файлы и удаленные и просто переименованные.
Однако мной было замечено, что при явном удалении файла создается еще одно событие с ID 4660, в котором, если внимательно изучить тело сообщения, содержится имя пользователя и еще много всякой служебной информации, но нет имени файла. Зато есть код дескриптора.

Однако предшествующим данному событию было событие с ID 4663. Где как раз таки и указывается и имя файла, и имя пользователя и время, и операция как не странно там DELETE. И самое главное там имеется номер дескриптора, который соответствует номеру дескриптора из события выше (4660, помните? которое создается при явном удалении файла). Значит теперь, чтобы точно знать какие файлы удалены, необходимо просто найти все события с ID 4660, а так же предшествующие каждому этому событию, событие с кодом 4663, в котором будет содержаться номер нужного дескриптора.

Эти 2 события генерируются одновременно при удалении файла, но записываются последовательно, сначала 4663, потом 4660. При этом их порядковые номера различаются на один. У 4660 порядковый номер на единицу больше чем у 4663.
Именно по этому свойству и ищется нужное событие.


Т.е. берутся все события с ID 4660. У них берется 2 свойства, время создания и порядковый номер.
Далее в цикле по одному берется каждое событие 4660. Выбирается его свойства, время и порядковый номер.
Далее в переменную $PrevEvent заносится номер нужного нам события, где содержится нужная информация об удаленном файле. А так же определяются временные рамки в которых необходимо искать данное событие с определенным порядковым номером (с тем самым который мы занесли в $PrevEvent). Т.к. событие генерируется практически одновременно, то поиск сократим до 2х секунд: + — 1 секунда.
(Да, именно +1 сек и -1 сек, почему именно так, не могу сказать, было выявлено экспериментально, если не прибавлять секунду, то некоторые может не найти, возможно связано с тем, что возможно два эти события могут создаваться один раньше другой позже и наоборот).
Сразу оговорюсь, что искать только по порядковому номеру по всем событиям в течении часа — очень долго, т.к. порядковый номер находиться в теле события, и чтобы его определить, нужно пропарсить каждое событие — это очень долго. Именно поэтому необходим такой маленький период в 2 секунда (+-1сек от события 4660, помните?).
Именно в этом временном промежутке ищется событие с необходимым порядковым номером.
После того как оно найдено, работают фильтры:
|where{ ([xml]$_.ToXml()).Event.EventData.Data |where {$_.name -eq "ObjectName"}|where {($_.'#text') -notmatch ".*tmp"} |where {($_.'#text') -notmatch ".*~lock*"}|where {($_.'#text') -notmatch ".*~$*"}}


Т.е. не записываем информацию об удаленных временных файлах (.*tmp), файлах блокировок документов MS Office (.*lock), и временных файлах MS Office (.*~$*)
Таким же образом берем необходимые поля из этого события, и пишем их в переменную $BodyL.
После нахождения всех событий, пишем $BodyL в текстовый файл лога.

Для лога удаленных файлов я использую схему: один файл на один месяц с именем содержащим номер месяца и год). Т.к. удаленных файлов в разы меньше чем файлов к которым был доступ.

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

Рекомендации


Вам придется самим определить время в течении которого вы будете искать нужные события. Чем больше период, тем дольше ищет. Все зависит от производительности сервера. Если слабенький — то начните с 10 минут. Посмотрите, как быстро отработает. Если дольше 10 минут, то либо увеличьте еще, вдруг поможет, либо наоборот уменьшите период до 5 минут.

После того как определите период времени. Поместите данный скрипт в планировщик задач и укажите, что выполнять данный скрипт необходимо каждые 5,10,60 минут (в зависимости какой период вы указали в скрипте). У меня указано каждый 60 минут. $time = (get-date) — (new-timespan -min 60).

PS


У меня оба эти скрипта работают для сетевого ресурса в 100Гб, на котором ежедневно активно работают в среднем 50 пользователей.
Время поиска удаленных файлов за час — 10-15 минут.
Время поиска всех файлов, к которым был доступ — от 3 до 10 минут. В зависимости от нагрузки на сервер.
Андрей @Deks
карма
14,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Администрирование

Комментарии (20)

  • 0
    Я использую для этих целей Diskeeper Undelete, поиск удаленного файла занимает около 10-15 секунд, так как программа записывает путь до директории откуда был удален файл, одной кнопкой файл можно вернуть на место, так же можно посмотреть какой юзер его удалил.
  • 0
    Возможно придется настроить размер журнала Security, если событий слишком много и старые события перезатираются.
    Если событий реально много — тогда имеет смысл включить архивирование журналов (параметр Archive the log when full, do not overwrite events), уменьшить размер лога, скажем до 10 мегабайт. Потом модифицировать скрипт, чтобы он ходил по архивным логам, парсил их и удалял обработанные.
    • +1
      Слишком больше логи ооочень долго парсит. Лучше запускать скрипт с более-менее нормальным периодом, и смысла не будет хранить виндовые логи в архивах. Слишком уже они неудобные.
  • –1
    Какой красивый значок терминала!)
  • 0
    На боевых серверах парсить журнал PowerShell-ом — проще застрелиться.
    У нас есть самодельный сервис, который подписывается на события eventlog-а и сливает события в MS SQL прямо как есть в xml-виде (кидает в SQLite, если MSSQL временно недоступен). Благо, MSSQL-сервер позволяет индексировать такие данные и делать хорошие выборки с использованием xpath-синтаксиса.
    • 0
      почему считаете что проще застрелится… вполне себе нормально отрабатывает. Все конечно зависит от объема, но в моем случае вполне даже себе справляется. Конечно проиндексированные данные из БД парсить куда удобнее. Поделитесь?)
      • 0
        Неудобства PS:
        1. долго работает (конечно, если искать за нужный день. сливать за последние N минут не пробовал)
        2. полные данные некоторых событий (например, редактирование объекта AD) нужно собирать по нескольким event-ам, связывая по Correlation GUID. join прямо таки просится

        поделиться не могу — в рабочее время написано по тз.
        заняло 2 дня: 1) копипаста с MSDN готовых кусков (скелет службы и разные варианты подписки на eventlog есть в законченных примерах), 2) отладка опечаток
  • 0
    Я у нас делал так: С помощью Snare конвертировал Eventlog в Syslog и отправлял его на соседний Linux — сервер, который скриптом на перле обрабатывал каждую строку и делал выводы по этим шаблонам:

    if($_[0]=~m/READ_CONTROL ReadData \(or ListDirectory\) ReadEA ReadAttributes/i){return OPEN;}
    elsif($_[0]=~m/READ_CONTROL ReadData \(or ListDirectory\) WriteData \(or AddFile\) AppendData \(or AddSubdirectory or CreatePipeInstance\) ReadEA WriteEA ReadAttributes WriteAttributes/i){return EDIT;}
    elsif($_[0]=~m/DELETE ReadAttributes/i){return DELETE;}
    elsif($_[0]=~m/READ_CONTROL WRITE_DAC ReadData \(or ListDirectory\) WriteData \(or AddFile\) AppendData \(or AddSubdirectory or CreatePipeInstance\) ReadEA WriteEA ReadAttributes WriteAttributes/i){return PASTE;}
    elsif($_[0]=~m/DELETE SYNCHRONIZE/i){return MOVE;}
    elsif($_[0]=~m/DELETE READ_CONTROL ReadData \(or ListDirectory\) WriteData \(or AddFile\) AppendData \(or AddSubdirectory or CreatePipeInstance\) WriteEA ReadAttributes WriteAttributes/i){return MOVE;}

    Потом это все писалось в Mysql и отчеты делались уже по ней, не нагружая сервер с виндой.
    Но, конечно, у вас проверки более детальные, у меня определял иногда действия ошибочно и генерировал очень много лишних логов. Надо переписать согласно вашей статье, спасибо!
    • 0
      У меня так же работает SYSlog сервер который собирает себе в MySQLную базу все логи с Windows-серверов. Заметил только, что неокторые логи приходят обрезаные:( Поле Message, если оно достаточно большое, обрезается, видимо. Проверил прямым запросом к базе, и там они так же обрезаны. Так же заметил, что некоторые логи вообще пустые в поле Message. Возможно проблема конечно с клиентом который кидает все это на сислог-сервер. У меня Evtsys используется.
      Кстати вопрос как раз таки… кто нибудь вкурсе как кидать алерты с rsyslog-сервера в Jabber? :)
      • 0
        Проблема в клиенте — Evtsys тоже у меня обрезал. Попробуйте Snare.
        • 0
          О! спасибо огромное за направление.
          Для страждущих, SNARE тут.
          Плюсанул бы, но не могу:(
  • 0
    Андрей, Спасибо за очередную psвкусняшку! (особо про удаленку)
    завтра бум пробовать ;-)
  • 0
    Может, имеет смысл дополнить — что для windows 2003 надо искать другие event Id — 564 и 560 соответственно?
    • 0
      Ды и Get-WinEvent там нету, может есть спецы которые перепишут скрипты под Win2003? Буду благодарен.
  • 0
    Я правильно понимаю, что
    1. операция 4663 с DELETE-ом означает ЛИБО переименование, ЛИБО удаление?
    2. если в течении нескольких секунд до или после такого 4663 с DELETE-ом мы видим 4660 и Handler-ы совпадают, то значит что операция была удаление
    3. А как «связать» переименование? Я пропустил. Как понять, как новое имя файла?
    • 0
      После публикации данного поста, я немного изменил скрипты, добавил запись логов не в файл, а в SQL. И переписал все скрипты на .NET C# в виде Windows-службы, которая все время висит в фоне и парсит лог. Работает в разы быстрее — и ест всего 8МБ памяти (судя по диспетчеру задач).

      1. все верно.
      2. да
      3. К сожалению новое имя файла не фиксируется в логах. Возможно стоит поиграться с политиками аудита, но объем логов может вырасти в десятки раз.
      При переименовании файла фиксируется доступ к данному файлу и папке (4663), которая его содержит.

      В дополнении хотелось бы еще указать на еще один евент под номером 4659.

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

      • 0
        Я понимаю, что очень нагло, но все же спрошу. Не могли бы вы поделиться своим произведением?
        • 0
          В общем то могу конечно. Вы можете мне отписать в аську 375173554 или в скайп: deks__

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.