отслеживание попыток доступа по rdp
скрипт, наглядно выводящий форматированные время, IP-адреса и имена хостов, с которых неудачно пытались подключиться к Windows 2003 RDP-серверу согласно виндовому журналу событий. Для отслеживания таких попыток, необходимо включить аудит отказов во входе в систему для локальной политики аудита в параметрах безопасности (делается через оснастку secpol.msc, например). Скрипт будет полезен для открытых кишками наружу серверов терминалов.
В скрипте сразу предусмотрена возможность не выводить определенные адреса/имена хостов, или целые «диапазоны» (сравниваются строки а не маски).
'Event code 529 'кодировка ISO 8859-2 'Event code 529 ' '(0) Сбой входа в систему: '(1) Причина: неизвестное имя пользователя или неверный пароль '(2) Пользователь: user '(3) Домен: Domain '(4) Тип входа: 7 '(5) Процесс входа: User32 '(6) Пакет проверки: Negotiate '(7) Рабочая станция: workstation '(8) Имя вызывающего пользователя: user$ '(9) Домен вызывающего: Domain '(10) Код входа вызывающего: (0x0,0x3E7) '(11) Код процесса вызывающего: 2804 '(12) Промежуточные службы: - '(13) Адрес сети источника: 127.0.0.1 '(14) Порт источника: 123456 Dim ArrayOfValues(14) Dim ArrayOfIPExclusions(2) ArrayOfIPExclusions(0) = "192.168.0." ArrayOfIPExclusions(1) = "10.0." ArrayOfIPExclusions(2) = "10.10.10." Dim ArrayOfHOSTExclusions(0) ArrayOfHOSTExclusions(0) = "admin.max.org.ua" Set HOSTSDictionary = CreateObject("Scripting.Dictionary") strComputer = "." set objShell = createobject("wscript.shell") Set objWMIService = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set dtmStartDate = CreateObject("WbemScripting.SWbemDateTime") Set dtmEndDate = CreateObject("WbemScripting.SWbemDateTime") DateStart = Now() - 120 DateStop = Now() dtmStartDate.SetVarDate DateStart, True dtmEndDate.SetVarDate DateStop, True Set colLoggedEvents = objWMIService.ExecQuery _ ("Select * from Win32_NTLogEvent Where Logfile = 'Security' AND EventCode = '529' AND EventType = 5 AND TimeWritten >= '" _ & dtmStartDate & "' AND TimeWritten < '" & dtmEndDate & "'") Set objRegEx = CreateObject("VBScript.RegExp") objRegEx.Global = True objRegEx.Pattern = "[0-9]" endOfString = chr(13) & chr(10) For Each objEvent in colLoggedEvents objDay = Mid(objEvent.TimeWritten, 7, 2) objMonth = Mid(objEvent.TimeWritten, 5, 2) objYear = Left(objEvent.TimeWritten, 4) 'костыль, зато не сбивает сортировку ''отбираем события только за последние 120 дней 'if DateDiff("s", Now() - 120, DateSerial(objYear, objMonth, objDay)) < 0 then '' exit for 'end if 'Wscript.Echo "Category: " & objEvent.Category 'Wscript.Echo "Computer Name: " & objEvent.ComputerName 'Wscript.Echo "Event Code: " & objEvent.EventCode 'Wscript.Echo "Message: " & objEvent.Message currentValue = 0 ArrayOfStrings = Split(objEvent.Message, endOfString) For Each row in ArrayOfStrings if row <> "" then SplittedRow = Split(row, ":") ArrayOfValues(currentValue) = Trim(Replace(SplittedRow(1), Chr(9), "")) currentValue = currentValue + 1 end if Next IPExistsInExclusions = 0 IP = ArrayOfValues(13) 'сравним со списком IP адресов, которые исключаем из вывода скрипта for each row in ArrayOfIPExclusions if InStr(IP, row) > 0 then IPExistsInExclusions = 1 exit for end if next if IPExistsInExclusions = 0 then HOSTExistsInExclusions = 0 'если IP содержит лишние знаки имя хоста узнавать не будем strhost = " " if objRegEx.Replace(IP, "") = "..." then 'проверим, не получали ли мы имя хоста по этому IP раньше if HOSTSDictionary.Exists(IP) = false then 'http://social.technet.microsoft.com/Forums/en-US/ITCG/thread/f21292fd-e9d7-4a57-a14e-31461aeab905/ strParams = "%comspec% /c nslookup " & IP Set objExecObj = objShell.exec(strParams) do while not objExecObj.StdOut.AtEndOfStream strText = objExecObj.StdOut.Readline() if instr(strText, "Server") then strServer = trim(replace(strText,"Server:","")) elseif instr (strText, "Name") Then strhost = trim(replace(strText,"Name:","")) end if loop HOSTSDictionary.Add IP, strhost 'если получали - просто подставим это имя хоста else HOSTSDictionary.Exists(IP) strhost = HOSTSDictionary.Item(IP) end if 'сравним со списком хостов, которые исключаем из вывода скрипта for each row in ArrayOfHOSTExclusions if InStr(LCase(strhost), row) > 0 then HOSTExistsInExclusions = 1 exit for end if next end if if HOSTExistsInExclusions = 0 then IP = IP & Left(" ", 15 - Len(IP)) strhost = strhost & Left(" ", 40 - Len(strhost)) Wscript.Echo objDay & "." & objMonth & "." & objYear & " " & Mid(objEvent.TimeWritten, 9, 2) & ":" & Mid(objEvent.TimeWritten, 11, 2) & ":" & Mid(objEvent.TimeWritten, 13, 2) & "; IP: " & IP & " (" & strhost & "); user: " & ArrayOfValues(2) end if end if 'Wscript.Echo "Record Number: " & objEvent.RecordNumber 'Wscript.Echo "Source Name: " & objEvent.SourceName 'Wscript.Echo "Time Written: " & objEvent.TimeWritten 'Wscript.Echo "Event Type: " & objEvent.Type 'Wscript.Echo "User: " & objEvent.User Next
скачать: rdp.vbs (4322 Загрузки)
результатом выполнения такого скрипта из командной строки «cscript rdp.vbs > rdp.txt», будет файл, примерно такого содержания:
01.12.2012 13:53:25; IP: xx.xx.xx.xx (xx.xx.xx.xx.internet.net ); user: Администратор 01.12.2012 12:45:30; IP: xx.xx.xx.xx (xx.xx.xx.xx.internet.net ); user: Администратор 23.11.2012 18:06:23; IP: xx.xx.xx.xx (xx-xx-xx-xx-gprs.telefona.net ); user: user2 20.11.2012 14:25:25; IP: xx.xx.xx.xx (xx-xx-xx-xx-gprs.telefona.net ); user: user1 19.11.2012 17:48:20; IP: xx.xx.xx.xx (server17.max.org.ua ); user: Администратор
подчеркну, что скрипт необходимо сохранять в кодировке ISO 8859-2, или же скачать готовый файл выше.
кстати, хорошей идеей было бы прикручивание утилиты Whois от Марка Руссиновича, которая отображала бы данные по IP адресу в той же строке. Велкам в комментарии 🙂
UPD 05.12.12. Прикрутил получение имени хоста по IP если оно доступно, а также возможность исключить из вывода определенные имена хостов. Текст скрипта а также файл обновлены.
UPD 13.09.13. Добавил в запрос выборку в определенном интервале дат. В примере выбираются события за последние 120 дней. Оптимизировал процесс получения имени хоста — если оно уже было получено для IP-адреса то повторно запрашиваться не будет, а будет взято из соответствия. Также предусмотрел ситуацию, когда в поле IP события может попасть вовсе не IP-адрес. Также, из-за отбора в диапазоне дат данные в результат выводятся в возрастающем порядке, т.е. последние события будут в конце файла, что не очень удобно. Добавил комментарии в код.
спасибо!
пожалуйста! рад, что скрипт пригодился
спасибо полезная штука, а каким скриптом можно получить лог подключений к серверу по rdp — т.е. под какой учеткой, с какого айпи зашли на сервак и когда…
для того, чтобы получить лог подключений необходимо включить аудит успехов во время входа в систему (с помощью того же secpol.msc) и поменять в скрипте пару строчек. А именно изменить код события с 529 на 682 и тип события с 5 (отказ) на 4 (успех). Также у нас поменяется структура самого сообщения, вместо 15ти строк будет выведено 7. Также поменяется и порядок следования IP-адреса и имени пользователя. Вот, накидал работающий вариант:
скачать: rdp_success.vbs
Идея супер. Реализация красива. Но…
Данный скрипт не работает на Server 2008
Меняю код события на четырезначный. Валится ошибка про индекс массива.
да, уже слышал о том, что на 2008 сервере не работает. Если дойдут руки — унифицирую
Спасибо!
Пожалуйста! 🙂
Привет! Спасибо, скриптик очень выручает. Огорчает две вещи:
— не получается перенаправить вывод в файл (либо стартую из проводника — тогда открывается окно командного процессора, записи пролетают и окно закрывается; либо из батника — тогда на каждую строку вылазит модальное окно и опять-таки, эхо в файл не перенаправляется).
— нет возможности задать диапазон дат, скрипт раз за разом шерстит весь журнал, сколько бы тот не весил.
Можно ли реализовать параметры Имя_файла, Нач_дата, Кон_дата?
С выводом разобрался — стартовать не «start Success.vbs >result.txt», а «cscript Success.vbs >result.txt». А даты не запилите?
Вы как раз вовремя. Я сам задался вопросом про даты, т.к. скрипт начинает работать все дольше и дольше. Обновил текст скрипта, приложенный файл и текст статьи.
набросок rdp
#include
Global $sData
$sTime = _FormatTimeInWMI ("h",-8) ; Здесь задаем время: -3 часа от текущего времени
$strComputer = "localhost"
$objWMIService = ObjGet("winmgmts:\\" & $strComputer & "\root\CIMV2") ; Подключаемся к пространству имен WMI
; Запрос К WMI
; Где Logfile = 'System' - Журнал с которого будем читать события
; SourceName = 'DHCP' - Источник события
; TimeGenerated > - Время с которого необходимо получение информации
$colItems = $objWMIService.ExecQuery( "SELECT * FROM Win32_NTLogEvent WHERE Logfile = 'Security' " )
$sData = @MDAY &':' &@MON &':' &@MIN &@CRLF
; "SELECT * FROM Win32_NTLogEvent WHERE Logfile = 'Security' AND TimeGenerated > " & $sTime )
; Перебираем журнал
For $objItem in $colItems
If $objItem.EventCode == "4648" then
$arrayname = StringRegExp($objItem.Message, '(?s).*Были использованы учетные данные следующей учетной записи:.*(?-s)Имя учетной записи:\t*(.*)', 1)
$arrayname2 = StringRegExp($objItem.Message, '(?s).*Сведения о сети:.*(?-s)Сетевой адрес:\t*(.*)', 1)
$arrayname3 = StringRegExp($objItem.Message, '(?s).*Сведения о сети:.*(?-s)Порт:\t*(.*)', 1)
; ConsoleWrite( $arrayname[0])
$sData &= "-----------------------------------" & @CRLF _ ; $sData - Переменная для сохранения полученных данных
&_FormatTimeFromWMI($objItem.TimeGenerated) &" |" _ ; Получаем время создания события
&"EventCode: " & $objItem.EventCode &" |" _ ; Код События Имя учетной записи:
&"UserName: " & $arrayname[0] &" |" _ ; Сообщение о событии
&"IP: " & $arrayname2[0] &" |" _ ; Сообщение о событии
&"Port: " & $arrayname3[0] & @CRLF ; Сообщение о событии
EndIf
Next
$hFile = FileOpen("C:\RdpEvent\"&@YEAR &" " &@MON &" " &@MDAY &".Txt",9); Открываем файл для записи
FileWrite($hFile,$sData ) ; Записываем в файл данные из переменной $sData
FileClose($hFile) ; Закрываем ранее открытый файл
; Функция форматирует время в формат понятный WMI
Func _FormatTimeInWMI ($sType,$iNumb )
$sNewDate = _DateAdd( 'h',$iNumb, _NowCalc())
$sDateTime = StringRegExpReplace ( $sNewDate, "[^0-9]", "" )
Return "'" & $sDateTime & ".000000+240'"
EndFunc
; Функция форматирует вывод времени из WMI в нормальный вид
Func _FormatTimeFromWMI ($sText)
$sStroka = StringSplit ( $sText, ".")
$sDate = StringFormat ("%04d/%02d/%02d", StringMid ( $sStroka[1],1,4), StringMid ( $sStroka[1],5,2), StringMid ( $sStroka[1],7,2))
$sTimes = StringFormat ("%02d:%02d:%02d", StringMid ( $sStroka[1],9,2), StringMid ( $sStroka[1],11,2), StringMid ( $sStroka[1],13,2))
Return $sDate & " " & $sTimes
EndFunc
язык autoit )
Не, не слышал. AutoIt, ровно как и PowerShell требует доустановки приложения.
По существу: насколько я понимаю, скрипт для >= W2k8? На W2k3 выкинул обышку «Cannot parse #include» 🙁
вот держи, дописал http://yadi.sk/d/TV6gr06P9RkoW
на 2008r, ,брал сообщения журнала 4648,4624,4625
Конфигурация Компьютера-Конф Windows-Параметры безопасности-Локальные политики-Политика аудита
лог сохраняется в C:\RdpEvent\дата месяц день.txt
в нутри
17:09:41
————————————
2013/09/17 13:40:57 |EventCode: 4648 |UserName: ххх
|IP: ххх.хх.х.х
|Port: 62592
В 2008 есть нюанс. В групповых политиках должен быть обязательно включен аудит события «Вход». В принципе, тогда штатными средствами можно задать фильтр по событию 4779 и видеть подключенных по терминалке. Вроде бы так.
Спасибо за скрипт, но есть вопрос как или точнее с помощью чего его запустить на сервере? не в командной же строке ))
например, с помощью планировщика заданий
Привет.
Делаю аналогичный скрипт + скрипт закидывающий черный список IP адресов в IP filter локальной политики безопасности…
Вопрос такой:
не знаете, почему при задании в запросе интервала на TimeWritten менее 6 часов от текущего времени запрос не возвращает ни одной записи?
Например, хочу выбрать ошибки логона за последние 5 или 20 минут.
Если же сделаю запрос за 6+ последних часов, то запрос возвращает кучу записей, при этом в этих записях есть и TimeWritten 5 минутной давности.
Думал, что таблица Win32_NTLogEvent как-то обновляется раз в несколько часов, но раз там есть недавние записи, значит это не так. Одним словом пока приходится перебирать результат запроса с 6 часовой информацией, и отсеивать записи, что немного затормаживает результат. Поэтому приходится пока раз в час обновлять черный список, иначе сервер начинает напрягаться. Хотелось бы более оперативно, например раз в 5 минут.
запрос в студию. Ну, и самим скриптом поделись 🙂
Sergey, покажите, пожалуйста «скрипт закидывающий черный список IP адресов в IP filter локальной политики безопасности».
Спасибо большое! Очень полезный скрипт. Это именно то, что я искал.
Sergey, также буду благодарен за данный скрипт!
На 2003 сервере выдает ошибку (154,9) недопустимый вызов или аргумент процедуры ‘Left’
Жуть. Удивлен, что до сих пор кто-то еще использует 2003 Server. Попробуйте отладить, что пишется в objEvent.TimeWritten
Вы не поверите — пользуются. Сравниваю серваки с 2008 и 2003 осями:
2003 с 2 гигами оперативки так умеет работать с памятью и файлом подкачки, что не заваливается практически на тяжёлых задачках. Да — тупит, да — свопит, но тянет. А 2008 с 4мя гигами оперативки при достижении 98% заполнения физической памяти просто вешается.
По сабжу темы — а реально выцепить из логов аудита какие пароли подбором вводили?
пароль даже не записывается нигде. Так что никак
Подскажите.
А если
при подключении и подбора пароля
Сетевой адрес источника:-
как включить чтобы IP показывало какой подключается
попробуйте включить оба аудита отказов в политиках локальной безопасности (secpol.msc)
помогите как эту скрип запустить я скачал запускаю ничево не происходит
тут есть кто-нибудь,есть подобный скрипт для 2008?
Под 2008 нету, да и неактуально уже это, т.к. скрипт был написан в основном с целью банить адреса, с которых перебирают пароли, а для этого давно уже существует прекрасная бесплатная утилита IPBan, рекомендую.