ITセキュリティ用のPowerShell。 パートIV:スクリプトプラットフォーム





このシリーズの以前のメモでは、個々のスクリプト(イベント処理用、分類用)を1つのシステムに結合する可能性を提案しました。 1つのPowerShellコードに基づいてセキュリティプラットフォームをスワイプすることは可能ですか?



主にPowerShellの激烈なイベントに関連するいくつかの詳細に取り組んだ後、私は勝利を宣言することができ、スクリプトに基づいたセキュリティプラットフォーム-SSP(Security Scripting Platform)の特許を登録しました。



米国のPowerShell



PowerShellで忘れられない経験を得ている間、スクリプトで自分の結果を覚えていない人もいることに気付きました。

一緒に覚えましょう。



最初のメモでは、ファイルアクセスイベントを監視し、PSに似たスクリプトブロック(つまり、独自のメモリ領域で実行されるスクリプトコード)を実行するPowerShellコードの素晴らしいラインを紹介しました。



1. Register-WmiEvent -Query "SELECT * FROM __InstanceModificationEvent WITHIN 5 WHERE TargetInstance ISA 'CIM_DataFile' and TargetInstance.Path = '\\Users\\bob\\' and targetInstance.Drive = 'C:' and (targetInstance.Extension = 'doc' or targetInstance.Extension = 'txt)' and targetInstance.LastAccessed > '$($cur)' " -sourceIdentifier "Accessor" -Action $action
      
      





スクリプトのいくつかの要素を追加することで、ファイルアクセス分析アプリケーションと呼ばれるコードを取得しました。 実際、アクセスイベントをカウントし、いくつかの基本的な統計情報を表示するとともに、ハッキングの試みを示す可能性のあるアクセスサージを特定します。



これは、ここでVaronisで広く使用されているユーザー行動分析テクノロジーの簡易バージョンです



これまでのところ、とても良い。



次に、3番目のメモで、PowerShellを使用して、フォルダー内のファイルを比較的簡単にスキャンおよび分類する方法を示しました。 このアクションはディスク使用量が多いため、Runspacesと呼ばれるPowerShellマルチタスク機能を使用して分類を高速化することは理にかなっています。



Varonis Data Classification Frameworkなどの既存のファイルイベント処理およびデータ分類ソリューションでは、ファイル操作を分類モジュールに転送することにより、より合理的なアプローチでファイルを分類します。



なんで?



ファイルの内容を再度分類し直す必要がないため、変更されたファイルのみが考慮されます。 したがって、ファイル変更イベントに関する情報を受け取った場合、分類スクリプトは大いに役立ちます。



スクリプトを使用したセキュリティプラットフォームを使用してこのアプローチを実装しました。

LinuxまたはWindowsのファイルイベントをインターセプトするVaronis Native Agentは、高度に専門化された低レベルのコードです。 このタイプの作業には、短く、シンプルで、イベントに関する情報を収集し、このデータをより高いレベルの処理を既に実行する他のアプリケーションに迅速に転送することに重点を置いたコードが必要です。



したがって、元のイベント処理スクリプトを取得して最適化し、統計を表示するためにすべてのコードを削除しました。 次に、変更されたファイルのみをチェックするように分類子を修正しました。



基本的に、これは内部モジュールと外部インターフェイスの古典的な組み合わせです。

問題は、2つのスクリプトを接続する方法です。ファイルを使用した操作について分類子に通知する方法ですか。



メッセージとイベント



開発者向けフォーラムの調査に数日費やした後、ようやくRegister-EngineEventというPowerShell関数に出会いました。



このPowerShellコマンドレットとは何ですか?



これは、2つのスクリプトで共有できる名前付きイベントを使用してメッセージを送信する方法のようです。 受信したメッセージが非同期にPowerShellスクリプトブロックを起動するため、従来のシステムメッセージやキューとは少し異なります。 さらに、それはより明確になるでしょう。



実際、register-EngineEventには2つのオプションがあります。 -forwardパラメーターを使用すると、このコマンドレットはイベントパブリッシャーとして機能します。 -forwardパラメーターがない場合、レシーバーとして機能します。



いいですか



Deltaというイベント(技術的にはSourceIdentifer識別子の値として機能します)を使用して、イベントメッセージを送信するイベント処理スクリプトと、これらのメッセージを受信する分類子スクリプトの作業を調整しました。



これらの2つのスクリプトの最初の部分(後で説明する抜粋)では、-Register-EngineEvent -forward行を使用してDeltaイベントのパブリック名を登録し、次に内部ファイルアクセスイベントを予測する方法を示します。 このようなイベントが発生したとき、ファイルの内部イベントに関するメッセージを送信しました(PowerShellで転送しました)。2番目のコードスニペットの分類スクリプト内の対応するRegister-EngineEventコマンドレットに送信しました。



 1. Register-EngineEvent -SourceIdentifier Delta -Forward 2. While ($true) { 3. $args=Wait-Event -SourceIdentifier Access # wait on internal file event 4. Remove-Event -SourceIdentifier Access 5. if ($args.MessageData -eq "Access") { 6. #do some plain access processing 7. New-Event -SourceIdentifier Delta -EventArguments $args.SourceArgs -MessageData $args.MessageData #send event to classifier via forwarding 8. } 9. elseif ($args.MessageData -eq "Burst") { 10. #do some burst processing 11. New-Event -SourceIdentifier Delta -EventArguments $args.SourceArgs -MessageData $args.MessageData #send event to classifier via forwarding 12. } 13. }
      
      





受信側では、-forwardオプションを使用せず、代わりにイベントを非同期的に処理するPowerShellスクリプトブロックに切り替えました。 以下の結果を見ることができます。



 1. Register-EngineEvent -SourceIdentifier Delta -Action { 2. 3. Remove-Event -SourceIdentifier Delta 4. if($event.MessageData -eq "Access") { 5. $filename = $args[0] #got file! 6. Lock-Object $deltafile.SyncRoot{ $deltafile[$filename]=1} #lock&load 7. } 8. elseif ($event.Messagedata -eq "Burst") { 9. #do something 10. } 11. 12. }
      
      





混乱した? また、最近、ファイルイベントの処理は簡単ではなく、トレーニングスクリプトは実際のワークロードの処理に対応できないと述べました。

内部イベントメッセージング用のNew-EventおよびWait-Eventコマンドレットは、Register-EngineEventで提供される外部イベントメッセージング機能とは異なるため、混乱が生じます。



さらなる混乱



完全な分類スクリプトを以下に示します。 このシリーズの次と最後のメモで、彼について詳しく説明します。 それまでの間、このスクリプトを見て、イベント処理とマルチタスクに注意を払ってください。



 1. Import-Module -Name .\pslock.psm1 -Verbose 2. function updatecnts { 3. Param ( 4. [parameter(position=1)] 5. $match, 6. [parameter(position=2)] 7. $obj 8. ) 9. 10. for($j=0; $j -lt $match.Count;$j=$j+2) { 11. switch -wildcard ($match[$j]) { 12. 'Top*' { $obj| Add-Member -Force -type NoteProperty -Name Secret -Value $match[$j+1] } 13. 'Sens*' { $obj| Add-Member -Force -type NoteProperty -Name Sensitive -Value $match[$j+1] } 14. 'Numb*' { $obj| Add-Member -Force -type NoteProperty -Name Numbers -Value $match[$j+1] } 15. } 16. 17. } 18. 19. return $obj 20. } 21. 22. $scan = { 23. $name=$args[0] 24. function scan { 25. Param ( 26. [parameter(position=1)] 27. [string] $Name 28. ) 29. $classify =@{"Top Secret"=[regex]'[tT]op [sS]ecret'; "Sensitive"=[regex]'([Cc]onfidential)|([sS]nowflake)'; "Numbers"=[regex]'[0-9]{3}-[0-9]{2}-[0-9]{3}' } 30. 31. $data = Get-Content $Name 32. 33. $cnts= @() 34. 35. if($data.Length -eq 0) { return $cnts} 36. 37. foreach ($key in $classify.Keys) { 38. 39. $m=$classify[$key].matches($data) 40. 41. if($m.Count -gt 0) { 42. $cnts+= @($key,$m.Count) 43. } 44. } 45. $cnts 46. } 47. scan $name 48. } 49. 50. 51. $outarray = @() #where I keep classification stats 52. $deltafile = [hashtable]::Synchronized(@{}) #hold file events for master loop 53. 54. $list=Get-WmiObject -Query "SELECT * From CIM_DataFile where Path = '\\Users\\bob\\' and Drive = 'C:' and (Extension = 'txt' or Extension = 'doc' or Extension = 'rtf')" 55. 56. 57. #long list --let's multithread 58. 59. #runspace 60. $RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,5) 61. $RunspacePool.Open() 62. $Tasks = @() 63. 64. 65. foreach ($item in $list) { 66. 67. $Task = [powershell]::Create().AddScript($scan).AddArgument($item.Name) 68. $Task.RunspacePool = $RunspacePool 69. 70. $status= $Task.BeginInvoke() 71. $Tasks += @($status,$Task,$item.Name) 72. } 73. 74. 75. Register-EngineEvent -SourceIdentifier Delta -Action { 76. 77. Remove-Event -SourceIdentifier Delta 78. if($event.MessageData -eq "Access") { 79. $filename = $args[0] #got file 80. Lock-Object $deltafile.SyncRoot{ $deltafile[$filename]=1} #lock& load 81. } 82. elseif ($event.Messagedata -eq "Burst") { 83. #do something 84. } 85. } 86. 87. while ($Tasks.isCompleted -contains $false){ 88. 89. } 90. 91. #check results of tasks 92. for ($i=0; $i -lt $Tasks.Count; $i=$i+3){ 93. $match=$Tasks[$i+1].EndInvoke($Tasks[$i]) 94. 95. 96. if ($match.Count -gt 0) { # update clasafication array 97. $obj = New-Object System.Object 98. $obj | Add-Member -type NoteProperty -Name File -Value $Tasks[$i+2] 99. #defaults 100. $obj| Add-Member -type NoteProperty -Name Secret -Value 0 101. $obj| Add-Member -type NoteProperty -Name Sensitive -Value 0 102. $obj| Add-Member -type NoteProperty -Name Numbers -Value 0 103. 104. $obj=updatecnts $match $obj 105. $outarray += $obj 106. } 107. $Tasks[$i+1].Dispose() 108. 109. } 110. 111. $outarray | Out-GridView -Title "Content Classification" #display 112. 113. #run event handler as a separate job 114. Start-Job -Name EventHandler -ScriptBlock({C:\Users\bob\Documents\evhandler.ps1}) #run event handler in background 115. 116. 117. while ($true) { #the master executive loop 118. 119. 120. Start-Sleep -seconds 10 121. Lock-Object $deltafile.SyncRoot { #lock and iterate through synchronized list 122. foreach ($key in $deltafile.Keys) { 123. 124. $filename=$key 125. 126. if($deltafile[$key] -eq 0) { continue} #nothing new 127. 128. $deltafile[$key]=0 129. $match = & $scan $filename #run scriptblock 130. #incremental part 131. 132. $found=$false 133. $class=$false 134. if($match.Count -gt 0) 135. {$class =$true} #found sensitive data 136. if($outarray.File -contains $filename) 137. {$found = $true} #already in the array 138. if (!$found -and !$class){continue} 139. 140. #let's add/update 141. if (!$found) { 142. 143. $obj = New-Object System.Object 144. $obj | Add-Member -type NoteProperty -Name File -Value $Tasks[$i+2] 145. #defaults 146. $obj| Add-Member -type NoteProperty -Name Secret -Value 0 147. $obj| Add-Member -type NoteProperty -Name Sensitive -Value 0 148. $obj| Add-Member -type NoteProperty -Name Numbers -Value 0 149. 150. $obj=updatecnts $match $obj 151. 152. } 153. else { 154. $outarray|? {$_.File -eq $filename} | % { updatecnts $match $_} 155. } 156. $outarray | Out-GridView -Title "Content Classification ( $(get-date -format M/d/yy:HH:MM) )" 157. 158. } #foreach 159. 160. } #lock 161. }#while 162. 163. Write-Host "Done!"
      
      





つまり、この分類子は、フォルダー内のファイルの初期スキャンを実行し、分類結果を$ outarrayに保存し、ファイル変更イベントが発生すると、$ outarrayを更新し、新しい分類データを追加します。 つまり、データ追加システムが実装されています。



$ outarrayの更新を処理する必要があるという小さな問題があります。これは、分類スクリプトの別の部分で、このハッシュテーブル変数で変更されたものを検索するたびに発生します。



これは古典的な試合の状況です。 この状況に対処するため、PowerShellの同期変数を使用することにしました。



この神秘的なPowerShell関数の詳細については、次の記事で説明し、結論として、独自のソリューションを作成する方法についていくつかの言葉を述べます。



All Articles