⬆️ ⬇️

PowerShell for IT security. Part IV: Script Security Platform





In the previous post of this series, I suggested the possibility of combining my separate scripts — one for handling events, the other for classification — into one system. Is it possible to wipe the security platform on the basis of a single PowerShell code?



After working through some of the details, mainly related to the dreadful events of PowerShell, I was able to declare my victory and registered a patent for a security platform based on scripts - Security Scripting Platform (SSP).



United States PowerShell

')

While I was getting an unforgettable experience with PowerShell, I realized that some of you may not remember my results of working with scripts.

Let's remember them together.



In the first note, I presented an amazing line of PowerShell code that monitors file access events and runs a script block, similar to PS, that is, script code that runs in its own memory region.



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 


Having added some elements of the script, I received the code, which I called the file access analysis application. In fact, it counts access events and displays some basic statistical data, and also identifies access bursts that may indicate hacking attempts.



This is a simplified version of the technology for analyzing user behavior, which is widely used here in the company Varonis.



So far, so good.



Then in the third note, I showed how using PowerShell you can relatively simply scan and categorize the files in a folder. Since this action is associated with disk-intensive use, it makes sense to use the multitasking feature of PowerShell, known as Runspaces, to speed up the classification.



Existing solutions for file event handling and data classification, say Varonis's Data Classification Framework, use a more optimized approach to categorizing files by transferring file operations to the classification module.



Why?



Because they do not need to re-classify the contents of the files again: only the files that are changed are considered. Therefore, my script classifier will greatly benefit if it receives some information about file change events.



I implemented this approach using a security platform using scripts.

Varonis proprietary agents that intercept Linux or Windows file events are highly specialized, low-level code. This type of operation requires code that is short, simple, and fully focused on gathering information about events and quickly transferring this data to other applications that will already handle higher-level processing.



So I took my original event handling script, optimized it and deleted all the code to display statistical data. Then I reworked the classifier to check only the modified files.



Basically, this is a classic combination of the internal module and the external interface.

The question is how to connect two scripts: how to inform the classifier about the operation with the file?



Messages and events



After I spent several long days studying the developer forums, I finally came across a PowerShell function called Register-EngineEvent.



What is this PowerShell cmdlet?



It seems to me that this is a way to send messages using a named event that can be shared in two scripts. It works slightly differently from traditional system messages and queues, since the received message asynchronously starts the PowerShell script block. Further it will become more understandable.



The fact is that register-EngineEvent has two options. With the -forward option, this cmdlet works as an event publisher. Without the -forward option, it acts as a recipient.



Clear?



I used an event called Delta — which technically plays the role of the SourceIdentifier ID — to coordinate my event-handling script, which reports events, and my classifier script, which receives these messages.



In the first of these two scripts, an excerpt from which is presented below, I show how I registered the public name of the Delta event using the line -Register-EngineEvent -forward, and then waited for the internal file access events. When such an event occurred, I sent a message about the internal file event — in PowerShell, I sent it to the corresponding Register-EngineEvent cmdlet in the classifier script in the second code snippet.



 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. } 


On the recipient side, I didn’t use the -forward parameter and instead switched to a PowerShell script block that asynchronously processed the event. You can see the result below.



 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. } 


Confused? And I said recently that handling event files is not easy, and that my training scripts will not cope with handling real production loads?

Confusion arises because the New-Event and Wait-Event cmdlets for internal event messaging are different from the external event messaging functions provided in Register-EngineEvent.



More confusion



The full classification script is presented below. I will tell you more about him in the next and last article in this series. In the meantime, take a look at this script and note the event handling and multitasking.



 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!" 


In short, this classifier performs an initial scan of files in a folder, saves the classification results to $ outarray, then, when a file change event occurs, it updates $ outarray, entering new classification data into it. In other words, a system for adding data is implemented.



There is a minor side problem with the need to work with updates in $ outarray, which occurs every time a search is made in a different part of the classification script for what has been changed in this hash table variable.



This is a classic match. And to handle this situation, I decided to use synchronized PowerShell variables.



I will tell you more about this mysterious PowerShell function in my next post, and in conclusion I will say a few words about how to make your own solutions.

Source: https://habr.com/ru/post/338848/



All Articles