Короткий ответ

PowerShell — основной админ-инструмент и любимый канал для вредоносных скриптов. Без Script Block Logging (событие 4104 в Microsoft-Windows-PowerShell/Operational) и Module Logging (4103) вы видите только факт запуска, не содержимое. Включите оба в GPO на jump, серверах и станциях админов; транскрипты (Start-Transcript политикой) — на выделенный шару с ACL. Не выключайте логи «шумит 1С». Не отключайте Defender вместо логирования.

Событие 400 в том же журнале — старт движка. Это мало для расследования. WinRM-сессии тоже должны попадать в лог: настройка WinRM. Privileged скрипты — с jump, не с daily WS-042 под DA: разделение учёток.

Симптомы и как отличить

  • Operational PowerShell пуст или только 400/403.
  • Реестр ScriptBlockLogging отсутствует.
  • Инцидент «кто-то вызвал Invoke-*», содержимого нет.
  • Транскрипт есть у админа локально в профиле и никуда не копируется.
Что естьЧего не хватает
4688 powershell.exeкомандлет внутри — нужен 4104
AMSI/Defender детектвсё равно нужен журнал для разбора ложноположительных
Constrained Languageне замена логированию
PS 5.1 vs 7политика Windows PowerShell ≠ автоматически PS 7; смотрите оба, если используете 7

Возможные причины

  1. GPO не настроена, клиент на defaults.
  2. Политика только на User, а нужен Computer.
  3. Журнал Operational отключён / крошечный.
  4. Смотрят Classic «Windows PowerShell» log, не Operational.
  5. PS 7 (pwsh) без своих политик.
  6. Админ запускает -Version 2.0 где ещё доступно — обход (на 11/2022 v2 обычно нет; проверьте).

Диагностика

Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging' -ErrorAction SilentlyContinue
Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging' -ErrorAction SilentlyContinue
Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription' -ErrorAction SilentlyContinue
Get-WinEvent -ListLog 'Microsoft-Windows-PowerShell/Operational' | Select-Object IsEnabled, MaximumSizeInBytes, RecordCount
Get-WinEvent -LogName 'Microsoft-Windows-PowerShell/Operational' -MaxEvents 8 -ErrorAction SilentlyContinue |
  Group-Object Id | Select-Object Name, Count

Прогон:

Write-Output 'aaadmin-laps-test'
Get-WinEvent -LogName 'Microsoft-Windows-PowerShell/Operational' -MaxEvents 15 |
  Where-Object Id -eq 4104 | Select-Object TimeCreated, Id

Если 4104 нет — политика не активна. Версия:

$PSVersionTable
Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2 -ErrorAction SilentlyContinue

Решение

Сценарий A. GPO Windows PowerShell 5.1

Computer Configuration → Administrative Templates → Windows Components → Windows PowerShell:

  • Turn on PowerShell Script Block Logging = Enabled (желательно также Invoke logging / «Log script block invocation start/stop», если есть в ваших ADMX)
  • Turn on Module Logging = Enabled, модули * (или список, если сознательно сужаете — * для jump)
  • Turn on PowerShell Transcription = Enabled, каталог \\fs01.contoso.example\ps-transcripts\%COMPUTERNAME%, Include invocation headers
gpupdate /force

Увеличьте Operational:

wevtutil sl Microsoft-Windows-PowerShell/Operational /ms:268435456

256 МБ — ориентир для jump; подберите.

Сценарий B. PowerShell 7

Отдельные ADMX PowerShell 7 / политики Microsoft: включите script block logging для pwsh. Проверьте журналы PowerShell 7 (имена каналов зависят от установки — смотрите Event Viewer Applications and Services Logs). Не считайте, что GPO 5.1 покрывает 7.

Сценарий C. Объём и PII

4104 содержит текст скрипта. Могут попасть пароли, если админы так пишут. Регламент: секреты в Vault, не в командной строке. Retention и доступ к логам — как к privileged data. Не выключайте 4104 из-за паролей — выключите пароли в скриптах.

Сценарий D. Централизация

WEF: канал Microsoft-Windows-PowerShell/Operational, ID 4103, 4104, плюс 400 на jump. Фильтр по машинам OU-Admins, если канал узкий; на серверах — тоже, не только ПК.

На DC01 логировать каждый 4104 с интерактивных админ-сессий полезно, но объём большой: начните с jump и OU серверов, станции пользователей подключите выборочно. WS-042 бухгалтера с включённым Script Block Logging без WEF затрёт Operational за день. Транскрипты на \\fs01.contoso.example\ps-transcripts не заменяют 4104: файл можно удалить локальным админом. Сопоставляйте 4104 с 4624 privileged-учётки — так видно, что скрипт шёл не с daily-drive. PowerShell 7 на тех же хостах проверьте отдельно: молчание 5.1 Operational не значит, что pwsh не исполнялся.

Как проверить, что проблема устранена

На WS-042 и jump после gpupdate:

Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging'
Get-WinEvent -LogName 'Microsoft-Windows-PowerShell/Operational' -MaxEvents 20 | Where-Object Id -eq 4104

EnableScriptBlockLogging = 1. Тестовая строка видна в 4104. Транскрипт появился на шаре (если включён). SIEM видит событие с jump.

Проверьте скрытый вызов powershell -enc на пилоте — 4104 должен содержать декодированный блок (поведение платформы).

Если не помогло

  • Политика User выиграла/проиграла Computer — смотрите gpresult.
  • Журнал не включён: wevtutil sl ... /e:true.
  • Смотрите Classic log Windows PowerShell — не тот канал.
  • AppLocker/WDAC режут скрипт до лога — смотрите и Code Integrity, и PowerShell.
  • PSRemoting: логи на целевом хосте и на клиенте по-разному; собирайте оба в расследовании.

Не решайте шум полным off.

Профилактика

  • Baseline jump: logging on, транскрипты, аудит входов.
  • Запрет PowerShell 2.0 feature где ещё встречается на Server 2019.
  • Обучение: не передавать пароли в -Password в открытую.
  • Алерт на 4104 с подозрительными cmdlet (Invoke-WebRequest к странным хостам) — на стороне SIEM, без выключения лога.
  • Часть hardening.

FAQ

Хватит ли только Transcription?

Нет. Файл на диске проще стереть, чем Operational+WEF. Транскрипт дополняет 4104, не заменяет.

Module Logging без Script Block

4103 видит вызовы модулей, не весь произвольный код. Нужны оба.

4104 слишком большой для SIEM

Собирайте с jump и серверов полностью; со станций пользователей — по риску/фильтру. Не с jump.

Constrained Language Mode вместо логов?

Дополнение. Админам на jump нужен Full Language + лог, иначе не работают инструменты.

Секреты в 4104 — нарушение?

Это аргумент не класть секреты в скрипты, плюс жёсткий ACL на SIEM. Выключение 4104 хуже для расследований.

Event 800 на Classic log

Старый канал. Для 5.1 якорь — Operational 4104. Classic можно оставить, не как единственный источник.