Oтзывы и предложения для stranichko.org.ua

отслеживание попыток доступа по 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 (4269 Загрузок)

результатом выполнения такого скрипта из командной строки «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-адрес. Также, из-за отбора в диапазоне дат данные в результат выводятся в возрастающем порядке, т.е. последние события будут в конце файла, что не очень удобно. Добавил комментарии в код.

VN:F [1.8.8_1072]
Rating: 4.3/5 (3 votes cast)
отслеживание попыток доступа по rdp4.353
43 815 просмотров

32 комментария на “отслеживание попыток доступа по rdp”

  • lex:

    спасибо!

  • serega:

    спасибо полезная штука, а каким скриптом можно получить лог подключений к серверу по rdp — т.е. под какой учеткой, с какого айпи зашли на сервак и когда…

    • для того, чтобы получить лог подключений необходимо включить аудит успехов во время входа в систему (с помощью того же secpol.msc) и поменять в скрипте пару строчек. А именно изменить код события с 529 на 682 и тип события с 5 (отказ) на 4 (успех). Также у нас поменяется структура самого сообщения, вместо 15ти строк будет выведено 7. Также поменяется и порядок следования IP-адреса и имени пользователя. Вот, накидал работающий вариант:

      'Event code 682
      '
      '(0) Сеанс подключен к станции:
      '(1) Имя пользователя:	user
      '(2) Домен:		DOMAIN
      '(3) Код входа:		(0x0,0x1E76B6)
      '(4) Имя сеанса:	RDP-Tcp#6
      '(5) Имя клиента:	ComputerName
      '(6) Адрес клиента:	127.0.0.1
      
      Dim ArrayOfValues(6)
      
      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"
      
      strComputer = "."
      set objShell = createobject("wscript.shell")
      Set objWMIService = GetObject("winmgmts:" _
          & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
      
      Set colLoggedEvents = objWMIService.ExecQuery _
          ("Select * from Win32_NTLogEvent Where Logfile = 'Security' AND EventCode = '682' AND EventType = 4")
      
      endOfString = chr(13) & chr(10)
      
      For Each objEvent in colLoggedEvents
      
          'Wscript.Echo "Category: " & objEvent.Category
          'Wscript.Echo "Computer Name: " & objEvent.ComputerName
          'Wscript.Echo "Event Code: " & objEvent.EventCode
          
          'Wscript.Echo "Message: " & objEvent.Message
          i = 0
      
          ArrayOfStrings = Split(objEvent.Message, endOfString)
          
          For Each row in ArrayOfStrings
            
            if row <> "" then
            
              SplittedRow = Split(row, ":")
              ArrayOfValues(i) = Trim(Replace(SplittedRow(1), Chr(9), ""))
              i = i + 1
            
            end if
      
          Next
          
          a = 0
          IP = ArrayOfValues(6) 
          
          for each row in ArrayOfIPExclusions
          
            if InStr(IP, row) > 0 then
            
              a = 1
              exit for
            
            end if
          
          next
      
          if a = 0 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
            
            b = 0
            for each row in ArrayOfHOSTExclusions
          
              if InStr(LCase(strhost), row) > 0 then
            
                b = 1
                exit for
            
              end if
          
            next
           
            if b = 0 then
            
              IP = IP & Left("               ", 15 - Len(IP))
              strhost = strhost & Left("                                       ", 40 - Len(strhost))
              Wscript.Echo Mid(objEvent.TimeWritten, 7, 2) & "." & Mid(objEvent.TimeWritten, 5, 2) & "." & Left(objEvent.TimeWritten, 4) & " " & Mid(objEvent.TimeWritten, 9, 2) & ":" & Mid(objEvent.TimeWritten, 11, 2) & ":" & Mid(objEvent.TimeWritten, 13, 2) & "; IP: " & IP & " (" & strhost & "); user: " & ArrayOfValues(1)
            
            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_success.vbs

  • Смирнов Саша:

    Идея супер. Реализация красива. Но…
    Данный скрипт не работает на Server 2008
    Меняю код события на четырезначный. Валится ошибка про индекс массива.

  • Смирнов Саша:

    Спасибо!

  • mstsc:

    Привет! Спасибо, скриптик очень выручает. Огорчает две вещи:
    — не получается перенаправить вывод в файл (либо стартую из проводника — тогда открывается окно командного процессора, записи пролетают и окно закрывается; либо из батника — тогда на каждую строку вылазит модальное окно и опять-таки, эхо в файл не перенаправляется).
    — нет возможности задать диапазон дат, скрипт раз за разом шерстит весь журнал, сколько бы тот не весил.
    Можно ли реализовать параметры Имя_файла, Нач_дата, Кон_дата?

    • mstsc:

      С выводом разобрался — стартовать не «start Success.vbs >result.txt», а «cscript Success.vbs >result.txt». А даты не запилите?

      • Вы как раз вовремя. Я сам задался вопросом про даты, т.к. скрипт начинает работать все дольше и дольше. Обновил текст скрипта, приложенный файл и текст статьи.

  • black1:

    набросок 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

  • black1:

    язык autoit )

    • autoit

      Не, не слышал. AutoIt, ровно как и PowerShell требует доустановки приложения.

      По существу: насколько я понимаю, скрипт для >= W2k8? На W2k3 выкинул обышку «Cannot parse #include» 🙁

  • black1:

    вот держи, дописал 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

  • mstsc:

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

    • fynjy:

      Спасибо за скрипт, но есть вопрос как или точнее с помощью чего его запустить на сервере? не в командной же строке ))

  • Sergey:

    Привет.
    Делаю аналогичный скрипт + скрипт закидывающий черный список IP адресов в IP filter локальной политики безопасности…
    Вопрос такой:
    не знаете, почему при задании в запросе интервала на TimeWritten менее 6 часов от текущего времени запрос не возвращает ни одной записи?

    Например, хочу выбрать ошибки логона за последние 5 или 20 минут.
    Если же сделаю запрос за 6+ последних часов, то запрос возвращает кучу записей, при этом в этих записях есть и TimeWritten 5 минутной давности.

    Думал, что таблица Win32_NTLogEvent как-то обновляется раз в несколько часов, но раз там есть недавние записи, значит это не так. Одним словом пока приходится перебирать результат запроса с 6 часовой информацией, и отсеивать записи, что немного затормаживает результат. Поэтому приходится пока раз в час обновлять черный список, иначе сервер начинает напрягаться. Хотелось бы более оперативно, например раз в 5 минут.

  • Игорь:

    Спасибо большое! Очень полезный скрипт. Это именно то, что я искал.

  • Виталий:

    Sergey, также буду благодарен за данный скрипт!

  • дима:

    На 2003 сервере выдает ошибку (154,9) недопустимый вызов или аргумент процедуры ‘Left’

    • Жуть. Удивлен, что до сих пор кто-то еще использует 2003 Server. Попробуйте отладить, что пишется в objEvent.TimeWritten

      • Макс:

        Вы не поверите — пользуются. Сравниваю серваки с 2008 и 2003 осями:
        2003 с 2 гигами оперативки так умеет работать с памятью и файлом подкачки, что не заваливается практически на тяжёлых задачках. Да — тупит, да — свопит, но тянет. А 2008 с 4мя гигами оперативки при достижении 98% заполнения физической памяти просто вешается.

        По сабжу темы — а реально выцепить из логов аудита какие пароли подбором вводили?

  • Богдан:

    Подскажите.
    А если
    при подключении и подбора пароля
    Сетевой адрес источника:-
    как включить чтобы IP показывало какой подключается

  • fed:

    помогите как эту скрип запустить я скачал запускаю ничево не происходит

  • Дмитрий:

    тут есть кто-нибудь,есть подобный скрипт для 2008?

    • Под 2008 нету, да и неактуально уже это, т.к. скрипт был написан в основном с целью банить адреса, с которых перебирают пароли, а для этого давно уже существует прекрасная бесплатная утилита IPBan, рекомендую.

Добавить комментарий для fynjy

(обязательнo)