пятница, 3 июня 2016 г.

Безопасность Windows: аудит безопасности с помощью PowerShell

Недавно на Хабре нашел интересную статью. по использованию функционала Windows PowerShell в целях проведения аудита безопасности выполняемого обычными скриптами с использование стандартного лога системы. Не сказать, что решение "золотое", но однако в практике может пригодиться. Что же давайте более подробно рассмотрим как это работает.
Хочу поделится с вами способом, который может облегчить рутинные будни системного администрирования Win системы, с помощью PowerShell.
В один прекрасный день передо мной встала задача повседневного отслеживания активности пользователей, которые используют сервер терминалов в качестве рабочих станций. Думаю я выражу не только свое мнение, сказав что «Просмотр событий» входящий в состав средств администрирования Windows, является не самым удобным средством отслеживания ситуации на сервере. Да там есть фильтр, по которому можно отсеивать только интересующие нас события, но нет удобного способа который меняет формат отображения данной информации. В результате чего и появилась идея с помощью PowerShell осуществлять парсинг событий журнала безопасности.

Для получения списка событий нам понадобится команда Get-EventLog одним из параметров которой является название журнала, в нашем случае security.

image

Команда отображает содержимое всего журнала, что в корне меня не устраивает. Но все не так плохо, то что вы видите на скриншоте, не просто текст, а вполне себе объекты, со свойствами которых можно делать все что угодно в рамках возможностей PowerShell. Получить свойства данных объектов позволяет комадлет Get-Member. Выполнив в командной строке Get-EventLog security | Get-Member, мы получим в результате список свойств всех объектов выводимых Get-EventLog.

image

Зная список свойств, можно манипулировать результатами работы Get-EventLog. Например что бы получить список всех событий за сегодняшний день, самым простым способом будет использование параметров командлета Get-EventLog, а именно параметр -after. В результате у нас получится команда Get-EventLog security -after (Get-date -hour 0 -minute 0 -second 0), где командлет Get-Date выдает текущую дату и время, но параметры hour, minute и second задают вывод времени с начала текущего дня. В результате мы получим список событий произошедших за сегодня. Уже лучше, но все еще не то.
Мне необходимо получить список всех пользователей совершавших вход на сервер по протоколу RPD, что привело меня к изучению значений EventID и EntryType. Далее я приведу не полный перечень этих значений.

Значения EventID

Каждое событие входа в систему дополняется конкретным типом входа, список которых будет перечислен ниже.

  • 528 — Успешный вход пользователя на компьютер.
  • 529 — Отказ входа в систему. Не правильное имя пользователя или пароль.
  • 530 — Отказ входа в систему. Попытка входа в систему с учетной записью пользователя вне допустимого интервала времени.
  • 531 — Отказ входа в систему. Попытка входа в систему с использованием отключенной учетной записи пользователя.
  • 532 — Отказ входа в систему. Попытка входа в систему с использованием устаревшей учетной записи пользователя.
  • 533 — Отказ входа в систему. Попытка входа в систему пользователя, которому не разрешен вход на данный компьютер.
  • 534 — Отказ входа в систему. Попытка входа в систему с указанием неразрешенного типа входа.
  • 535 — Отказ входа в систему. Срок действия пароля для указанной учетной записи истек.
  • 536 — Отказ входа в систему. Служба Net Logon отключена.
  • 537 — Отказ входа в систему. Попытка входа в систему не удалась по другим причинам (В некоторых случаях причина отказа входа в систему может быть неизвестна).
  • 538 — Процесс выхода пользователя из системы завершен.
  • 539 — Отказ входа в систему. Во время попытки входа в систему учетная запись пользователя заблокирована.
  • 540 — Успешный вход пользователя в сеть.
  • 541 -Завершен основной режим проверки подлинности по протоколу IKE между локальным компьютером и зарегистрированной одноранговой тождественностью (установление надежного сопоставления), или быстрый режим установил канал данных.
  • 542 — Канал данных отключен.
  • 543 — Основной режим отключен.(Причиной этого может быть окончание временного интервала, ограничивающего длительность надежного соединения (по умолчанию — 8 часов), изменение политики или одноранговое завершение).
  • 544 — Отказ основного режима проверки подлинности из-за того, что партнер не обеспечил действительный сертификат или не подтверждена подлинность подписи.
  • 545 — Отказ основного режима проверки подлинности из-за отказа Kerberos или неверного пароля.
  • 546 — Отказ создания надежного соединения IKE, вызванный поступлением от партнера неприемлемого предложения. Прием пакета, содержащего неверные данные.
  • 547 — Отказ во время процедуры установления соединения IKE.
  • 548 — Отказ входа в систему. Идентификатор надежности (SID), полученный от доверенного домена, не соответствует SID учетной записи домена для клиента.
  • 549 — Отказ входа в систему. Все идентификаторы надежности SID, соответствующие недоверенным пространствам имен, были отфильтрованы во время проверки подлинности в лесах.
  • 550 — Сообщение уведомления, которое может указывать на возможную атаку на службу.
  • 551 — Пользователь инициировал процесс выхода из системы.
  • 552 — Пользователь успешно вошел на компьютер, используя правильные учетные данные, несмотря на то что до этого уже вошел как другой пользователь.
  • 682 — Пользователь повторно подключен к отключенному сеансу терминального сервера.
  • 683 — Пользователь отключен от сеанса терминального сервера без выхода из системы (Это событие формируется, когда пользователь подключен к сеансу терминального сервера через сеть. Оно появляется на сервере терминалов).

Значения EntryType

  • 2 — Интерактивный. Успешный вход пользователя на компьютер.
  • 3 — Сеть. Пользователь или компьютер вошли на данный компьютер через сеть.
  • 4 — Пакетный. Пакетный тип входа используется пакетными серверами, исполнение процессов на которых производится по поручению пользователя, но без его прямого вмешательства.
  • 5 — Служба. Служба запущена Service Control Manager.
  • 7 — Разблокирование. Эта рабочая станция разблокирована.
  • 8 — NetworkCleartext. Пользователь вошел на данный компьютер через сеть. Пароль пользователя передан в пакет проверки подлинности в его нехешированной форме. Встроенная проверка подлинности упаковывает все хешированные учетные записи перед их отправкой через сеть. Учетные данные не передаются через сеть открытым текстом.
  • 9 — NewCredentials. Посетитель клонировал свой текущий маркер и указал новые учетные записи для исходящих соединений. Новый сеанс входа в систему имеет ту же самую локальную тождественность, но использует отличающиеся учетные записи для сетевых соединений.
  • 10 — RemoteInteractive. Пользователь выполнил удаленный вход на этот компьютер, используя Terminal Services или Remote Desktop.
  • 11 — CachedInteractive. Пользователь вошел на этот компьютер с сетевыми учетными данными, которые хранились локально на компьютере. Контроллер домена не использовался для проверки учетных данных.

Из полученных сведений можно сделать вывод что нам необходимо событие с EventID = 528 и EntryType = 10, что и будет соответствовать входу на компьютер через RDP. Немного изменим нашу команду.
Get-EventLog security -message "*Тип входа:?10*" -after (Get-date -hour 0 -minute 0 -second 0) | ?{$_.eventid -eq 528 }

Параметр -message отражает полностью сообщение нашего события, в котором содержится «Entry type» («Тип входа», так как у меня стоит русская версия 2003го), через шаблоны мы задаем поиск интересующей нас строки.

Поскольку я не нашел в параметрах командлета Get_EventLog -EventID, то пришлось использовать возможности свойств объекта:
$_ означает сам объект который фигурирует изначально
-eq означает равенство значению, в нашем случае 528

Результатом выполнения будет следующее:

image

В общем, то, что нужно, но только вот выводится нам совсем не та информация. Будем исправлять. Для меня актуальными являются три параметра объекта, это: время, имя пользователя, IP адрес. В дальнейшем создадим объект, и занесем в него интересующие нас данные. Я создал скрипт “test.ps1”, т.к. команду целиком будет проблематично набирать.


$Events = Get-EventLog security -message "*Тип входа:?10*" -after (get-date -hour 0 -minute 0 -second 0) | ?{$_.eventid -eq 528 }



$Data = New-Object System.Management.Automation.PSObject

$Data | Add-Member NoteProperty Time ($null)
$Data | Add-Member NoteProperty UserName ($null)
$Data | Add-Member NoteProperty Address ($null)

$Events | %{

$Data.time = $_.TimeGenerated

$message = $_.message.split("`n") | %{$_.trimstart()} | %{$_.trimend()}

$Data.UserName = ($message | ?{$_ -like "Пользователь:*"} | %{$_ -replace "^.+:."} )
$Data.Address = ($message | ?{$_ -like "Адрес сети источника:*"} | %{$_ -replace "^.+:."})

$data

}


Разберем данный код поближе.
$Events = Get-EventLog security -message "*Тип входа:?10*" -after (get-date -hour 0 -minute 0 -second 0) | ?{$_.eventid -eq 528 } — заносим результаты выборки по событиям в переменную, что бы в дальнейшем было удобно с ней работать.

Далее создадим «шаблон» нашей будущей таблицы, содержащей три значения: время, имя пользователя и адрес.
$Data = New-Object System.Management.Automation.PSObject

$Data | Add-Member NoteProperty Time ($null)

$Data | Add-Member NoteProperty UserName ($null)

$Data | Add-Member NoteProperty Address ($null)


$Events | %{} — пройдемся по каждому объекту который будет в результатах отбора

$Data.time = $_.TimeGenerated — заносим время
$message = $_.message.split("`n") | %{$_.trimstart()} | %{$_.trimend()} — в переменную message заносим массив строк, которые разделяются символом переноса строки ('n), функции trimstart(), trimend() убирают все лишние символы в конце и в начале строки, функция split разделяет строку.
$Data.UserName = ($message | ?{$_ -like "Пользователь:*"} | %{$_ -replace "^.+:."} )

$Data.Address = ($message | ?{$_ -like "Адрес сети источника:*"} | %{$_ -replace "^.+:."}) Далее во вновь образовавшемся массиве мы ищем совпадения по строке «Пользователь:» и «Адрес сети источника:», а -replace в дальнейшем удаляет эти регулярные выражения, оставляя саму информацию.


Запускаем скрипт командой .\test.ps1. (Как видите для запуска PS скрипта приходится указывать путь, даже если он находится в текущей рабочей папке):
image

Если скрипт не запустился, то скорее всего ваш PoSh не настроен на выполнение скриптов. Выполните команду Set-ExecutionPolicy RemoteSignet .

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


param ($key1,$val1,$val2,$val3,$val4,$val5,$val6)



if ($val1 -eq $null) {$val1=0};


$mydate = Get-date -hour 0 -minute 0 -second 0;

if ($key1 -eq "year") { $mydate = (Get-date -hour 0 -minute 0 -second 0 -day 1 -month 1); $mydate = $mydate.addyears(-$val1); };

if ($key1 -eq "month") { $mydate = (Get-date -hour 0 -minute 0 -second 0 -day 1); $mydate = $mydate.addmonths(-$val1); };

if ($key1 -eq "day") { $mydate = $mydate.adddays(-$val1) };

if ($key1 -eq "date") { $mydate = (Get-date -hour 0 -minute 0 -second 0 -day $val1 -month $val2 -year $val3); }; # здесь реализуем возможность задания интервала

if ($val4 -eq $null) {$Events = Get-EventLog security -message "*Тип входа:?10*" -after ($mydate) | ?{$_.eventid -eq 528 }}
if ($val4 -ne $null) {$Events = Get-EventLog security -message "*Тип входа:?10*" -after ($mydate) -before (get-date -hour 0 -minute 0 -second 0 -day $val4 -month $val5 -year $val6) | ?{$_.eventid -eq 528 }}
$Data = New-Object System.Management.Automation.PSObject
$Data | Add-Member NoteProperty Time ($null)
$Data | Add-Member NoteProperty UserName ($null)
$Data | Add-Member NoteProperty Address ($null)

$Events | %{

$Data.time = $_.TimeGenerated

$message = $_.message.split("`n") | %{$_.trimstart()} | %{$_.trimend()}

$Data.UserName = ($message | ?{$_ -like "Пользователь:*"} | %{$_ -replace "^.+:."} )
$Data.Address = ($message | ?{$_ -like "Адрес сети источника:*"} | %{$_ -replace "^.+:."})

$textcolor = $host.ui.rawui.foregroundcolor

$host.ui.rawui.foregroundcolor = "red"

if ($data.address -like "192.168.0*") {$host.ui.rawui.foregroundcolor = "DarkGreen"}
if ($data.address -like "10.*") {$host.ui.rawui.foregroundcolor = "yellow"}

$data

$host.ui.rawui.foregroundcolor = $textcolor

}


param ($key1,$val1,$val2,$val3,$val4,$val5,$val6) — определяет параметры, передаваемые скрипту.

if ($key1 -eq "day") { $mydate = $mydate.adddays(-$val1) };; проверяем переданные параметры на соответствие ключу, если ключ совпадает, то корректируем дату согласно задаваемым параметрам. В данном случае в качестве параметра передается ключ «day» аргументом которого мы будем переводить дату на определенное количество дней назад. Т.е. будет выведен лог за определенное количество дней, остальные условия выполняются по аналогии, за месяц и за год. Если указан ключ «date» то за начало отсчета берется конкретная дата, указанная через пробел, например «01 05 2011», если мы так же через пробел укажем другую дату, то на экран будет выведен определенный период, указанный в этих датах. Для вывода информации цветом, изначально планировалось использовать командлет Write-Host, который имеет параметры -backgroundcolor и -foregroundcolor, но в итоге пришлось от него отказаться, потому что он не дружит с выводом объектов.

image

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

А если задать {$_.eventid -eq 529 } то результатом будут все попытки входа с неправильными паролями.

image

Список вышел довольно длинный, полезно пару раз в день проверять и блокировать на фаерволе подобных негодяев.

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


За материалы спасибо Хабр

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

Отправить комментарий

Вы можете добавить свой комментарий...

Примечание. Отправлять комментарии могут только участники этого блога.