- копирование передаваемых сообщений для расследования инцидентов безопасности, в будущем;
- устранение возможности оправки не только конфиденциальной информации, но и различной нежелательной (спама, оскорбительных выражений, информации эротического содержания, огромных объёмов данных и т.п.);
- фильтрация нежелательной информации при получении, а не только при отправке;
- устранения способов использования информационных ресурсов, сотрудниками, в личных целях;
- уменьшение трафика, оптимизация нагрузки каналов;
- контроль рабочего времени сотрудников.
Наша ручная DLP система не будет решать все поставленные задачи, а сосредоточиться только на:
- поиске определенных файлов в сети;
- составление отчетов и доставка отчетов системному администратору или офицеру безопасности;
- выполнение удаления, по определенным критериям.
Поиск файлов на ОС Windows, простая и тривиальная задача. Даже поиск на удаленной машине не намного сложнее. А вот если нужно, что то найти на сотни машинах, тут уж возникает вопрос как? Не руками же проходиться по всем ПК. Данная задача довольно часто встречается в работе Windows админов, когда например, нужно провести аудит по хранению информации. Вы скажете, что есть примеры реализации, да, но они больше нацелены на поиск запрещенной для хранения данных (фильмов, игр и т.п), предложенный мной вариант реализует одну из задач DLP системы.
И так что умеет скрипт (точнее набор скриптов)? Что бы не утруждать администратора и не выгружать список ПК для проверки, скрипт интегрируется с AD и получает нужную ему информацию, имеется возможность фильтра по расширению и имени, добавления исключений, формирования отчета в папке и с возможностью отослать сообщение администратору и пользователю (Оповещение об обнаруженных файлах и рекомендациям, которое необходимо предпринять). Параметры можно комбинировать и менять, получая нужный функционал. После поиска формируется список файлов с именами ПК в которых хранится информация по поиску. Вторая часть скрипта это удаление найденных файлов, всех или по определенным критериям.
Мы используем этот скрипт с целью слежения за пользователями и сообщения им о необходимости хранить рабочие документы в определенном месте (личном сетевом диске), скрипт определяет активного пользователя на ПК, берет данные о почтовом ящике из АД и отсылает уведомления о том какой файл и где обнаружен на локальном диске, который пользователь должен переместить на сетевое хранилище либо он будет удален. В конечном итоге удаляем, то, что не по регламенту. Таким образом, реализуем одну из функций DLP-систем по контролю хранения конфиденциальной информации.
Алгоритм скрипта по поиску файлов
- Получает перечень рабочих станций из определенной OU
- Проверяет данные в атрибуте HomePage, если он имеет значение «Pass», пропускает поиск файлов, так как на этом компьютере уже осуществлялся поиск
- Проверяет доступность
- Если не доступен, записывает об этом в файл
- Если доступен, выполняет поиск файлов
- По окончанию поиска, записывает в атрибут HomePage – значение «Pass»
- Формируется файл и именем машины и перечнем найденных файлов
- Оправляется сообщение администратору с вложением
- Определяет имя локального пользователя
- Узнает в AD почтовый адрес пользователя
- Отправляет копию отчета
Алгоритм скрипта сброса обхода
- Получает перечень машин из AD
- Устанавливает значение атрибута notpass
Таким образом все машины, попадают в поле обработки скрипта, в том числе которые уже были отсканированы.
Алгоритм скрипта удаления файлов
- Загружает содержимое скрипта
- По каждой строке из списка (результата), удаляет объект на удаленном компьютере
Настройка скрипта и запуск (аудит файлов)
Скрипт выполняется как команда с заданными параметрами. Ниже приведены примеры запуска скрипта и его параметры. Start-AuditFiles – команда выполняющая скрипт. Параметры можно комбинировать, так как того требует поставленная задача.
Пример 1
Start-AuditFiles -OU "OU=Test,DC=root,DC=local" -SMTP smtp.server.com -AdminMail administrator@server.com -IncludeFile *.doc,*.docx,*.sys -ExclusionFile *File1*,*File2* -ExclusionFolder “*Folder1*,*Folder2*” -ReportPath \\server\reports\ - Throttle 5
В этом примере осуществляется поиск на компьютерах из OU, файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*), кроме каталогов (*Folder1*,*Folder2*), отчет дублируется в каталог (\\server\reports\). Отчет отсылается пользователю и администратору. Количество потоков равно 5-ти.
Пример 2
Start-AuditFiles -RemoteComputer ws-pc-4902,ws-pc-0982 -SMTP smtp.server.com -AdminMail administrator@server.com -Include *.doc,*.docx,*.sys -ExclusionFile *New*,*au* -AdminOnly - Throttle 10
В этом примере осуществляется поиск на компьютерах (ws-pc-4902,ws-pc-098), файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*). Отчет отсылается только администратору. Количество потоков равно 10-ти.
Параметры целевых компьютеров
OU (обязательный или необходимо задать RemoteComputer) – путь к организационной единице с целевыми компьютерами, если этот параметр не указан, следует указать параметр RemoteComputer. Один из двух этих параметров должен быть использован в скрипте.
Пример: -OU «OU=Test,DC=root,DC=local» или -OU $Computerlist (задается переменная в комбинации с другими скриптами).
RemoteComputer (обязательный или необходимо задать OU) – задается если необходимо выполнить скрипт только для определенных компьютеров из списка, либо один определенный, либо несколько указав из через запятую.
Пример: -RemoteComputer ws-pc-4902,ws-pc-0982
Параметры поиска
IncludeFile (обязательный, возможно использование маски *) – перечень файлов или их расширения, поиск которых необходимо выполнить (может быть списком).
ExclusionFile (опциональный) – перечень файлов, которые необходимо исключить из поиска (может быть списком).
ExclusionFolder (опциональный) – перечень исключенных из поиска каталогов.
Параметры отчетов
ReportPath (опциональный) – путь к сетевому ресурсу или локальному каталогу, в который будет выполняться копирование результатов сканирования.
AdminMail (опциональный) – адрес от имени которого выполняется отправка отчета, на этот же адрес будут доставляться отчеты предназначенные администратору.
SMTP (опциональный) – имя SMTP сервере, используемый в качестве шлюза отправки сообщений.
AdminOnly (опциональный) – включает режим отправки отчетов только администратору.
Throttle (обязательный, числовое значение от 1 – до 99) – устанавливает количество потоков сканирования.
Установка модулей
Необходимо в каталог «C:\Windows\system32\WindowsPowerShell\v1.0\Modules», скопировать файлы:
Invoke-Parallel.psm1
Start-AuditFiles.psm1
Перед выполнением скрипта модули необходимо импортировать:
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Invoke-Parallel.psm1
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Start-AuditFiles.psm1
Скрипт (модуль)
Данный скрипт необходимо сохранить как файл Invoke-Parallel.psm1.
function Invoke-Parallel {
[cmdletbinding(DefaultParameterSetName='ScriptBlock')]
Param (
[Parameter(Mandatory=$false,position=0,ParameterSetName='ScriptBlock')]
[System.Management.Automation.ScriptBlock]$ScriptBlock,
[Parameter(Mandatory=$false,ParameterSetName='ScriptFile')]
[ValidateScript({test-path $_ -pathtype leaf})]
$ScriptFile,
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[Alias('CN','__Server','IPAddress','Server','ComputerName')]
[PSObject]$InputObject,
[PSObject]$Parameter,
[switch]$ImportVariables,
[switch]$ImportModules,
[int]$Throttle = 20,
[int]$SleepTimer = 200,
[int]$RunspaceTimeout = 0,
[switch]$NoCloseOnTimeout = $false,
[int]$MaxQueue,
[validatescript({Test-Path (Split-Path $_ -parent)})]
[string]$LogFile = "C:\temp\log.log",
[switch] $Quiet = $false
)
Begin {
if( -not $PSBoundParameters.ContainsKey('MaxQueue') )
{
if($RunspaceTimeout -ne 0){ $script:MaxQueue = $Throttle }
else{ $script:MaxQueue = $Throttle * 3 }
}
else
{
$script:MaxQueue = $MaxQueue
}
Write-Verbose "Throttle: '$throttle' SleepTimer '$sleepTimer' runSpaceTimeout '$runspaceTimeout' maxQueue '$maxQueue' logFile '$logFile'"
if ($ImportVariables -or $ImportModules)
{
$StandardUserEnv = [powershell]::Create().addscript({
$Modules = Get-Module | Select -ExpandProperty Name
$Snapins = Get-PSSnapin | Select -ExpandProperty Name
$Variables = Get-Variable | Select -ExpandProperty Name
@{
Variables = $Variables
Modules = $Modules
Snapins = $Snapins
}
}).invoke()[0]
if ($ImportVariables) {
Function _temp {[cmdletbinding()] param() }
$VariablesToExclude = @( (Get-Command _temp | Select -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables )
Write-Verbose "Excluding variables $( ($VariablesToExclude | sort ) -join ", ")"
$UserVariables = @( Get-Variable | Where { -not ($VariablesToExclude -contains $_.Name) } )
Write-Verbose "Found variables to import: $( ($UserVariables | Select -expandproperty Name | Sort ) -join ", " | Out-String).`n"
}
if ($ImportModules)
{
$UserModules = @( Get-Module | Where {$StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue)} | Select -ExpandProperty Path )
$UserSnapins = @( Get-PSSnapin | Select -ExpandProperty Name | Where {$StandardUserEnv.Snapins -notcontains $_ } )
}
}
Function Get-RunspaceData {
[cmdletbinding()]
param( [switch]$Wait )
Do {
$more = $false
if (-not $Quiet) {
Write-Progress -Activity "Running Query" -Status "Starting threads"`
-CurrentOperation "$startedCount threads defined - $totalCount input objects - $script:completedCount input objects processed"`
-PercentComplete $( Try { $script:completedCount / $totalCount * 100 } Catch {0} )
}
Foreach($runspace in $runspaces) {
$currentdate = Get-Date
$runtime = $currentdate - $runspace.startTime
$runMin = [math]::Round( $runtime.totalminutes ,2 )
$log = "" | select Date, Action, Runtime, Status, Details
$log.Action = "Removing:'$($runspace.object)'"
$log.Date = $currentdate
$log.Runtime = "$runMin minutes"
If ($runspace.Runspace.isCompleted) {
$script:completedCount++
if($runspace.powershell.Streams.Error.Count -gt 0) {
$log.status = "CompletedWithErrors"
Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
foreach($ErrorRecord in $runspace.powershell.Streams.Error) {
Write-Error -ErrorRecord $ErrorRecord
}
}
else {
$log.status = "Completed"
Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
}
$runspace.powershell.EndInvoke($runspace.Runspace)
$runspace.powershell.dispose()
$runspace.Runspace = $null
$runspace.powershell = $null
}
ElseIf ( $runspaceTimeout -ne 0 -and $runtime.totalseconds -gt $runspaceTimeout) {
$script:completedCount++
$timedOutTasks = $true
$log.status = "TimedOut"
Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
Write-Error "Runspace timed out at $($runtime.totalseconds) seconds for the object:`n$($runspace.object | out-string)"
if (!$noCloseOnTimeout) { $runspace.powershell.dispose() }
$runspace.Runspace = $null
$runspace.powershell = $null
$completedCount++
}
ElseIf ($runspace.Runspace -ne $null ) {
$log = $null
$more = $true
}
if($logFile -and $log){
($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] | out-file $LogFile -append
}
}
$temphash = $runspaces.clone()
$temphash | Where { $_.runspace -eq $Null } | ForEach {
$Runspaces.remove($_)
}
if($PSBoundParameters['Wait']){ Start-Sleep -milliseconds $SleepTimer }
} while ($more -and $PSBoundParameters['Wait'])
}
if($PSCmdlet.ParameterSetName -eq 'ScriptFile')
{
$ScriptBlock = [scriptblock]::Create( $(Get-Content $ScriptFile | out-string) )
}
elseif($PSCmdlet.ParameterSetName -eq 'ScriptBlock')
{
[string[]]$ParamsToAdd = '$_'
if( $PSBoundParameters.ContainsKey('Parameter') )
{
$ParamsToAdd += '$Parameter'
}
$UsingVariableData = $Null
if($PSVersionTable.PSVersion.Major -gt 2)
{
$UsingVariables = $ScriptBlock.ast.FindAll({$args[0] -is [System.Management.Automation.Language.UsingExpressionAst]},$True)
If ($UsingVariables)
{
$List = New-Object 'System.Collections.Generic.List`1[System.Management.Automation.Language.VariableExpressionAst]'
ForEach ($Ast in $UsingVariables)
{
[void]$list.Add($Ast.SubExpression)
}
$UsingVar = $UsingVariables | Group SubExpression | ForEach {$_.Group | Select -First 1}
$UsingVariableData = ForEach ($Var in $UsingVar) {
Try
{
$Value = Get-Variable -Name $Var.SubExpression.VariablePath.UserPath -ErrorAction Stop
[pscustomobject]@{
Name = $Var.SubExpression.Extent.Text
Value = $Value.Value
NewName = ('$__using_{0}' -f $Var.SubExpression.VariablePath.UserPath)
NewVarName = ('__using_{0}' -f $Var.SubExpression.VariablePath.UserPath)
}
}
Catch
{
Write-Error "$($Var.SubExpression.Extent.Text) is not a valid Using: variable!"
}
}
$ParamsToAdd += $UsingVariableData | Select -ExpandProperty NewName -Unique
$NewParams = $UsingVariableData.NewName -join ', '
$Tuple = [Tuple]::Create($list, $NewParams)
$bindingFlags = [Reflection.BindingFlags]"Default,NonPublic,Instance"
$GetWithInputHandlingForInvokeCommandImpl = ($ScriptBlock.ast.gettype().GetMethod('GetWithInputHandlingForInvokeCommandImpl',$bindingFlags))
$StringScriptBlock = $GetWithInputHandlingForInvokeCommandImpl.Invoke($ScriptBlock.ast,@($Tuple))
$ScriptBlock = [scriptblock]::Create($StringScriptBlock)
Write-Verbose $StringScriptBlock
}
}
$ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param($($ParamsToAdd -Join ", "))`r`n" + $Scriptblock.ToString())
}
else
{
Throw "Must provide ScriptBlock or ScriptFile"; Break
}
Write-Debug "`$ScriptBlock: $($ScriptBlock | Out-String)"
Write-Verbose "Creating runspace pool and session states"
$sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
if ($ImportVariables)
{
if($UserVariables.count -gt 0)
{
foreach($Variable in $UserVariables)
{
$sessionstate.Variables.Add( (New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Variable.Name, $Variable.Value, $null) )
}
}
}
if ($ImportModules)
{
if($UserModules.count -gt 0)
{
foreach($ModulePath in $UserModules)
{
$sessionstate.ImportPSModule($ModulePath)
}
}
if($UserSnapins.count -gt 0)
{
foreach($PSSnapin in $UserSnapins)
{
[void]$sessionstate.ImportPSSnapIn($PSSnapin, [ref]$null)
}
}
}
$runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host)
$runspacepool.Open()
Write-Verbose "Creating empty collection to hold runspace jobs"
$Script:runspaces = New-Object System.Collections.ArrayList
$bound = $PSBoundParameters.keys -contains "InputObject"
if(-not $bound)
{
[System.Collections.ArrayList]$allObjects = @()
}
if( $LogFile ){
New-Item -ItemType file -path $logFile -force | Out-Null
("" | Select Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile
}
$log = "" | Select Date, Action, Runtime, Status, Details
$log.Date = Get-Date
$log.Action = "Batch processing started"
$log.Runtime = $null
$log.Status = "Started"
$log.Details = $null
if($logFile) {
($log | convertto-csv -Delimiter ";" -NoTypeInformation)[1] | Out-File $LogFile -Append
}
$timedOutTasks = $false
}
Process {
if($bound)
{
$allObjects = $InputObject
}
Else
{
[void]$allObjects.add( $InputObject )
}
}
End {
Try
{
$totalCount = $allObjects.count
$script:completedCount = 0
$startedCount = 0
foreach($object in $allObjects){
$powershell = [powershell]::Create()
if ($VerbosePreference -eq 'Continue')
{
[void]$PowerShell.AddScript({$VerbosePreference = 'Continue'})
}
[void]$PowerShell.AddScript($ScriptBlock).AddArgument($object)
if ($parameter)
{
[void]$PowerShell.AddArgument($parameter)
}
if ($UsingVariableData)
{
Foreach($UsingVariable in $UsingVariableData) {
Write-Verbose "Adding $($UsingVariable.Name) with value: $($UsingVariable.Value)"
[void]$PowerShell.AddArgument($UsingVariable.Value)
}
}
$powershell.RunspacePool = $runspacepool
$temp = "" | Select-Object PowerShell, StartTime, object, Runspace
$temp.PowerShell = $powershell
$temp.StartTime = Get-Date
$temp.object = $object
$temp.Runspace = $powershell.BeginInvoke()
$startedCount++
Write-Verbose ( "Adding {0} to collection at {1}" -f $temp.object, $temp.starttime.tostring() )
$runspaces.Add($temp) | Out-Null
Get-RunspaceData
$firstRun = $true
while ($runspaces.count -ge $Script:MaxQueue) {
if($firstRun){
Write-Verbose "$($runspaces.count) items running - exceeded $Script:MaxQueue limit."
}
$firstRun = $false
Get-RunspaceData
Start-Sleep -Milliseconds $sleepTimer
}
}
Write-Verbose ( "Finish processing the remaining runspace jobs: {0}" -f ( @($runspaces | Where {$_.Runspace -ne $Null}).Count) )
Get-RunspaceData -wait
if (-not $quiet) {
Write-Progress -Activity "Running Query" -Status "Starting threads" -Completed
}
}
Finally
{
if ( ($timedOutTasks -eq $false) -or ( ($timedOutTasks -eq $true) -and ($noCloseOnTimeout -eq $false) ) ) {
Write-Verbose "Closing the runspace pool"
$runspacepool.close()
}
[gc]::Collect()
}
}
}
Следующий скрипт необходимо сохранить как файл Start-AuditFiles.psm1.
$Body =
", напоминаем Вам о действии приказа о запрете хранения служебной информации на локальных дисках ПК.
В прикрепленном к письму файле, Вы найдете список документов обнаруженных на Вашем ПК потенциально содержащих служебную информацию.
Вам необходимо в кратчайшие сроки:
1) проверить указанные документы на предмет наличия служебной информации
2) переместить файлы содержащие служебную информацию на личный сетевой диск.
Function Start-AuditFiles {
<#
.Synopsis
Сканирует файлы на удаленной машине, в случае успеха отправляет отчет почтовым сообщением
.Description
Сканер позволяет обнаружить искомые файлы на удаленной машине через административный ресурс (C$ D$ .. и т.д).
После выполнения скрипт:
1. Находит объекты комрьютер в опеределенной OU или заданый компьютер через параметр
2. Проверяет доступность машины
3. Выполняет поиск всех дисков
4. Выполняет поиск искомых файлов с фильтрацией, формирует отчет
5. Отправляет отчет администратору
6. Получает активного пользователя, определяет его почтовый адрес, отправляет копию отчета
7. Формирует списки результатов сканирования, опционально копирует результаты на удаленный ресурс
.Examples
Пример 1
Start-AuditFiles -OU "OU=Test,DC=root,DC=local" -SMTP smtp.server.com -AdminMail administrator@server.com -IncludeFile *.doc,*.docx,*.sys -ExclusionFile *File1*,*File2* -ExclusionFolder *Folder1*,*Folder2* -ReportPath \\server\reports
В этом примере осуществляется поиск на компьютерах из OU, файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*), кроме каталогов (*Folder1*,*Folder2*), отчет дублируется в каталог (\\server\reports\)
Пример 2
Start-AuditFiles -RemoteComputer ws-pc-4902,ws-pc-0982 -SMTP smtp.server.com -AdminMail administrator@server.com -Include *.doc,*.docx,*.sys -ExclusionFile *New*,*au* -AdminOnly
В этом примере осуществляется поиск на компьютерах (ws-pc-4902,ws-pc-098), файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*), отчет отсылается только администратору
.Notes
Следует использовать только один из ключей OU или RemoteComputer, OU указывает организационную единицу, RemoteComputer указывает один или несколько компьютер в качестве объектов сканирования
.Link
...
#>
[CmdletBinding()]
Param (
[String]$OU,
[String[]]$RemoteComputer,
[String[]]$ExclusionFile,
[String]$ReportPath,
[String]$AdminMail,
[String[]]$IncludeFile,
[String[]]$ExclusionFolder,
[Switch]$AdminOnly = $false,
[String]$SMTP,
[String]$Throttle = 5
)
If (!$RemoteComputer) {$Hosts = (Get-ADComputer -Filter * -SearchBase $OU -Properties * | where { ( $PSItem.HomePage -notlike 'pass' )} ).name} else { $Hosts = $RemoteComputer }
invoke-parallel -InputObject $Hosts -throttle $Throttle -ImportVariables -ScriptBlock {
if(Test-Connection -ComputerName $_ -BufferSize 16 -quiet -count 2) {
$Object = $_
$ErrorActionPreference = 'SilentlyContinue'
$ExclusionFolder2 = $ExclusionFolder -replace ",","|"
$StartTime = (Get-Date).ToString()
$Hosts
(Get-WMIObject Win32_LogicalDisk -filter "DriveType = 3" -ComputerName $Object | %{Get-ChildItem ('\\' + $Object + '\' + ($_.DeviceID).remove(1) + '$\*') -Include $IncludeFile -Exclude $ExclusionFile -Recurse -Force | ?{$PSItem.FullName -notmatch $ExclusionFolder2}}).FullName | Out-File -FilePath $env:TEMP\$Object.txt -Encoding unicode
If (!$ReportPath) {} else {Copy-Item -Path $env:TEMP\$Object.txt -Destination $ReportPath -Force}
$EndTime = (Get-Date).ToString()
Write-Output ($Object) | Add-Content $env:TEMP\Online.txt
Invoke-Item $env:TEMP\$Object.txt
$Results = "" | Select ComputerName, "StartTime", "EndTime"
$Results.ComputerName = $Object
$Results.StartTime = $StartTime
$Results.EndTime =$EndTime
$Results
If ((Get-Content $env:TEMP\$Object.txt) -eq $Null) {}
else {
Try {
Send-MailMessage -SmtpServer $SMTP -to $AdminMail -Body $Object -From denis.pasternak@hotmail.com -Subject $Object -Attachments $env:TEMP\$Object.txt
} Catch {''}
If ($AdminOnly -eq $True) { Write-Host "Включен параметр AdminOnly - отчет отправлен только аминистратору" -ForegroundColor Yellow} else
{
$Username=((gwmi win32_computersystem -computer $Object -ErrorAction SilentlyContinue).UserName -split '\\')[1]
if($username -ne $null)
{
$Body = $Body
$dispalyname = (Get-AdUser $username -properties DisplayName).DisplayName
$email = (Get-AdUser $username -properties mail).mail
sleep -Seconds 3
Send-MailMessage -SmtpServer $SMTP -Body ( 'Уважаемый ' + $Dispalyname + ' ' + $Body | out-string ) -To $email -From $AdminMail -Subject $Object -Attachments $env:TEMP\$Object.txt -Encoding Unicode
}
}
}
else{ }
}
else {
(Write-Output ($Object + ' ' + (Get-Date).ToString()) | Add-Content $env:TEMP\Offline.txt)}
}
$OU= $null
$RemoteComputer = $null
$Hosts = $nul
Get-Content $env:TEMP\Online.txt | Set-ADComputer -HomePage 'pass'
}
Function Remove-AuditFiles {
[CmdletBinding(SupportsShouldProcess=$True)]
Param (
[String]$TargetFile
)
Get-Content -Path "$env:TEMP\$Path" | %{Remove-Item $PSItem}
}
Function Reset-AuditComputers {
[CmdletBinding(SupportsShouldProcess=$True)]
Param (
[String]$TargetOU
)
Get-ADComputer -Filter * -SearchBase $TargetOU -Properties * | Set-ADComputer -HomePage 'notpass'
'' | Set-Content -Path $env:TEMP\Online.txt
}
Выполнение по расписанию
Создайте файл, в примере каталог c:\scripts.
RunScript.ps1 — файл
Скопируйте текст:
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Invoke-Parallel.psm1
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Start-AuditFiles.psm1
# Ниже приведен пример, укажите предпочитаемые параметры и их значения
Start-AuditFiles -OU "OU=Test,DC=root,DC=local" -SMTP smtp.server.com -AdminMail administrator@server.com -IncludeFile *.doc,*.docx,*.sys -ExclusionFile *File1*,*File2* -ExclusionFolder *Folder1*,*Folder2* -ReportPath \\server\reports\ - Throttle 5
Создание расписания поиска файлов
- в управлении компьютером, перейдите в раздел «Планировщик заданий». Создайте задание, введите желаемое имя задания
- укажите периодичность «Ежедневное»
- установите период «каждый день»
- оставьте опцию «Запустить программу»
- путь к программе — C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe Аргумент запуска — C:\Scripts\RunScript.ps1
- после создания, откройте свойство задания, перейдите на вкладку «Триггеры»
- установите опцию «Повторить задачу каждые», указав нужную периодичность повторения
- установите опции «Выполнять для всех пользователей» и «Выполнять с наивысшим приоритетом». Укажите пользователя, имеющего право на чтение всех файлов на удаленных компьютерах. Как правило это пользователи, входящие в группу администраторов на удаленных компьютерах.
Информация о результатах и рабочих файлах
Скрипт хранит результаты сканирования в каталоге %TEMP%. В примере этот каталог: C:\Users\Администратор\AppData\Local\Temp. Если компьютер доступен и файл найден – создается файл с результатом. Если компьютер не доступен – информация об это добавляется в файл offline.txt.
Удаление найденных файлов
Запустите PowerShell выполните команду:
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Start-AuditFiles.psm1
Remove-AuditFiles
Или
Remove-AuditFiles - TargetFile ws-9281.txt,ws-8721.txt
Remove-AuditFiles — выполнит поиск всех результатов поиска, обработав каждый результат удалит файлы.
Вы можете указать конкретный файл (конкретный компьютер) Например:
Remove-AuditFiles — TargetFile ws-9281.txt,ws-8721.txt
Сброс перечня отсканированных компьютеров
Запустите PowerShell выполните команду:
Reset-AuditComputers
После выполнения, компьютеры в указанной OU будут отмечены как не отсканированы:
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Start-AuditFiles.psm1
Reset-AuditComputers - TargetOU OU "OU=Test,DC=root,DC=local"
$Body =
", напоминаем Вам о действии приказа о запрете хранения служебной информации на локальных дисках ПК.
В прикрепленном к письму файле, Вы найдете список документов обнаруженных на Вашем ПК потенциально содержащих служебную информацию.
Вам необходимо в кратчайшие сроки:
1) проверить указанные документы на предмет наличия служебной информации
2) переместить файлы содержащие служебную информацию на личный сетевой диск.
Начало функции, которой дано производное название Start-AuditFiles:
Function Start-AuditFiles {
Описание скрипта, сопроводительный текст справки. Позволяет использовать помощь, получить информацию о примерах и синтаксисе используя «Get-Help Start-AuditFiles»:
<#
.Synopsis
Сканирует файлы на удаленной машине, в случае успеха отправляет отчет почтовым сообщением
.Description
Сканер позволяет обнаружить искомые файлы на удаленной машине через административный ресурс (C$ D$ .. и т.д).
После выполнения скрипт:
1. Находит объекты комрьютер в опеределенной OU или заданый компьютер через параметр
2. Проверяет доступность машины
3. Выполняет поиск всех дисков
4. Выполняет поиск искомых файлов с фильтрацией, формирует отчет
5. Отправляет отчет администратору
6. Получает активного пользователя, определяет его почтовый адрес, отправляет копию отчета
7. Формирует списки результатов сканирования, опционально копирует результаты на удаленный ресурс
.Examples
Пример 1
Start-AuditFiles -OU "OU=Test,DC=root,DC=local" -SMTP smtp.server.com -AdminMail administrator@server.com -IncludeFile *.doc,*.docx,*.sys -ExclusionFile *File1*,*File2* -ExclusionFolder *Folder1*,*Folder2* -ReportPath \\server\reports
В этом примере осуществляется поиск на компьютерах из OU, файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*), кроме каталогов (*Folder1*,*Folder2*), отчет дублируется в каталог (\\server\reports\)
Пример 2
Start-AuditFiles -RemoteComputer ws-pc-4902,ws-pc-0982 -SMTP smtp.server.com -AdminMail administrator@server.com -Include *.doc,*.docx,*.sys -ExclusionFile *New*,*au* -AdminOnly
В этом примере осуществляется поиск на компьютерах (ws-pc-4902,ws-pc-098), файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*), отчет отсылается только администратору
.Notes
Следует использовать только один из ключей OU или RemoteComputer, OU указывает организационную единицу, RemoteComputer указывает один или несколько компьютер в качестве объектов сканирования
.Link
...
#>
Описываем переменные, которые в дальнейшем будем использовать в функции:
$OU — путь к Organization Unit в Active Directory
$ExclusionFile –перечень файлов, исключённых из поиска
$ReportPath – путь к каталогу, куда будут дублироваться отчеты
$AdminMail – адрес электронной администратора, от которого отправляются отчеты и куда будет приходить копия, предназначенная администратору
$IncludeFile – расширения файлов или имена файлов, поиск которых осуществляется
$ExclusionFolder – перечень папок, исключенных из поиска
$AdminOnly – параметр отвечающий, будут ли отчеты отправляться только администратору
$SMTP – адрес или имя SMTP сервера
$Throttle – количество параллельных потоков.
[CmdletBinding()]
Param (
[String]$OU,
[String[]]$RemoteComputer,
[String[]]$ExclusionFile,
[String]$ReportPath,
[String]$AdminMail,
[String[]]$IncludeFile,
[String[]]$ExclusionFolder,
[Switch]$AdminOnly = $false,
[String]$SMTP,
[String]$Throttle = 5
)
Выполняется проверка, если использовался ключ «RemoteComputer», в таком случае сканирование пройдет только на определенном (заданном) компьютере, если указан ключ «OU» — в этом случае список будет получен из определённой OU AD, будут выбраны компьютеры с атрибутом HomePage – не равным «pass» (это выполняется, дабы исключить повторный поиск на машинах, на которых уже был проведен поиск).
If (!$RemoteComputer) {$Hosts = (Get-ADComputer -Filter * -SearchBase $OU -Properties * | where { ( $PSItem.HomePage -notlike 'pass' )} ).name} else { $Hosts = $RemoteComputer }
Начало выполнения скрипта, подставляется значение Host и число потоков:
invoke-parallel -InputObject $Hosts -throttle $Throttle -ImportVariables -ScriptBlock {
Проверка доступности компьютера в сети:
if(Test-Connection -ComputerName $_ -BufferSize 16 -quiet -count 2) {
Присваиваются значения переменных, необходимых для выполнения поиска взятые из переменных описанных выше. Запоминается начало выполнения поиска.
$Object = $_
$ErrorActionPreference = 'SilentlyContinue'
$ExclusionFolder2 = $ExclusionFolder -replace ",","|"
$StartTime = (Get-Date).ToString()
$Hosts
Выполнение поиска:
- получение списка физических дисков (Get-WMIObject Win32_LogicalDisk -filter «DriveType = 3» -ComputerName $Object)
- формируется UNC путь с проставлением буквы диска (Get-ChildItem ('\\' + $Object + '\' + ($_.DeviceID).remove(1) + '$\*')
- указывается ключ поиска включенных объектов и исключения (-Include $IncludeFile -Exclude $ExclusionFile -Recurse -Force)
- исключение для каталогов, убираем из результатов поиска не нужные каталоги (?{$PSItem.FullName -notmatch $ExclusionFolder2}}).FullName)
- формируется отчет во временном каталоге (Out-File -FilePath $env:TEMP\$Object.txt -Encoding unicode)
(Get-WMIObject Win32_LogicalDisk -filter "DriveType = 3" -ComputerName $Object | %{Get-ChildItem ('\\' + $Object + '\' + ($_.DeviceID).remove(1) + '$\*') -Include $IncludeFile -Exclude $ExclusionFile -Recurse -Force | ?{$PSItem.FullName -notmatch $ExclusionFolder2}}).FullName | Out-File -FilePath $env:TEMP\$Object.txt -Encoding unicode
Если в момент запуска скрипта, был объявлен параметр «ReportPath», отчет копируется в заданный каталог.
If (!$ReportPath) {} else {Copy-Item -Path $env:TEMP\$Object.txt -Destination $ReportPath -Force}
Запоминаем время завершения поиска.
$EndTime = (Get-Date).ToString()
Добавляется запись в лог файл, с успешно завершенными операциями.
Write-Output ($Object) | Add-Content $env:TEMP\Online.txt
Вывод на экран таблицы с результатом поиска, начало поиска и его время его окончание, объект на котором производился поиск.
Invoke-Item $env:TEMP\$Object.txt
$Results = "" | Select ComputerName, "StartTime", "EndTime"
$Results.ComputerName = $Object
$Results.StartTime = $StartTime
$Results.EndTime =$EndTime
$Results
Открывает содержимое отчета, если он не пустой, выполняет действие (отправка отчета), если пустой ничего не выполняет. Предотвращает отправку пустых отчетов.
If ((Get-Content $env:TEMP\$Object.txt) -eq $Null) {}
Если файл отчета не оказался пустой, формируется сообщение с заданными параметрами SMTP сервера, адресом администратора, тестом сообщения, вложением отчета.
else {
Try {
Send-MailMessage -SmtpServer $SMTP -to $AdminMail -Body $Object -From denis.pasternak@hotmail.com -Subject $Object -Attachments $env:TEMP\$Object.txt
} Catch {''}
Сообщает на экране о том, что включён решим отправки только администратору, если был указан соответствующий ключ.
If ($AdminOnly -eq $True) { Write-Host "Включен параметр AdminOnly - отчет отправлен только аминистратору" -ForegroundColor Yellow} else
Получаем имя пользователя активного на удаленном компьютере.
{
$Username=((gwmi win32_computersystem -computer $Object -ErrorAction SilentlyContinue).UserName -split '\\')[1]
if($username -ne $null)
Если значение пользователя не пустое, ищем пользователя в Active Directory, определяем его значение почтовый адрес из моля Email. Получаем так же ФИО из Active Directory.
{
$Body = $Body
$dispalyname = (Get-AdUser $username -properties DisplayName).DisplayName
$email = (Get-AdUser $username -properties mail).mail
sleep -Seconds 3
Отправляем сообщение пользователю, с телом письма указанным в переменной $Body с подстановкой ФИО взятого из Active Directory.
Send-MailMessage -SmtpServer $SMTP -Body ( 'Уважаемый ' + $Dispalyname + ' ' + $Body | out-string ) -To $email -From $AdminMail -Subject $Object -Attachments $env:TEMP\$Object.txt -Encoding Unicode
Если компьютер не был доступен, записываем результат в лог файл, о том что он был не доступен.
}
}
}
else{ }
}
else {
(Write-Output ($Object + ' ' + (Get-Date).ToString()) | Add-Content $env:TEMP\Offline.txt)}
}
Сбрасываем переменные.
$OU= $null
$RemoteComputer = $null
$Hosts = $nul
Устанавливаем атрибут «HomePage» для объектов компьютеры в Active Directory. Что поможет нам не проводить повторный поиск на этих компьютерах, пока эти значения не будут сброшены.
Get-Content $env:TEMP\Online.txt | Set-ADComputer -HomePage 'pass'
}
Функция позволяет, открыть файл, получить содержимое (путь к файлу на удаленном компьютере), по каждой строке удалить файл на удаленном компьютере. $TargetFile – задает путь к файлу в котором хранится список файлов на удаление.
Function Remove-AuditFiles {
[CmdletBinding(SupportsShouldProcess=$True)]
Param (
[String]$TargetFile
)
Get-Content -Path "$env:TEMP\$Path" | %{Remove-Item $PSItem}
}
Функция позволяет сбросить атрибут HomePage объектов в Active Directory. Берет список компьютеров из указанной OU. Удаляет список в лог-файле.
Function Reset-AuditComputers {
[CmdletBinding(SupportsShouldProcess=$True)]
Param (
[String]$TargetOU
)
Get-ADComputer -Filter * -SearchBase $TargetOU -Properties * | Set-ADComputer -HomePage 'notpass'
'' | Set-Content -Path $env:TEMP\Online.txt
}
Комментарии (6)
foxmuldercp
13.01.2016 23:31Ну, то есть, штатную фичу 2012+, которая была анонсировала 3 года назад, по поиску данных по шаблонам, и классический пример которой — поиск серий и номеров паспортов и динамические ACL, автор не смотрел? Она же Dynamic access control, DAC.
например, по вот этой статье habrahabr.ru/post/176467system-admins
14.01.2016 09:39Этот скрипт работает не только на 2012r2, и скорее служит для поиска на клиентских ПК, ведь пользователи, как правило, хранят все свои файлы в папке Мои документы, или на рабочем столе.
foxmuldercp
16.01.2016 02:18Как правило, пользовательский профиль в нормальном окружении находится на сетевом диске с настроенной политикой регулярного резервного копирования. в том числе и от всяких порно-локеров.
Обычно одного инцидента ИБ достаточно для утверждения такой политики руководством.
Знаем, проходили.
grieverrr
То есть эта DLP-система ищет файлы с определенным расширением на компах в локалке?