Сбор информации об установке “заплаток” (HotFix’ов) в ОС Windows (на примере HotFix’ов из бюллетеня безопасности MS12-020)

13.03.2012 Microsoft выпустила очередную порцию обновлений, устраняющих недавно обнаруженные уязвимости. Среди этих “заплаток” имеет место быть и критическое обновление, устраняющее уязвимость в протоколе RDP. Успешная эксплуатация этой уязвимости позволит злоумышленнику удаленно выполнить произвольный код на атакуемой машине. Процитирую бюллетень безопасности MS12-020:

Настоящее обновление для системы безопасности устраняет две обнаруженные пользователями уязвимости в протоколе удаленного рабочего стола. Наиболее серьезная из этих уязвимостей делает возможным удаленное выполнение кода, если злоумышленник отправляет уязвимой системе последовательность специально созданных пакетов RDP. По умолчанию протокол удаленного рабочего стола (RDP) отключен во всех операционных системах Windows. Системы, на которых не включен RDP, не подвержены данной уязвимости.

Это обновление безопасности имеет уровень “критический” для всех поддерживаемых версий Microsoft Windows

Уже опубликован PoC-код (PoC – Proof of Concept), а так же появляются разной степени достоверности сообщения о наличии работоспособного эксплоита для данной уязвимости. Посему, пока гром не грянул, нужно заткнуть эту брешь в обороне. Для этого необходимо установить обновления, указанные в бюллетене MS12-020, после чего проверить, что обновления успешно установлены.

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

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

################################################################################
#  Get-CompsHotFixiesInfo.ps1 PowerShell 20120319 ShS
################################################################################

<#
.SYNOPSIS
Получаем всевозможные данные о компьютерах в организации и сохраняем их в CSV-файле отчета

.PARAMETER    SearchRoot
задает корневой контейнер, с которого будет начат поик учетных записей компьютеров,
подлежащих опросу
Если не задано иное, то по умолчанию принимает значение "$(([ADSI]"LDAP://rootDSE").rootDomainNamingContext)",
которое равно DN корневого домена леса.

.PARAMETER SearchScope
область поиска, может принимать одно из 3х возможных значений:
'Base'     -    ограничивает область поиска базовым объектом (SearchRoot)
'OneLevel' -    поиск только прямых потомков базового объекта (SearchRoot),
                за исключением самого базового объекта
'Subtree' -        поиск по всему поддереву, начиная с базового объекта (SearchRoot),
                включая сам базовый объект
                    Если не задано иное, то по умолчанию принимает значение 'Subtree'
.PARAMETER    ReportFileName
полное имя файла отчета
.EXAMPLE
Get-CompsProperties.ps1 -ReportFileName .\my_report.txt
#>

param([Quest.ActiveRoles.ArsPowerShellSnapIn.Data.IdentityParameter]$SearchRoot="$(([ADSI]"LDAP://RootDSE").defaultNamingContext)",`
        [DirectoryServices.SearchScope]$SearchScope="Subtree", [Parameter(Mandatory=$true)] $ReportFileName)
#================================================================================================================
<################## начало функции #############################################

.Synopsis
Функция Get-CompHFInfo предназначена для получения информации о заданном перечне обновлений (HotFix'ов).

.Parameter CompName
имя исследуемого компьютера

.Parameter $HotFixes
массив, содержащий названия HotFix'ов, информацию о которых требуется получить
#################################################################################>
Function Get-CompHFInfo ($CompName=".", $HotFixes=@("KB2621440","KB2667402")) {
    #
    #Создаем объект с полями, в котором будем хранитть свойства опрашиваемог окомпьютера
    $objComp=New-Object PSObject
    #Добавляем поля к объекту
    $objComp|Add-Member NoteProperty CompName -Value $null
    $objComp|Add-Member NoteProperty OSVersion -Value $null
    $objComp|Add-Member NoteProperty SPVersion -Value $null
    $objComp|Add-Member NoteProperty fDenyTSConnections -Value $null
    $HotFixes| foreach {$objComp|Add-Member NoteProperty $_ -Value $null}
    #
    #======================= Здесь начинаем заполнять поля объекта ==========================
    #
    #Имя компьютера
    $OS=gwmi win32_OperatingSystem -ComputerName $CompName -ErrorAction Stop
    $objComp.CompName=$OS.CSName
    #Версия OS И ServicePack'а
    $objComp.OSVersion=$OS.Version
    $objComp.SPVersion=$OS.ServicePackMajorVersion
    #Флаг разрешения доступа к удаленному рабочему столу
    $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $CompName)
    $regKey=$reg.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server")
    $objComp.fDenyTSConnections=$regKey.GetValue("fDenyTSConnections")
    #Информация о HotFix'ах
    $HotFixes| foreach {
        if (Get-HotFix -Id $_ -ComputerName $CompName -ErrorAction SilentlyContinue) {
            #Обновление было найдено
            $objComp.$_=1
        }
        else {
            #Обновление не было найдено
            $objComp.$_=0
        }
    }
    #Вовращем objComp, в качестве результата работы функции
    $objComp
}
########################### конец функции ###############################
#
#================= Точка входа скрипта ==================================
cls
Write-Host $(Get-Date)
#
$VerbosePreference = "Continue"
#Папка, из которой был запущен скрипт
$ScriptPath=Split-Path $MyInvocation.MyCommand.Path
#Если существует старый отчет, то...
if (Test-Path $ReportFileName) {
    #...загрузим данные из него в хэш-таблицу
    $ReportHash=import-csv $ReportFileName -UseCulture| group -AsHashTable CompName
}
#Создадим пустой массив, в который будем собирать информацию об опрашиваемых компьютерах
$DomainCompsProps=@()
#аналогично, создаем массив для формирования лог-файла выполнения скрипта
$PollLog=@()
#Выбираем из заданной ветки все незаблокированные компьютеры
$Comps = Get-QADComputer -IncludedProperties lastLogontimeStamp -ErrorAction SilentlyContinue -SearchRoot $SearchRoot -SearchScope $SearchScope -SizeLimit 0 |
     select name, lastLogontimeStamp, Description, whenCreated, @{Name="Disabled"; Exp={$_.useraccountcontrol -band 2}}|
    ?{$_.Disabled -eq 0}
#Перебираем все компьютеры и получаем их свойства
foreach ($Comp in $Comps) {
    #создаем объект для сбора информации о процессе опроса компьютера (логирование опроса)
    #и заполняем его поля значениями
    $objPol=New-Object PSObject
    $objPol|Add-Member NoteProperty CompName -Value $Comp.Name
    $objPol|Add-Member NoteProperty Description -Value $Comp.Description
    $objPol|Add-Member NoteProperty ErrorNumber -Value 0
    $objPol|Add-Member NoteProperty whenCreated -Value $Comp.whenCreated
    $objPol|Add-Member NoteProperty lastLogonTimeStamp -Value $Comp.lastLogontimeStamp
    $objPol|Add-Member NoteProperty PollDate -Value (Get-Date)
    #
    #
    $PollingError=$false
      if (Test-Connection $Comp.Name -Count 1 -Quiet) {
        try {
            #Write-Host "CompName=",$Comp.Name, "  CompDescription=", $Comp.Description #, "  LoggedonUser=", $LoggedonUserName
            Write-Host "$("CompName= {0,-15} CompDescription= {1,-20}" -f $Comp.Name, $Comp.Description)"
            $CurCompProps=Get-CompHFInfo -CompName $Comp.Name
            #Добавим еще несколько полей к объекту со свойствами компьютера
            ## Добавляем Description
            $CurCompProps| Add-Member NoteProperty Description -Value $Comp.Description
            ## Добавляем lastLogonTimeStamp
            $CurCompProps| Add-Member NoteProperty lastLogonTimeStamp -Value $Comp.lastLogontimeStamp
            ## Добавляем whenCreated
            $CurCompProps| Add-Member NoteProperty whenCreated -Value $Comp.whenCreated
            ##Добавляем дату сбора информации
            $CurCompProps| Add-Member NoteProperty ReportDate -Value (Get-Date)
            #$DomainCompsProps[$CurCompProps.CompName]=$CurCompProps
            $DomainCompsProps+=$CurCompProps
        }
        catch {
            $PollingError=$true
            #если компьютер пингуется, но его не удалось опросить, то в лог помещаем Error #5
            $objPol.ErrorNumber=5
            Write-Verbose "$("Ошибка доступа к компьютеру CompName= {0,-15}  CompDescription= {1,-20} LastLogonTimeStamp= {2,8}" -f $Comp.Name, $Comp.Description, $Comp.lastLogontimeStamp.ToString("yyyyMMdd"))"
        }
     }
    else {
        $PollingError=$true
        #если компьютер не пингуется, то в лог идет Error #1
        $objPol.ErrorNumber=1
        Write-Verbose "$("Компьютер CompName= {0,-15}  CompDescription= {1,-20} не пингуется. LastLogonTimeStamp= {2,8}" -f $Comp.Name, $Comp.Description, $Comp.lastLogontimeStamp.ToString("yyyyMMdd"))"
    }
    #Если произошла ошибка во время опроса компьютера,
    #а в старом отчете информация об этом компьютере имеется...
    if ($PollingError -and $ReportHash) {
        if ($ReportHash[$Comp.Name]) {
           #..., то добавим эту информацию к формируемому отчету
           $DomainCompsProps+=$ReportHash[$Comp.Name]
        }
    }
    $PollLog+=$objPol
}
if ($DomainCompsProps) {
    Write-Host "Выгружаем результаты в файл отчета.."
    # Выгружаем результат в файл
    $DomainCompsProps| Export-Csv -Path $ReportFileName -UseCulture #-Delimiter ";"
    Write-Host $(Get-Date)
}
else {
    Write-Host "Данных для формирования отчета собрать не удалось!"
}
#Если лог-объект не пустой, то выгружаем его в лог-файл
if ($PollLog) {
    $PollLog| Export-Csv -Path ((Split-Path $ReportFileName)+"\log.csv") -UseCulture
}
Write-Host "Работа завершена!"

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

HotFixesReport

Описывать подробно скрипт не буду, т.к.ничего принципиально нового в нем нет (по сравнению с исходным скриптом). Ну, а краткое описание таково:

  • Скрипт опрашивает все компьютеры, чьи незаблокированные учетные записи будут найдены в AD (в текущем домене).
  • Скрипт проверяет доступность компьютера (однократный пинг),
  • В случае удачи опрашивает удаленный компьютер на предмет наличия на нем обновлений, затыкающих “дыру” в RDP, а так же извлекает некоторую сопутствующую информацию.
  • Результат выгружает в CSV-файл, который удобно анализировать в Excel при помощи автофильтра.

Информацию об уязвимости см. здесь:

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.