はじめに
くどうです。
今回は、PowerShellで実行されたコマンドのロギングの方法を解説します。
昨今のマルウェアは、Windowsを対象にしたものがほとんどです。
更にはPowerShellを利用するマルウェアも増えてきています。
PowerShell マルウェアが急増。シマンテックの解析で、95.4% が悪質なスクリプトと判明。
http://d.hatena.ne.jp/ripjyr/20161210/1481338727
また、企業内でもWindowsサーバーを利用するケースが増えています。
これまではLinuxでsyslogを利用し実行されたコマンドをログ取得し、集中管理するなどロギングは当たり前のように行われてきました。
しかし現在、主流となっているPowerShell 4.0ではロギングできません。
そのため、マルウェアはログの残らないPowershellを標的にしているのかもしれません。
主なロギング出来ない旧バージョンPowerShellのOS
- Windows Server 2012 R2
- Windows Server 2012
- Windows 2008 R2 SP1
- Windows 8.1
- Windows 7 SP1
- Windows 10 Version 1511以前
詳しい各バージョンのマトリクスは下記を参照してください。
https://msdn.microsoft.com/ja-jp/powershell/wmf/readme
2016/2/24にリリースされたPowerShell 5.0よりロギングの機能が実装されています。
そこで、最新版 PowerShell 5.1を参考にロギングの方法を解説していきます。
古いバージョンの場合はアップデートが必要です。
追記:PowerShell4.0でも下記のパッチをあてることでログ機能を拡張できます(by @stknohg)
8.1/2012 R2 → KB3000850
2012 → KB3119938
7/2008 R2 SP1 → KB3109118
ログの種類
PowerShell 5.1では取得できるログのは3種類あります。
- モジュールログ
- PowerShellスクリプトブロックのログ記録
- PowerShellトランスクリプション
各ログの取得方法とログの内容を解説していきます。
モジュールログ
モジュールログの取得を設定する場合、グループポリシーの設定をする必要があります。
グループポリシー [ユーザーの構成>管理用テンプレート>Windows PowerShell]を開きます。

「モジュールログを有効にする」を開き「有効」に変更します。

ポリシーの解説にはLogPipelineExecutionDetailsが変更されることが記載されています。
このポリシー設定を有効にした場合、指定したモジュールのメンバーのパイプライン実行イベントがイベント ビューアーの
Windows PowerShell ログに記録されます。モジュールに対してこのポリシー設定を有効にする操作は、
そのモジュールの LogPipelineExecutionDetails プロパティを True に設定する操作と同じ意味があります。
左ペインのモジュール名[表示]をクリックし下記を入力します。
Microsoft.PowerShell.*
Microsoft.WSMan.Management

以上で設定は完了です。
コマンドプロンプトなどでgpupdateを実行しポリシーを反映させます。
確認としてGet-Commandを引数に-Module Azure実行しAzureのコマンドを表示します。
PS C:\Users\kudo> Get-Command -Module Azure CommandType Name Version Source ----------- ---- Alias Add-WAPackEnvironment 2.0.1 Azure Alias Confirm-SSLegacyVolumeContainerStatus 2.0.1 Azure Alias Disable-WAPackWebsiteApplicationDiagnostic 2.0.1 Azure Alias Enable-WAPackWebsiteApplicationDiagnositc 2.0.1 Azure Alias Get-SSAccessControlRecord 2.0.1 Azure Alias Get-SSDevice 2.0.1 Azure Alias Get-SSDeviceBackup 2.0.1 Azure Alias Get-SSDeviceBackupPolicy 2.0.1 Azure Alias Get-SSDeviceConnectedInitiator 2.0.1 Azure Alias Get-SSDeviceVolume 2.0.1 Azure Alias Get-SSDeviceVolumeContainer 2.0.1 Azure Alias Get-SSFailoverVolumeContainers 2.0.1 Azure Alias Get-SSJob 2.0.1 Azure Alias Get-SSLegacyVolumeContainerConfirmStatus 2.0.1 Azure Alias Get-SSLegacyVolumeContainerMigrationPlan 2.0.1 Azure Alias Get-SSLegacyVolumeContainerStatus 2.0.1 Azure Alias Get-SSResource 2.0.1 Azure Alias Get-SSResourceContext 2.0.1 Azure
記録されたログを確認します。
イベントログを開きます。
ログは[アプリケーションとサービスログ > Microsoft > Windows > PowerShell >Operational]に記録されます。

ログの詳細には Get-Commandが実行され パラメーター Moduleに値 Azureが入力され実行されていることが記載されています。
CommandInvocation(Get-Command): "Get-Command"
パラメーター バインド(Get-Command): 名前="Module"; 値="Azure"
コンテキスト:
重要度 = Informational
ホスト名 = ConsoleHost
ホストのバージョン = 5.1.14393.693
ホスト ID = 8ee01dcc-0816-41d5-9a4e-a11740c598de
ホスト アプリケーション = C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
エンジンのバージョン = 5.1.14393.693
実行空間 ID = ba2f25ff-3cc4-4e20-9eda-e5493d041643
パイプライン ID = 25
コマンド名 = Get-Command
コマンドの種類 = Cmdlet
スクリプト名 =
コマンド パス =
シーケンス番号 = 64
ユーザー = xxxxx
接続されたユーザー =
シェル ID = Microsoft.PowerShell
ユーザー データ:
また、[Get-ChildItem]を実行した場合などは、Out-Defaultの値としてログに記載されます。
CommandInvocation(Out-Default): "Out-Default" パラメーター バインド(Out-Default): 名前="InputObject"; 値=".azurefunctions" パラメーター バインド(Out-Default): 名前="InputObject"; 値=".DataGrip2016.2" パラメーター バインド(Out-Default): 名前="InputObject"; 値=".dnx" パラメーター バインド(Out-Default): 名前="InputObject"; 値=".LSC" パラメーター バインド(Out-Default): 名前="InputObject"; 値=".nuget" パラメーター バインド(Out-Default): 名前="InputObject"; 値=".QtWebEngineProcess" パラメーター バインド(Out-Default): 名前="InputObject"; 値=".vscode" パラメーター バインド(Out-Default): 名前="InputObject"; 値="Contacts" パラメーター バインド(Out-Default): 名前="InputObject"; 値="Desktop" パラメーター バインド(Out-Default): 名前="InputObject"; 値="Documents" ~~~~~~
以上のように実行されたコマンドと一部の出力などイベントログ形式で取得が可能となります。
最低限、このポリシーを有効にすることをオススメします。
PowerShellスクリプトブロックのログ記録
モジュールのロギングと同様にグループポリシー [ユーザーの構成>管理用テンプレート>Windows PowerShell]を開きます。
「PowerShell スクリプト ブロックのログ記録を有効にする」を開き「有効」変更します。

このポリシー設定を使用して、すべての PowerShell スクリプト入力を Microsoft-Windows-PowerShell/操作イベント ログに記録することができます。
このポリシー設定を有効にした場合、Windows PowerShell のコマンド処理、スクリプト ブロック、関数、スクリプトが記録されます。
対話的に呼び出された場合も、自動的に呼び出された場合もすべて記録されます。
[Get-Command -Module Azure]を実行すると、実行したコマンドと、コマンドが実行されたときに利用されたスクリプトなどがすべて記載されます。
イベントログを確認します。

コマンド記録
Scriptblock テキストを作成しています (1 個中 1 個目): Get-Command -Module Azure ScriptBlock ID: c96cd900-dc7a-4433-ada7-18992d64c57e パス:
更に、コマンドのモジュールが記載されます(一部)。
Scriptblock テキストを作成しています (1 個中 1 個目):
#
# Module manifest for module 'Azure'
#
# Generated by: Microsoft Corporation
#
# Generated on: 5/23/2012
#
@{
# Version number of this module.
ModuleVersion = '2.0.1'
# ID used to uniquely identify this module
GUID = 'D48CF693-4125-4D2D-8790-1514F44CE325'
# Author of this module
Author = 'Microsoft Corporation'
# Company or vendor of this module
CompanyName = 'Microsoft Corporation'
# Copyright statement for this module
Copyright = 'Microsoft Corporation. All rights reserved.'
# Description of the functionality provided by this module
Description = 'Microsoft Azure PowerShell - Service Management'
# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = '3.0'
# Name of the Windows PowerShell host required by this module
PowerShellHostName = ''
# Minimum version of the Windows PowerShell host required by this module
PowerShellHostVersion = ''
# Minimum version of the .NET Framework required by this module
DotNetFrameworkVersion = '4.0'
# Minimum version of the common language runtime (CLR) required by this module
CLRVersion='4.0'
# Processor architecture (None, X86, Amd64, IA64) required by this module
ProcessorArchitecture = 'None'
# Modules that must be imported into the global environment prior to importing this module
RequiredModules = @( @{ ModuleName = 'Azure.Storage'; ModuleVersion = '2.0.1'})
# Assemblies that must be loaded prior to importing this module
RequiredAssemblies = @()
# Script files (.ps1) that are run in the caller's environment prior to importing this module
ScriptsToProcess = @()
# Type files (.ps1xml) to be loaded when importing this module
TypesToProcess = @(
'.\Services\Microsoft.WindowsAzure.Commands.Websites.Types.ps1xml',
'.\Sql\Microsoft.WindowsAzure.Commands.SqlDatabase.Types.ps1xml',
'.\StorSimple\Microsoft.WindowsAzure.Commands.StorSimple.Types.ps1xml'
)
# Format files (.ps1xml) to be loaded when importing this module
FormatsToProcess = @(
'.\Services\Microsoft.WindowsAzure.Commands.Websites.format.ps1xml',
'.\Services\Microsoft.WindowsAzure.Commands.CloudService.format.ps1xml',
'.\Services\Microsoft.WindowsAzure.Commands.ServiceBus.format.ps1xml',
'.\Services\Microsoft.WindowsAzure.Commands.Store.format.ps1xml',
'.\Services\Microsoft.WindowsAzure.Commands.Scheduler.format.ps1xml',
'.\Compute\Microsoft.WindowsAzure.Commands.ServiceManagement.format.ps1xml',
'.\Services\Microsoft.WindowsAzure.Commands.Profile.format.ps1xml',
'.\Networking\Microsoft.WindowsAzure.Commands.ServiceManagement.Network.format.ps1xml',
'.\StorSimple\Microsoft.WindowsAzure.Commands.StorSimple.format.ps1xml'
)
# Modules to import as nested modules of the module specified in ModuleToProcess
NestedModules = '.\Automation\Microsoft.Azure.Commands.Automation.dll',
'.\Compute\Microsoft.WindowsAzure.Commands.ServiceManagement.dll',
'.\HDInsight\Microsoft.WindowsAzure.Commands.HDInsight.dll',
'.\ManagedCache\Microsoft.Azure.Commands.ManagedCache.dll',
'.\Networking\Microsoft.WindowsAzure.Commands.ServiceManagement.Network.dll',
'.\RecoveryServices\Microsoft.Azure.Commands.RecoveryServicesRdfe.dll',
'.\RemoteApp\Microsoft.WindowsAzure.Commands.RemoteApp.dll',
'.\Services\Microsoft.WindowsAzure.Commands.dll',
'.\Services\Microsoft.WindowsAzure.Commands.Profile.dll',
'.\Sql\Microsoft.WindowsAzure.Commands.SqlDatabase.dll',
'.\StorSimple\Microsoft.WindowsAzure.Commands.StorSimple.dll',
'.\TrafficManager\Microsoft.WindowsAzure.Commands.TrafficManager.dll'
# Functions to export from this module
FunctionsToExport = '*'
# Cmdlets to export from this module
CmdletsToExport = '*'
# Variables to export from this module
VariablesToExport = '*'
# Aliases to export from this module
AliasesToExport = @(
'Add-WAPackEnvironment',
'Disable-WAPackWebsiteApplicationDiagnostic'
'Enable-WAPackWebsiteApplicationDiagnositc'
'Get-WAPackEnvironment',
'Get-WAPackPublishSettingsFile',
'Get-WAPackSBLocation',
'Get-WAPackSBNamespace',
'Get-WAPackSubscription',
'Get-WAPackWebsite',
'Get-WAPackWebsiteDeployment',
'Get-WAPackWebsiteLocation',
'Get-WAPackWebsiteLog',
'Import-WAPackPublishSettingsFile',
'New-WAPackSBNamespace',
'New-WAPackWebsite',
'Remove-WAPackEnvironment',
'Remove-WAPackSBNamespace',
'Remove-WAPackSubscription',
'Remove-WAPackWebsite',
'Restart-WAPackWebsite',
'Restore-WAPackWebsiteDeployment',
'Save-WAPackWebsiteLog',
~~~~~~~
以上のようにログの取得ができます。
ただし、実行結果(標準出力など)を記録することができません。
PowerShellトランスクリプション
これまで、PowerShell4.0 ではTranscript の機能は利用できました。
Start-Transcript
上記のコマンドで実行ログの出力ができます。
ですが、ご丁寧にコマンドを実行するマルウェアは存在しません(きっと)。
そこで、PowerShell5.0以降ではグループポリシーで設定するころで解決します。
グループポリシー [ユーザーの構成>管理用テンプレート>Windows PowerShell]を開きます。
「PowerShell トランスクリプションを有効にする」を開き「有効」変更します。
また、出力先を入力します。
解説にも記載されているがイベントログではなく、テキストへの出力となります。
このポリシー設定を使用して、Windows PowerShell コマンドの入出力をテキスト ベースのトランスクリプトとしてキャプチャできます。
このポリシー設定を有効にした場合、Windows PowerShell、Windows PowerShell ISE、さらに Windows PowerShell エンジンを利用するすべての
アプリケーションに対して、トランスクリプトの作成が有効になります。既定では、各ユーザーのマイ ドキュメント ディレクトリにトランスクリプト出力が
記録されます。記録されるファイルの名前は、’PowerShell_transcript’ にコンピューター名と開始日時が付加された形式になります。このポリシーを
有効にすることは、各 Windows PowerShell セッションで Start-Transcript コマンドレットを呼び出すことと同じです。

出力されるログは「PowerShell_transcript.マシン名.ランダム英数字.20170124132441」の形式で保存されます。

ログには実行された内容がそのまま記載されます。
********************** Windows PowerShell トランスクリプト開始 開始時刻: 20170124160909 ユーザー名: xxxxxxx RunAs ユーザー: xxxxxxxxx コンピューター: xxxxxxxxxx (Microsoft Windows NT 10.0.14393.0) ホスト アプリケーション: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe プロセス ID: 2596 PSVersion: 5.1.14393.693 PSEdition: Desktop PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.14393.693 BuildVersion: 10.0.14393.693 CLRVersion: 4.0.30319.42000 WSManStackVersion: 3.0 PSRemotingProtocolVersion: 2.3 SerializationVersion: 1.1.0.1 ********************** PS C:\Users\kudo> Get-Command -Module Azure CommandType Name Version Source ----------- ---- ------- ------ Alias Add-WAPackEnvironment 2.0.1 Azure Alias Confirm-SSLegacyVolumeContainerStatus 2.0.1 Azure Alias Disable-WAPackWebsiteApplicationDiagnostic 2.0.1 Azure Alias Enable-WAPackWebsiteApplicationDiagnositc 2.0.1 Azure Alias Get-SSAccessControlRecord 2.0.1 Azure Alias Get-SSDevice 2.0.1 Azure Alias Get-SSDeviceBackup 2.0.1 Azure Alias Get-SSDeviceBackupPolicy 2.0.1 Azure Alias Get-SSDeviceConnectedInitiator 2.0.1 Azure ~~~~~~
以上のようにテキストで出力されます。
ただし、プロセス(?)ごとにファイルが分かれてしまいます。
テキストなので加工、一括管理し易いかと思います。
まとめ
PowerShellのロギングについて3つ解説しました。3つとも有効にしてログを取得するのがベストだと思います。
最低限でも「モジュールログの取得」を行うことで、監査ログを取得し、いざというときに備えましょう。皆さんLinuxではやってるので、Windowsでも行いましょう。
また、Operations Management Suiteなどでイベントログを一括収集することでログ運用が楽になるかと思います。 Linuxに対応できるのでOS関係なく一括管理できるので利用してみてはいかがでしょうか。
ではでは