diff --git a/Login_Monitor.ps1 b/Login_Monitor.ps1 index c37142d..ce821ee 100644 --- a/Login_Monitor.ps1 +++ b/Login_Monitor.ps1 @@ -10,6 +10,8 @@ - На некоторых серверах RDP-логин приходит как LogonType=3, поэтому интерактивные типы: 2/3/10. - Добавлены исключения шума: DWM-*, UMFD-*, HealthMailbox*, Font Driver Host*, NtLmSsp и др. - Heartbeat без дрейфа: используется nextHeartbeatTime (а не "прошло N секунд"). + - Win32_OperatingSystem.ProductType: 1 = рабочая станция (4624/4625 только LogonType 10 + при наличии журнала событие 1149 RCM); + 2/3 = сервер/КД — прежняя логика (типы 2, 3, 10). #> [CmdletBinding()] @@ -30,7 +32,7 @@ function Uc { param([int[]]$C) -join ($C | ForEach-Object { [char]$_ }) } # ============================================ # Версия (обновляйте при значимых изменениях; пишется в лог и в консоль при интерактивном запуске) -$ScriptVersion = "1.1.3" +$ScriptVersion = "1.2.0" # Логи $LogFile = "D:\Soft\Logs\login_monitor.log" @@ -55,6 +57,10 @@ $EnableRDGatewayMonitoring = $true $RDGatewayLogName = "Microsoft-Windows-TerminalServices-Gateway/Operational" $RDGatewayEvents = @(302, 303) +# RDP Remote Connection Manager (workstations): User authentication succeeded — событие 1149 +$RcmLogName = "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational" +$RcmEventId = 1149 + $ExcludedProcesses = @( "HTTP", "HTTP/*", "W3WP.EXE", "MSExchange", "SYSTEM", "LOCAL SERVICE", "NETWORK SERVICE", "OUTLOOK.EXE", "EXCHANGE", "EDGETRANSPORT", "STORE.EXE", @@ -216,6 +222,9 @@ if (-not (Test-Administrator)) { } Write-Log "Скрипт запущен с правами администратора, версия $ScriptVersion" +$script:IsWorkstation = $false +$script:OsInstallKindLabel = "" + function Enable-SecurityAudit { Write-Log "Checking security audit (auditpol) settings..." @@ -398,6 +407,97 @@ function Test-RDSDeploymentPresent { return $false } +function Get-OsInstallKind { + try { + $os = Get-CimInstance Win32_OperatingSystem -ErrorAction Stop + $pt = [int]$os.ProductType + # 1 Workstation, 2 Domain Controller, 3 Server (member) + $label = switch ($pt) { + 1 { 'Workstation' } + 2 { 'Domain Controller' } + 3 { 'Server' } + default { "ProductType=$pt" } + } + return [pscustomobject]@{ + ProductType = $pt + IsWorkstation = ($pt -eq 1) + Label = $label + } + } catch { + Write-Log "Определение SKU ОС не удалось: $($_.Exception.Message); считаем Server" + return [pscustomobject]@{ + ProductType = 3 + IsWorkstation = $false + Label = 'Unknown (assume Server)' + } + } +} + +function Test-RcmLogAvailable { + try { + $logExists = Get-WinEvent -ListLog $RcmLogName -ErrorAction SilentlyContinue + return [bool]$logExists + } catch { + return $false + } +} + +function Get-Rcm1149EventInfo { + param($Event) + $eventData = @{ + TimeCreated = $Event.TimeCreated + Username = "-" + ClientIP = "-" + } + try { + $map = Get-EventDataMap -Event $Event + $user = Get-FirstNonEmptyMapValue -DataMap $map -Keys @( + "TargetUser","User","Domain User","Param1","AccountName","ConnectionUser","SubjectUserName" + ) + $ip = Get-FirstNonEmptyMapValue -DataMap $map -Keys @( + "ClientIP","Client Address","IpAddress","Ip","Param3","Address","CallingStationId" + ) + if (-not [string]::IsNullOrWhiteSpace($user)) { $eventData.Username = [string]$user } + if (-not [string]::IsNullOrWhiteSpace($ip)) { $eventData.ClientIP = [string]$ip } + + if ($eventData.Username -eq '-' -and $Event.Properties.Count -gt 0) { + $eventData.Username = [string]$Event.Properties[0].Value + } + if ($eventData.ClientIP -eq '-') { + if ($Event.Properties.Count -gt 2) { + $eventData.ClientIP = [string]$Event.Properties[2].Value + } elseif ($Event.Properties.Count -gt 1) { + $eventData.ClientIP = [string]$Event.Properties[1].Value + } + } + } catch { + Write-Log "Ошибка разбора события RCM 1149: $($_.Exception.Message)" + } + return $eventData +} + +function Format-Rcm1149Event { + param( + [string]$Username, + [string]$ClientIP, + [datetime]$TimeCreated, + [string]$SecurityLogComputerName + ) + $logHost = $SecurityLogComputerName + if ([string]::IsNullOrWhiteSpace($logHost)) { $logHost = $env:COMPUTERNAME } + $hUser = (ConvertTo-TelegramHtml $Username) + $hIp = (ConvertTo-TelegramHtml $ClientIP) + $hLog = (ConvertTo-TelegramHtml $logHost) + $hTime = (ConvertTo-TelegramHtml ($TimeCreated.ToString('dd.MM.yyyy HH:mm:ss'))) + $message = "🔑 RDP: успешная аутентификация (1149)`r`n" + $message += "👤 Пользователь: $hUser`r`n" + $message += "🏢 Узел: $hLog`r`n" + $message += "🌐 Клиент (IP): $hIp`r`n" + $message += "🕐 Время: $hTime`r`n" + $message += "🔢 Event ID: 1149 (RemoteConnectionManager)" + return $message +} + function Send-Heartbeat { param([switch]$IsStartup = $false) @@ -408,6 +508,14 @@ function Send-Heartbeat { $message = "✅ Мониторинг логинов ЗАПУЩЕН`r`n" $message += "🖥️ Сервер: $hHost`r`n" $message += "🕐 Время запуска: $timestamp" + if ($script:OsInstallKindLabel) { + $message += "`r`n💻 Тип установки: $(ConvertTo-TelegramHtml $script:OsInstallKindLabel)" + } + if ($script:IsWorkstation) { + $message += "`r`n📌 Режим: рабочая станция — Security 4624/4625 только LogonType 10 (RDP); при наличии журнала — также 1149 (Remote Connection Manager)." + } else { + $message += "`r`n📌 Режим: сервер — Security 4624/4625, типы входа 2, 3, 10." + } if (Test-RDSDeploymentPresent) { $message += "`r`n🔐 RDS (хост сессий): обнаружены компоненты RDS помимо чистого шлюза — в мониторинг входят входы по RDP/RDS на этом узле (Security 4624/4625, типы входа по настройке скрипта)." } @@ -867,9 +975,18 @@ function Start-LoginMonitor { [switch]$MonitorInteractiveOnly = $true ) + $osKind = Get-OsInstallKind + $script:IsWorkstation = $osKind.IsWorkstation + $script:OsInstallKindLabel = $osKind.Label + Write-Log "Тип ОС (Win32_ProductType=$($osKind.ProductType)): $($osKind.Label)" + Write-Log "========================================" Write-Log "Запуск мониторинга логинов" - Write-Log "Интерактивные типы: 2,3,10" + if ($osKind.IsWorkstation) { + Write-Log "Режим рабочей станции: Security — только LogonType 10 (RDP); при наличии журнала — событие 1149 (Remote Connection Manager)." + } else { + Write-Log "Режим сервера: Security — LogonType 2, 3, 10" + } Write-Log "========================================" Cleanup-OldLogs @@ -879,11 +996,17 @@ function Start-LoginMonitor { $rdGatewayAvailable = $false if ($EnableRDGatewayMonitoring) { $rdGatewayAvailable = Test-RDGatewayLog } + $rcmMonitoringEnabled = ($osKind.IsWorkstation -and (Test-RcmLogAvailable)) + if ($osKind.IsWorkstation -and -not $rcmMonitoringEnabled) { + Write-Log "Рабочая станция: журнал Remote Connection Manager недоступен — уведомления только по Security 4624/4625 (LogonType 10). Проверьте, что включён удалённый рабочий стол." + } + $nextHeartbeatTime = (Get-Date).AddSeconds($HeartbeatInterval) $nextRotationCheck = Check-AndRotateLog $nextReportCheck = Check-AndSendDailyReport $lastCheckTime = (Get-Date).AddSeconds(-10) $lastGatewayCheckTime = (Get-Date).AddSeconds(-10) + $lastRcmCheckTime = (Get-Date).AddSeconds(-10) $monitorEvents = @(4624, 4625, 4648) while ($true) { @@ -906,7 +1029,11 @@ function Start-LoginMonitor { if ($event.Id -eq 4648) { $shouldIgnore = $true } elseif ($event.Id -in 4624, 4625) { - $interactiveTypes = @(2, 3, 10) + if ($osKind.IsWorkstation) { + $interactiveTypes = @(10) + } else { + $interactiveTypes = @(2, 3, 10) + } if ($interactiveTypes -notcontains $eventInfo.LogonType) { $shouldIgnore = $true } @@ -968,6 +1095,30 @@ function Start-LoginMonitor { } } + if ($rcmMonitoringEnabled) { + $rcmEvents = Get-WinEvent -FilterHashtable @{ + LogName = $RcmLogName + ID = $RcmEventId + StartTime = $lastRcmCheckTime + } -ErrorAction SilentlyContinue + + if ($rcmEvents) { + foreach ($event in $rcmEvents) { + if ($event.TimeCreated -le $lastRcmCheckTime) { continue } + $rcmInfo = Get-Rcm1149EventInfo -Event $event + if ($rcmInfo.Username -like "*$") { continue } + if (Should-IgnoreEvent -Username $rcmInfo.Username -ProcessName "-" ` + -ComputerName "-" -EventID 1149 -LogonType 10 -SourceIP $rcmInfo.ClientIP) { continue } + + $msg = Format-Rcm1149Event -Username $rcmInfo.Username -ClientIP $rcmInfo.ClientIP ` + -TimeCreated $rcmInfo.TimeCreated -SecurityLogComputerName $event.MachineName + Write-Log "Notify RCM 1149: User=$($rcmInfo.Username) IP=$($rcmInfo.ClientIP)" + Send-TelegramMessage -Message $msg | Out-Null + } + $lastRcmCheckTime = ($rcmEvents | Measure-Object -Property TimeCreated -Maximum | Select-Object -ExpandProperty Maximum).AddSeconds(1) + } + } + $now = Get-Date if ($now -ge $nextHeartbeatTime) { Send-Heartbeat