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

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

Первым делом необходимо было определить список событий, которые необходимо было отслеживать. Чтобы уменьшить количество текста, я создала процедуру, которая по ID события выдает ее описание:

Function DefineReason ($Id){
    switch ($Id){
        4741{ Return "Создана учетна запись компьютера"}
        4742{ Return "Изменена учетна запись компьютера" }
        4743{ Return "Удалена учетная запись компьютера"}
        4727{ Return "Создана глобальная группа с включенной безопасностью"}
        4728{ Return "Добавлен пользователь к глобальной группе с включенной безопасностью"}
        4729{ Return "Удален пользователь из глобальной группы с включенной безопасностью"}
        4730{ Return "Удалена глобальная группа с включенной безопасностью"}
        4731{ Return "Создана локальная группа с включенной безопасностью"}
        4732{ Return "Добавлен пользователь в локальную группу с включенной безопасностью"}
        4733{ Return "Удален пользователь из локальной группы с включенной безопасностью"}
        4734{ Return "Удалена локальная группа с включенной безопасностью"}
        4735{ Return "Изменена локальная группа с включенной безопасностью"}
        4737{ Return "Изменена глобальная группа с включенной безопасностью"}
        4743{ Return "Удалена учетная запись компьютера"}
        4754{ Return "Создана универсальная группа с включенной безопасностью" }
        4755{ Return "Изменена универсальная группа с включенной безопасностью"}
        4756{ Return "Добавлен пользователь к универсальной группе с включенной безопасностью"}
        4757{ Return "Удален пользователь из универсальной группы с включенной безопасностью"}
        4758{ Return "Удалена универсальная группа с включенной безопасностью"}
        4764{ Return "Изменен тип группы"}
        4720{ Return "Создана учетна запись"}
        4722{ Return "Включена учетна запись"}
        4724{ Return "Сброс пароля пользователя"}
        4725{ Return "Учетна запись пользователя отключена"}
        4726{ Return "Учетна запись пользователя удалена"}
        4738{ Return "Изменена учетна запись"}
        4740{ Return "Учетна запись пользователя заблокирована"}
        4767{ Return "Учетна запись пользователя разблокирована"}
        4780{ Return "Список управления Доступом был установлен на учетные записи, которые являются членами группы администраторов"}
        4781{ Return "Было изменено имя учетной записи"}
        4794{ Return "Была предпринята попытка задать режим восстановления служб каталогов"}
        5376{ Return "Диспетчер учетных данных: учетные данные были сохранены"}
        5377{ Return "Диспетчер учетных данных: учетные данные были востановлены из резервной копии"}
        4825{ Return "Запрeщен доступ к удаленному рабочемо столу, не входит в группу RDP"}
        1102{ Return "Удален журнал Security"}
    }
}

Затем понадобилось описать события, которые отслеживаются по маске доступа:

# Функция определения описания события по маске доступа

Function DefineReasonByAccessMask ($AccessMask){
    switch($AccessMask){
        "0xc0000064"  { Return "Имя пользователя не существует" }
        "0xc000006A"  { Return "Верное имя, но не правильный пароль"}
        "0xc0000234"  { Return "Пользователь заблокирован" }
        "0xc0000072"  { Return "Учетка деактивирована"}
        "0xc0000006F" { Return "Вход вне рабочее время"}
        "0xc00000070" { Return "Ограничение локальной станции"}
        "0xc00000193" { Return "Срок действия учетной записи истек"}
        "0xc00000071" { Return "Срок действия пароля Истек"}
        "0xc00000224" { Return "Необходимо сменить пароль при следующем входе"}
        "0xc000015b"  { Return "Пользователю запрещен вход на этой машине"}
        "0xc000006d"  { Return "Неверный пароль"}
    }
}

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

Чтобы получить события из журнала можно использовать одну из команд, в каждой из которых есть свои преимущества и недостатки:

1. Get-LogEvent security


Преимущества:

— быстрый доступ к свойствам события;
— не нужна предварительная обработка для доступа к свойствам события.

Недостатки:

— доступ только с системному журналу безопасности, который хранится по пути: Windows/System32/winevt/security.evtx;
— долгая обработка содержимого тега . Обработка происходит путем построчной сепарации с ликвидацией спецсимволов.

2. Get-WinEvent –path “D:/”


Преимущества:

— быстрая обработка журнала;
— доступ к любому журналу. Главное прописать полный путь.

Недостатки:

— для обработки полученного журнала требуется предварительная обработка.

Try {
$Events = Get-WinEvent -FilterHashTable $MyFilter
 } Catch {"No events were found in $Log"; Continue}
    ForEach ($Raw_Event in $Events)
    {	    
      Try{
          $EventXML = [xml]$Raw_Event.ToXML()
      } Catch {Write-Host "Unable to convert an event to XML"}
      $Event = @{}
      ForEach ($object in $EventXML.Event.EventData.Data) {
        $Event.Add($object.name,$object.'#text')
      }

      $Event.Add("ID",$Raw_Event.ID)
      $Event.Add("TimeCreated",$Raw_Event.TimeCreated)

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

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

image

1. Первый параметр предполагает, что журналы периодически экспортируются в определенный каталог, который и следует задать. Если журнал один, то задается стандартный каталог для хранения журналов: «C:\Windows\System32\winevt\Logs».

2. Второй параметр определяет сервер, по которому будет искаться логи. Если сервер один, то ставим «*».

3. Третий параметр предельно понятен: * — ищем события за весь период, если задана дата, например, 30.11.2015, то ищем за эту дату. Поиск за период осуществляется вводом начальной и конечной даты через тире (01.11.2015-30.11.2015).

4. Указываем путь для сохранения результата работы, например, «D:\log.log». Ниже приведен код, позволяющий построить интерфейс для скрипта:

$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "Ввод исходных данных"
$objForm.Size = New-Object System.Drawing.Size(300,450) 
$objForm.StartPosition = "CenterScreen"

# Events path
$objLabel1 = New-Object System.Windows.Forms.Label
$objLabel1.Location = New-Object System.Drawing.Size(10,20) 
$objLabel1.Size = New-Object System.Drawing.Size(280,40) 
$objLabel1.Text = "Введите полный путь к журналам в формате`nD:/.../.../ :"
$objForm.Controls.Add($objLabel1) 

$objTextBox1 = New-Object System.Windows.Forms.TextBox 
$objTextBox1.Location = New-Object System.Drawing.Size(10,60) 
$objTextBox1.Size = New-Object System.Drawing.Size(280,20) 
$objForm.Controls.Add($objTextBox1) 

#Find Server mode

$objLabel2 = New-Object System.Windows.Forms.Label
$objLabel2.Location = New-Object System.Drawing.Size(10,90) 
$objLabel2.Size = New-Object System.Drawing.Size(280,30) 
$objLabel2.Text = "1. * - по всем серверам.`n2. Укажите Сервер."
$objForm.Controls.Add($objLabel2) 

$objTextBox2 = New-Object System.Windows.Forms.TextBox 
$objTextBox2.Location = New-Object System.Drawing.Size(10,120) 
$objTextBox2.Size = New-Object System.Drawing.Size(280,30) 
$objForm.Controls.Add($objTextBox2)

#Type Events Mode

$objLabel3 = New-Object System.Windows.Forms.Label
$objLabel3.Location = New-Object System.Drawing.Size(10,150) 
$objLabel3.Size = New-Object System.Drawing.Size(280,45) 
$objLabel3.Text = "1. * - по всем событиям.`n2. Укажите событие.`n3. Перечислите через запятую события"
$objForm.Controls.Add($objLabel3) 

$objTextBox3 = New-Object System.Windows.Forms.TextBox 
$objTextBox3.Location = New-Object System.Drawing.Size(10,195) 
$objTextBox3.Size = New-Object System.Drawing.Size(280,30) 
$objForm.Controls.Add($objTextBox3)

#Date Events mode

$objLabel4 = New-Object System.Windows.Forms.Label
$objLabel4.Location = New-Object System.Drawing.Size(10,225) 
$objLabel4.Size = New-Object System.Drawing.Size(280,60) 
$objLabel4.Text = "1. * - за весь период.`n2. Укажите дату в формате ДД.ММ.ГГГГ.`n3. Укажите интервал в формате ДД.ММ.ГГГГ-ДД.ММ.ГГГГ"
$objForm.Controls.Add($objLabel4) 

$objTextBox4 = New-Object System.Windows.Forms.TextBox 
$objTextBox4.Location = New-Object System.Drawing.Size(10,285) 
$objTextBox4.Size = New-Object System.Drawing.Size(280,30) 
$objForm.Controls.Add($objTextBox4)

#Save Result

$objLabel5 = New-Object System.Windows.Forms.Label
$objLabel5.Location = New-Object System.Drawing.Size(10,315) 
$objLabel5.Size = New-Object System.Drawing.Size(280,30) 
$objLabel5.Text = "Путь для сохранения результата поиска"
$objForm.Controls.Add($objLabel5) 

$objTextBox5 = New-Object System.Windows.Forms.TextBox 
$objTextBox5.Location = New-Object System.Drawing.Size(10,345) 
$objTextBox5.Size = New-Object System.Drawing.Size(280,30) 
$objForm.Controls.Add($objTextBox5)

# Кнопки ОК и Отмена. Обработчки добавлен только на нажатие кнопки ОК. 

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,380)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($OKButton)
$OKButton.DialogResult=[System.Windows.Forms.DialogResult]::OK

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,380)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
$dialogResult =  $objForm.ShowDialog()
# Описание интерфейса закончено

Получаем введенные данные:

# Определяем входящие параметры
if ($dialogResult -eq "OK"){
    $Log_Path       = $objTextBox1.Text
    $FindServerMode = $objTextBox2.Text
    $TypeEventsMode = $objTextBox3.Text
    $DateEventsmode = $objTextBox4.Text
    $SaveResult     = $objTextBox5.Text
}

Если в поле дата есть символ "-", значит введен интервал, если этот символ введен ошибочно — ваши проблемы. Проводим сепарацию по символу "-", определяем начальную и конечную дату диапазона.

if ($DateEventsmode -match "-"){
    $x = $DateEventsmode.split("-")
    $StartDate = $x[0]
    $EndDate = $x[1]
    $StartDate = [DateTime]::parse($StartDate)
    $StartDate 
    $EndDate = [DateTime]::parse($EndDate)
    $EndDate
}

Если во введенном поле нет ни "-", ни "*", то значит введена конкретная дата.

if ($DateEventsmode -notmatch "-" -and $DateEventsmode -ne "*"){
    $StartDate1 = [DateTime]::parse($DateEventsmode)
}

1. Поиск по всем серверам, по всем событиям, за весь период:

if ($FindServerMode -eq "*" -and $DateEventsmode -eq "*" -and $TypeEventsMode -eq "*" -and $Log_Path -ne ""){
Write-host "вошли в 1"
     $ALL_LOGS = Get-ChildItem -Path $Log_Path -recurse| Where {$_.Extension -eq ".evtx"} | Sort LastWriteTime
     $ALL_LOGS
     foreach ($Log in $ALL_LOGS){
        $LogFile = "Audit EventLog Security" +"`n"
        ($Log).FullName
        $MyFilter = @{Path=($Log).FullName}
        $i=0
        Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
        ForEach ($Raw_Event in $Events){
        	    
           Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
           
           $Event = @{}
           ForEach ($object in $EventXML.Event.EventData.Data) {
              $Event.Add($object.name,$object.'#text')
           }
           $Event.Add("ID",$Raw_Event.ID)
           $Event.Add("TimeCreated",$Raw_Event.TimeCreated) 
           $LogFile+= "EventID:            " + $Raw_Event.ID                      +"`n"
           $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
           $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
           $LogFile+= "Status:             " + $Event.Status                        +"`n"
           $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
           $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
           $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
           $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                     +"`n"
           $Reason = DefineReason -Id $Raw_Event.ID
           $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
           $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
           $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
           $LogFile+= "----------------------------------------------------------------`n"
           $i++ 
        }   
     }
}

2. Поиск по всем серверам, по фильтру событий, за весь период:

if ($FindServerMode -eq "*" -and $DateEventsmode -eq "*" -and $TypeEventsMode -ne "*" -and $Log_Path -ne ""){
Write-host "вошли в 2"
         $ALL_LOGS = Get-ChildItem -Path $Log_Path | Where {$_.Extension -eq ".evtx"} | Sort LastWriteTime
         $ALL_LOGS
         foreach ($Log in $ALL_LOGS){
            $LogFile = "Audit EventLog Security" +"`n"
            ($Log).FullName
            $MyFilter = @{Path=($Log).FullName;ID=$TypeEventsMode}
            Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
            ForEach ($Raw_Event in $Events){	    
               Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
               $Event = @{}
               ForEach ($object in $EventXML.Event.EventData.Data) {
                  $Event.Add($object.name,$object.'#text')
               }

               $Event.Add("ID",$Raw_Event.ID)
               $Event.Add("TimeCreated",$Raw_Event.TimeCreated)

               $LogFile+= "EventID:            " + $Event.ID                       +"`n"
               $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
               $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
               $LogFile+= "Status:             " + $Event.Status                        +"`n"
               $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
               $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
               $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
               $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                     +"`n"
               $Reason = DefineReason -Id $Raw_Event.ID
               $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
               $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
               $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
               $LogFile+= "----------------------------------------------------------------`n" 
            }
         }
     }

3. Поиск по всем серверам, по всем событиям, за диапазон:

if ($FindServerMode -eq "*" -and $DateEventsmode -match "-" -and $TypeEventsMode -eq "*" -and $Log_Path -ne ""){
Write-host "вошли в 3"
        $ALL_LOGS = Get-ChildItem -Path $Log_Path -Recurse| Where {$_.Extension -eq ".evtx" } | Sort LastWriteTime
        $ALL_LOGS
        foreach ($Log in $ALL_LOGS){
           $LogFile = "Audit EventLog Security" +"`n"
           ($Log).FullName
           $StartDate
           $EndDate
           $MyFilter = @{Path=($Log).FullName}
           Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
           ForEach ($Raw_Event in $Events){	    
              Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
              $Event = @{}
              ForEach ($object in $EventXML.Event.EventData.Data) {
                 $Event.Add($object.name,$object.'#text')
              }

              $Event.Add("ID",$Raw_Event.ID)
              $Event.Add("TimeCreated",$Raw_Event.TimeCreated)
              $Event.TimeCreated
              if ($Event.TimeCreated -gt $StartDate -and $Event.TimeCreated -lt $EndDate){
                   $LogFile+= "EventID:            " + $Raw_Event.ID                      +"`n"
                   $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
                   $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
                   $LogFile+= "Status:             " + $Event.Status                        +"`n"
                   $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
                   $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
                   $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
                   $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                     +"`n"
                   $Reason = DefineReason -Id $Raw_Event.ID
                   $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
                   $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
                   $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
                   $LogFile+= "----------------------------------------------------------------`n"
              }
            }
         }
     }

4. Поиск по всем серверам, по всем событиям, за определенную дату:

if ($FindServerMode -eq "*" -and $DateEventsmode -notmatch "-"  -and $DateEventsmode -ne "*" -and $TypeEventsMode -eq "*" -and $Log_Path -ne ""){
Write-host "вошли в 5"
    $ALL_LOGS = Get-ChildItem -Path $Log_Path -Recurse| Where {$_.Extension -eq ".evtx" -and $StartDate1 -ne "null" } | Sort LastWriteTime
    $ALL_LOGS
    foreach ($Log in $ALL_LOGS){
       $LogFile = "Audit EventLog Security" +"`n"
       ($Log).FullName
       $Log.LastWriteTime
       $StartDate
       $EndDate
       $MyFilter = @{Path=($Log).FullName}
       Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
       ForEach ($Raw_Event in $Events){	    
       Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
       $Event = @{}
       ForEach ($object in $EventXML.Event.EventData.Data) {
          $Event.Add($object.name,$object.'#text')
       }

       $Event.Add("ID",$Raw_Event.ID)
       $Event.Add("TimeCreated",$Raw_Event.TimeCreated)

       if ($Event.TimeCreated.Day -eq $StartDate1.Day -and $Event.TimeCreated.Month -eq $StartDate1.Month -and $Event.TimeCreated.Year -eq $StartDate1.Year){
            $LogFile+= "EventID:            " + $Raw_Event.ID                      +"`n"
            $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
            $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
            $LogFile+= "Status:             " + $Event.Status                        +"`n"
            $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
            $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
            $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
            $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                      +"`n"
            $Reason = DefineReason -Id $Raw_Event.ID
            $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
            $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
            $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
            $LogFile+= "----------------------------------------------------------------`n"
        }
     }
   }
}

5. Поиск по всем серверам, по фильтру событий, за определенный период:

if ($FindServerMode -eq "*"  -and $DateEventsmode -match "-" -and $DateEventsmode -ne "*" -and $TypeEventsMode -ne "*" -and $Log_Path -ne ""){
Write-host "вошли в 4"
     $ALL_LOGS = Get-ChildItem -Path $Log_Path -Recurse| Where {$_.Extension -eq ".evtx" -and $StartDate1 -ne ""} | Sort LastWriteTime
     $ALL_LOGS
     foreach ($Log in $ALL_LOGS){
        $LogFile = "Audit EventLog Security" +"`n"
        ($Log).FullName
        $Log.LastWriteTime
        $StartDate
        $EndDate
        $MyFilter = @{Path=($Log).FullName;ID=$TypeEventsMode}
        Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
        ForEach ($Raw_Event in $Events){	    
        Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
        $Event = @{}
        ForEach ($object in $EventXML.Event.EventData.Data) {
           $Event.Add($object.name,$object.'#text')
        }

        $Event.Add("ID",$Raw_Event.ID)
        $Event.Add("TimeCreated",$Raw_Event.TimeCreated)

        if ($Event.TimeCreated.Day -eq $StartDate1.Day -and $Event.TimeCreated.Month -eq $StartDate1.Month -and $Event.TimeCreated.Year -eq $StartDate1.Year){
            $LogFile+= "EventID:            " + $Raw_Event.ID                      +"`n"
            $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
            $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
            $LogFile+= "Status:             " + $Event.Status                        +"`n"
            $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
            $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
            $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
            $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                      +"`n"
            $Reason = DefineReason -Id $Raw_Event.ID
            $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
            $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
            $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
            $LogFile+= "----------------------------------------------------------------`n"
         }  
     }
  }              
}             

6. Поиск по всем серверам, по фильтру событий, за определенную дату:

if ($FindServerMode -eq "*" -and $TypeEventsMode -ne "*" -and $DateEventsmode -notmatch "-"  -and $DateEventsmode -ne "*" -and $Log_Path -ne "") {
Write-host "вошли в 6"
    $ALL_LOGS = Get-ChildItem -Path $Log_Path -Recurse| Where {$_.Extension -eq ".evtx" -and $StartDate1 -ne "" } | Sort LastWriteTime
    $ALL_LOGS
    foreach ($Log in $ALL_LOGS){
       $LogFile = "Audit EventLog Security" +"`n"
       ($Log).FullName
       $Log.LastWriteTime
       $StartDate
       $EndDate
       $MyFilter = @{Path=($Log).FullName;ID=$TypeEventsMode}
       Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
       ForEach ($Raw_Event in $Events){	    
          Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
          $Event = @{}
          ForEach ($object in $EventXML.Event.EventData.Data) {
             $Event.Add($object.name,$object.'#text')
          }

          $Event.Add("ID",$Raw_Event.ID)
          $Event.Add("TimeCreated",$Raw_Event.TimeCreated)

          if ($Event.TimeCreated.Day -eq $StartDate1.Day -and $Event.TimeCreated.Month -eq $StartDate1.Month -and $Event.TimeCreated.Year -eq $StartDate1.Year){
              $LogFile+= "EventID:            " + $Raw_Event.ID                      +"`n"
              $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
              $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
              $LogFile+= "Status:             " + $Event.Status                        +"`n"
              $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
              $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
              $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
              $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                      +"`n"
              $Reason = DefineReason -Id $Raw_Event.ID
              $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
              $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
              $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
              $LogFile+= "----------------------------------------------------------------`n"
           }
       } 
     }            
  }

7. Поиск на определенном, сервере поиск по всем событиям, за весь период:

if ($FindServerMode -ne "*" -and $DateEventsmode -eq "*" -and $TypeEventsMode -eq "*" -and $Log_Path -ne ""){
Write-host "вошли в 7"
    $ALL_LOGS = Get-ChildItem -Path $Log_Path -Recurse| Where {$_.Extension -eq ".evtx" -and $_.FullName -match $FindServerMode} | Sort LastWriteTime
    $ALL_LOGS
    foreach ($Log in $ALL_LOGS){
       $LogFile = "Audit EventLog Security" +"`n"
       ($Log).FullName
       $MyFilter = @{Path=($Log).FullName}
       Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
       ForEach ($Raw_Event in $Events){	    
          Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
          $Event = @{}
          ForEach ($object in $EventXML.Event.EventData.Data) {
             $Event.Add($object.name,$object.'#text')
          }

          $Event.Add("ID",$Raw_Event.ID)
          $Event.Add("TimeCreated",$Raw_Event.TimeCreated)

          $LogFile+= "EventID:            " + $Raw_Event.ID                      +"`n"
          $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
          $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
          $LogFile+= "Status:             " + $Event.Status                        +"`n"
          $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
          $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
          $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
          $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                      +"`n"
          $Reason = DefineReason -Id $Raw_Event.ID
          $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
          $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
          $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
          $LogFile+= "----------------------------------------------------------------`n"
        }
    }          
}

8. Поиск на определенном сервере, по фильтру событий, за весь период:

if ($FindServerMode -ne "*" -and $DateEventsmode -eq "*" -and $TypeEventsMode -ne "*" -and $Log_Path -ne ""){
     Write-host "вошли в 8"
     $ALL_LOGS = Get-ChildItem -Path $Log_Path -Recurse| Where {$_.Extension -eq ".evtx" -and $_.FullName -match $FindServerMode} | Sort LastWriteTime
     
     $ALL_LOGS
     foreach ($Log in $ALL_LOGS){
     $LogFile = "Audit EventLog Security" +"`n"
     ($Log).FullName
     $MyFilter = @{Path=($Log).FullName;ID=$TypeEventsMode}
     Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
     ForEach ($Raw_Event in $Events){	    
        Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
        $Event = @{}
        ForEach ($object in $EventXML.Event.EventData.Data) {
           $Event.Add($object.name,$object.'#text')
        }

        $Event.Add("ID",$Raw_Event.ID)
        $Event.Add("TimeCreated",$Raw_Event.TimeCreated)

        $LogFile+= "EventID:            " + $Event.ID                       +"`n"
        $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
        $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
        $LogFile+= "Status:             " + $Event.Status                        +"`n"
        $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
        $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
        $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
        $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                      +"`n"
        $Reason = DefineReason -Id $Raw_Event.ID
        $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
        $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
        $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
        $LogFile+= "----------------------------------------------------------------`n"
     }    
   }
}

9. Поиск на конкретном сервере, по всем событиям, за период:

if ($FindServerMode -ne "*"-and $DateEventsmode -match "-" -and $TypeEventsMode -eq "*" -and $Log_Path -ne ""){
Write-host "вошли в 9"
    $ALL_LOGS = Get-ChildItem -Path $Log_Path -Recurse| Where {$_.Extension -eq ".evtx" -and $_.FullName -match $FindServerMode -and $StartDate -ne "null" -and $EndDate -ne "null"} | Sort LastWriteTime
    $ALL_LOGS
    foreach ($Log in $ALL_LOGS){
       $LogFile = "Audit EventLog Security" +"`n"
       ($Log).FullName
       $Log.LastWriteTime
       $StartDate
       $EndDate
       $MyFilter = @{Path=($Log).FullName}
       Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
       ForEach ($Raw_Event in $Events){	    
          Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
          $Event = @{}
          ForEach ($object in $EventXML.Event.EventData.Data) {
             $Event.Add($object.name,$object.'#text')
          }

          $Event.Add("ID",$Raw_Event.ID)
          $Event.Add("TimeCreated",$Raw_Event.TimeCreated)

          if ($Event.TimeCreated -gt $StartDate -and $Event.TimeCreated -lt $EndDate){
              $LogFile+= "EventID:            " + $Raw_Event.ID                      +"`n"
              $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
              $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
              $LogFile+= "Status:             " + $Event.Status                        +"`n"
              $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
              $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
              $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
              $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                      +"`n"
              $Reason = DefineReason -Id $Raw_Event.ID
              $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
              $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
              $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
              $LogFile+= "----------------------------------------------------------------`n"
           }
        }
    }
}

10. Поиск на конкретном сервере, по фильтру событий, за период:

if ($FindServerMode -ne "*"  -and $DateEventsmode -match "-" -and $DateEventsmode -ne "*" -and $TypeEventsMode -ne "*" -and $Log_Path -ne ""){
Write-host "вошли в 10"
    $ALL_LOGS = Get-ChildItem -Path $Log_Path | Where {$_.Extension -eq ".evtx" -and  $_.FullName -match $FindServerMode -and $StartDate -ne "null" -and $EndDate -ne "null"} | Sort LastWriteTime
    $ALL_LOGS
    foreach ($Log in $ALL_LOGS){
       $LogFile = "Audit EventLog Security" +"`n"
       ($Log).FullName
       $Log.LastWriteTime
       $StartDate
       $EndDate
       $MyFilter = @{Path=($Log).FullName;ID=$TypeEventsMode}
       Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
       ForEach ($Raw_Event in $Events){	    
          Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
          $Event = @{}
          ForEach ($object in $EventXML.Event.EventData.Data) {
             $Event.Add($object.name,$object.'#text')
          }

          $Event.Add("ID",$Raw_Event.ID)
          $Event.Add("TimeCreated",$Raw_Event.TimeCreated)
                        
          if ($Event.TimeCreated -gt $StartDate -and $Event.TimeCreated -lt $EndDate){
               $LogFile+= "EventID:            " + $Raw_Event.ID                      +"`n"
               $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
               $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
               $LogFile+= "Status:             " + $Event.Status                        +"`n"
               $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
               $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
               $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
               $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                      +"`n"
               $Reason = DefineReason -Id $Raw_Event.ID
               $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
               $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
               $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
               $LogFile+= "----------------------------------------------------------------`n"
           }
        }
     }
 }

11. Поиск по конкретному серверу, по всем событиям, за определенную дату:

if ($FindServerMode -ne "*" -and $DateEventsmode -notmatch "-"  -and $DateEventsmode -ne "*" -and $TypeEventsMode -eq "*" -and $Log_Path -ne ""){
 Write-host "вошли в 11"
      $ALL_LOGS = Get-ChildItem -Path $Log_Path -Recurse| Where {$_.Extension -eq ".evtx" -and $StartDate1 -ne "null" } | Sort LastWriteTime
      $ALL_LOGS
      foreach ($Log in $ALL_LOGS){
         $LogFile = "Audit EventLog Security" +"`n"
         ($Log).FullName
         $Log.LastWriteTime
         $StartDate
         $EndDate
         $MyFilter = @{Path=($Log).FullName}
         Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
         ForEach ($Raw_Event in $Events){	    
            Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
            $Event = @{}
            ForEach ($object in $EventXML.Event.EventData.Data) {
               $Event.Add($object.name,$object.'#text')
            }

            $Event.Add("ID",$Raw_Event.ID)
            $Event.Add("TimeCreated",$Raw_Event.TimeCreated)

            if ($Event.TimeCreated.Day -eq $StartDate1.Day -and $Event.TimeCreated.Month -eq $StartDate1.Month -and $Event.TimeCreated.Year -eq $StartDate1.Year){
                 $LogFile+= "EventID:            " + $Raw_Event.ID                      +"`n"
                 $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
                 $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
                 $LogFile+= "Status:             " + $Event.Status                        +"`n"
                 $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
                 $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
                 $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
                 $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                      +"`n"
                 $Reason = DefineReason -Id $Raw_Event.ID
                 $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
                 $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
                 $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
                 $LogFile+= "----------------------------------------------------------------`n"
              }
            }          
        }
  }

12. Поиск по конкретному серверу, по фильтру событий, за определенную дату:

if ($FindServerMode -ne "*" -and $TypeEventsMode -ne "*" -and $DateEventsmode -notmatch "-"  -and $DateEventsmode -ne "*" -and $Log_Path -ne ""){
 Write-host "вошли в 12"
     $ALL_LOGS = Get-ChildItem -Path $Log_Path -Recurse| Where {$_.Extension -eq ".evtx" -and $StartDate1 -ne "null" } | Sort LastWriteTime
     $ALL_LOGS
     foreach ($Log in $ALL_LOGS){
        $LogFile = "Audit EventLog Security" +"`n"
        ($Log).FullName
        $Log.LastWriteTime
        $StartDate
        $EndDate
        $MyFilter = @{Path=($Log).FullName;ID=$TypeEventsMode}
        Try {$Events = Get-WinEvent -FilterHashTable $MyFilter} Catch {"No events were found in $Log"; Continue}
        ForEach ($Raw_Event in $Events){	    
           Try{$EventXML = [xml]$Raw_Event.ToXML()} Catch {Write-Host "Unable to convert an event to XML"}
           $Event = @{}
           ForEach ($object in $EventXML.Event.Message) {
              $Event.Add($object.name,$object.'#text')
           }

           $Event.Add("ID",$Raw_Event.ID)
           $Event.Add("TimeCreated",$Raw_Event.TimeCreated)
           $Event
           if ($Event.TimeCreated.Day -eq $StartDate1.Day -and $Event.TimeCreated.Month -eq $StartDate1.Month -and $Event.TimeCreated.Year -eq $StartDate1.Year){
                $LogFile+= "EventID:            " + $Raw_Event.ID                      +"`n"
                $LogFile+= "Target User Name:   " + $Event.TargetUserName                +"`n"
                $LogFile+= "Target Domain Name: " + $Event.TargetDomainName              +"`n"
                $LogFile+= "Status:             " + $Event.Status                        +"`n"
                $LogFile+= "TimeGenerated:      " + $Event.TimeCreated                   +"`n"
                $LogFile+= "Workstation Name:   " + $Event.WorkstationName               +"`n"
                $LogFile+= "IpAddress:          " + $Event.IpAddress                     +"`n"
                $LogFile+= "Computer:           " + [xml]$Raw_Event.ToXML().Event.System.Computer                      +"`n"
                $Reason = DefineReason -Id $Raw_Event.ID
                $AccessM = DefineReasonByAccessMask -AccessMask $Event.Status  
                $LogFile+= "Reason(RU):         " + $Reason  + " "+  $AccessM            +"`n"
                $LogFile+= "Reason(SYS):        " + $Event.Message                      +"`n"
                $LogFile+= "----------------------------------------------------------------`n"
           }
        } 
     }
}

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

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

if ($SaveResult -ne ""){
   $LogFile | Out-File $SaveResult -Encoding utf8
   Invoke-Item  $SaveResult
   $Event2= @{}
   $Event = @{}
   $Log_Path=""

}
else{
   $LogFile | Out-File ".\log.log" -Encoding utf8
   Write-Host $LogFile
   $Event2= @{}
   $Event = @{}
   $Log_Path=""
}

Спасибо за внимание.

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

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


  1. Dal
    30.11.2015 14:41

    А все одним архивчиком есть?


    1. JunJa
      30.11.2015 22:36

  1. padla2k
    30.11.2015 20:14
    +3

    1. Форму удобнее нарисовать на xaml и оформить отдельным файлом. В скрипте только создавать и показывать. Делается не сложно — если нужен пример — могу поделиться. После создания формы можно навесить на элементы нужные функции в качестве обработчиков событий.
    2. Парсинг дат из текста — лишние телодвижения, ещё и не защищенные от ошибок. Если уж нарисовали форму, то почему бы не задействовать DatePicker и чек-бокс для скрытия/показа второго DataPicker'а?
    3. Чем вам не угодил командлет Get-EventLog? Если уж так нужно пробежаться по всем-всем логам в системе то вот вам в помощь:

    Get-EventLog -List | %{Get-EventLog -LogName $_.Log} | ?{<#тут делаем все что угодно с КАЖДОЙ записью лога#>}
    

    Опять же если осуществлять поиск по ВСЕМ логам (а их в системе ну ОЧЕНЬ МНОГО, особенно если сервер не молодой), то желательно использовать powershell 3.0+ и конструкции workflow и foreach -parallel. Это как минимум сэкономит вам время.

    В общем вижу некоторое количество костылей, которые сам делал будучи молодым скриптописателем… но все приходит с опытом. =)


    1. JunJa
      30.11.2015 22:32

      3. Чем вам не угодил командлет Get-EventLog? Если уж так нужно пробежаться по всем-всем логам в системе то вот вам в помощь:

      Get-EventLog -List | %{Get-EventLog -LogName $_.Log} | ?{<#тут делаем все что угодно с КАЖДОЙ записью лога#>}

      Сохраните журнал на диск Д и попробуйте его получить? Получиться сделать это при помощи команды?

      По поводу остальных замечательных, попробую учесть при написании след. скриптов. Спасибо.


      1. padla2k
        01.12.2015 07:54
        +1

        А зачем сохранять логи на диск? Оперативные логи есть в системе, если нужный диапазон времени не влазит в файл лога — увеличиваем его максимальный размер. Актуальность старых логов (кроме security) представляется мне сомнительной.


    1. n01d
      02.12.2015 11:11

      Форму удобнее нарисовать на xaml и оформить отдельным файлом
      Использую для рисования форм софтину PrimalForms от Sapien. Очень удобная штука: построение форм по принципу Visual Studio. К сожалению, в свободном доступе больше недоступен…

      workflow и foreach -parallel
      Ужасная вещь (особенно при неумелом использовании), но иногда экономит тысячи времени, да. Стараюсь использовать как можно реже, но при сборе инфы с 1000+ компов — просто спасает. Главное throttlelimitправильно подобрать, чтобы всю оперативку не сожрало ;-)


  1. Oldster
    01.12.2015 09:30

    Один из вариантов парсинга логов — Zabbix, не скажу, что это удобно под виндой, под линуксом лучше реализовано.


    1. 03.12.2015 16:41

      Эт точно. Отстутствие у виндового eventlog[] параметра output убивает всю затею штатного мониторинга логов Заббиксом под WIndows.