Tag Archives: penetration testing

Practical PowerShell for IT Security, Part V: Security Scripting Platform ...

Practical PowerShell for IT Security, Part V: Security Scripting Platform Gets a Makeover

A few months ago, I began a mission to prove that PowerShell can be used as a security monitoring tool. I left off with this post, which had PowerShell code to collect file system events, perform some basic analysis, and then present the results in graphical format. My Security Scripting Platform (SSP) may not be a minimally viable product, but it was, I think, useful as simple monitoring tool for a single file directory.

After finishing the project, I knew there were areas for improvement. The event handling was clunky, the passing of information between various parts of the SSP platform was anything but straightforward, and the information being displayed using the very primitive Out-GridViewwas really a glorified table.

New and Improved

I took up the challenge of making SSP a bit more viable. My first task was to streamline event handling. I had initially worked it out so that file event messages were picked up by a handler in my Register-EngineEvent scriptblock and sent to an internal queue and then finally forwarded to the main piece of code, the classification software.

I regained my sanity, and realized I could just directly forward the messages with Register-EngineEvent -forward from within the event handling scriptblock, removing an unnecessary layer of queuing craziness.

You can see the meaner, leaner version below.

#Count events, detect bursts, forward to main interface

$cur = Get-Date
$Global:baseline = @{"Monday" = @(1,1,1); "Tuesday" = @(1,.5,1);"Wednesday" = @(4,4,4);"Thursday" = @(7,12,4); "Friday" = @(5,4,6); "Saturday"=@(2,1,1); "Sunday"= @(2,4,2)}
$Global:cnts =     @(0,0,0)
$Global:burst =    $false
$Global:evarray =  New-Object System.Collections.ArrayList

$action = { 
    $i= [math]::floor((Get-Date).Hour/8) 


   #event auditing!
   $rawtime =  $EventArgs.NewEvent.TargetInstance.LastAccessed.Substring(8,6)
   $filename = $EventArgs.NewEvent.TargetInstance.Name
   $etime= [datetime]::ParseExact($rawtime,"HHmmss",$null)

   $msg="$($etime)): Access of file $($filename)"
   $msg|Out-File C:\Users\Administrator\Documents\events.log -Append
   New-Event -SourceIdentifier Delta -MessageData "Access" -EventArguments $filename  #notify 
   if(!$Global:burst) {
   else { 
     if($Global:start.AddMinutes(15) -gt $etime ) { 
        #File behavior analytics
        $sfactor=2*[math]::sqrt( $Global:baseline["$($d)"][$i])
        if ($Global:Count -gt $Global:baseline["$($d)"][$i] + 2*$sfactor) {  #at 95% level of poisson
          "$($etime): Burst of $($Global:Count) accesses"| Out-File C:\Users\Administrator\Documents\events.log -Append 
          $Global:burst =$false
          New-Event -SourceIdentifier Delta -MessageData "Burst" -EventArguments $Global:evarray #notify on burst
          $Global:evarray= [System.Collections.ArrayList] @()
     else { $Global:burst =$false; $Global:Count=0; $Global:evarray= [System.Collections.ArrayList]  @()}

Register-EngineEvent -SourceIdentifier Delta -Forward 
Register-WmiEvent -Query "SELECT * FROM __InstanceModificationEvent WITHIN 5 WHERE TargetInstance ISA 'CIM_DataFile' and TargetInstance.Path = '\\Users\\Administrator\\' and targetInstance.Drive = 'C:' and (targetInstance.Extension = 'txt' or targetInstance.Extension = 'doc' or targetInstance.Extension = 'rtf') and targetInstance.LastAccessed > '$($cur)' " -sourceIdentifier "Accessor" -Action $action   
Write-Host "starting engine ..."

while ($true) {

   Wait-Event -SourceIdentifier Access # just hang on this so I don't exit    


Then I took on the main piece of code, where I classify files based on whether they have social security numbers and other sensitive keywords. As events come in from the handler, the file reclassification is triggered. This code then periodically displays some of the results of the classification.

In this latest version, I removed the “real-time” classification and focused on cleaning up the PowerShell code and improving the graphics — more on that below.

I took a wrong turn in the original version by relying on a PowerShell data locking module to synchronize data access from concurrent tasks, which I used for some of the grunt work. On further testing, the freebie module that implements the Lock-Object cmdlet didn’t seem to work.

As every junior system programmer knows, it’s easier to synchronize with messages than with low-level locks. I reworked the code to take the messages from the event handler above, and send them directly to a main message processing loop. In short: I was able to deal with asynchronous events in a synchronous manner.

.Net Framework Charts and PowerShell

My great discovery in the last month was that I could embed Microsoft-style charts inside PowerShell. In other word, the bar, line, scatter and other charts that are available in Excel and Word can be controlled programmatically in PowerShell. As a newbie PowerShell programmer, this was exciting to me. You can read more about .Net Framework Controls in this post.

It’s a great idea, and it meant I could also replace the messy Out-GridViewcode.

But the problem, I quickly learned, is that you also have to deal with some of the interactive programming involved with Microsoft forms. I just wanted to display my .Net charts while not having to code the low-level details. Is there a lazy way out?

After much struggle, I came to see that the easiest way to do this is to launch each chart in its own runspace as a separate task. (Nerd Note: this is how I avoided coding message handling for all the charts since each runs separately as modal dialogs.)

I also benefited from this freebie PowerShell module that wraps the messy .Net chart controls. Thanks Marius!

I already had set up a tasking system earlier to scan and classify each file in the directory I was monitoring, so it was just a matter of reusing this tasking code to launch graphs.

I created a pie chart for showing relative concentration of sensitive data, a bar chart for a breakdown of files by sensitive data types, and, the one I’m most proud is a classic event stair-step chart for file access burst conditions — a possible sign of an attack.

My amazing dashboard. Not bad for PowerShell with .Net charts.

For those who are curious about the main chunk of code doing all the work of my SSP, here it is for your entertainment:

$scan = {  #file content scanner
function scan {
   Param (
      [string] $Name
      $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}' }
      $data = Get-Content $Name
      $cnts= @()
      if($data.Length -eq 0) { return $cnts} 
      foreach ($key in $classify.Keys) {
        if($m.Count -gt 0) {
           $cnts+= @($key,$m.Count)  
scan $name

#launch a .net chart 
function nchart ($r, $d, $t,$g,$a) {

$task= {

Import-Module C:\Users\Administrator\Documents\charts.psm1
$chart = New-Chart -Dataset $d -Title $t -Type $g -Axis $a
Show-Chart $chart

$Task = [powershell]::Create().AddScript($task).AddArgument($d).AddArgument($t).AddArgument($g).AddArgument($a)
$Task.RunspacePool = $r


Register-EngineEvent -SourceIdentifier Delta -Action {
      if($event.MessageData -eq "Burst") { #just look at bursts
        New-Event -SourceIdentifier File -MessageData $event.MessageData -EventArguments $event.SourceArgs 
      Remove-Event -SourceIdentifier Delta

$list=Get-WmiObject -Query "SELECT * From CIM_DataFile where Path = '\\Users\\Administrator\\' and Drive = 'C:' and (Extension = 'txt' or Extension = 'doc' or Extension = 'rtf')"  

#long list --let's multithread

$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,5)
$Tasks = @()

foreach ($item in $list) {
  $Task = [powershell]::Create().AddScript($scan).AddArgument($item.Name)
  $Task.RunspacePool = $RunspacePool
  $status= $Task.BeginInvoke()
  $Tasks += @($status,$Task,$item.Name)

while ($Tasks.isCompleted -contains $false){

#Analytics, count number of sensitive content for each file
$obj = @{}

for ($i=0; $i -lt $Tasks.Count; $i=$i+3) {
   if ($match.Count -gt 0) {   
      $s = ([string]$Tasks[$i+2]).LastIndexOf("\")+1
       for( $j=0; $j -lt $match.Count; $j=$j+2) {      
         switch -wildcard ($match[$j]) {
             'Top*'  { $tdcnt+= 1 }
             'Sens*' { $sfcnt+= 1}                      
             'Numb*' { $nfcnt+=1} 

#Display Initial Dashboard
#Pie chart of sensitive files based on total counts of senstive dat
$piedata= @{}
foreach ( $key in $obj.Keys) {
   $senscnt =0
   for($k=1; $k -lt $obj[$key].Count;$k=$k+2) {
     $senscnt+= $obj[$key][$k]

   $piedata.Add($key, $senscnt) 


nchart $RunspacePool $piedata "Files with Sensitive Content" "Pie" $false

#Bar Chart of Total Files, Sensitive  vs Total
$bardata = @{"Total Files" = $Tasks.Count}
$bardata.Add("Files w. Top Secret",$tdcnt)
$bardata.Add("Files w. Sensitive", $sfcnt)
$bardata.Add("Files w. SS Numbers",$nfcnt)

nchart $RunspacePool $bardata "Sensitive Files" "Bar" $false

#run event handler as a seperate job
Start-Job -Name EventHandler -ScriptBlock({C:\Users\Administrator\Documents\evhandler.ps1})

while ($true) { #main message handling loop
       [System.Management.Automation.PSEventArgs] $args = Wait-Event -SourceIdentifier File  # wait on event
        Remove-Event -SourceIdentifier File
        #Write-Host $args.SourceArgs      
        if ($args.MessageData -eq "Burst") {
        #Display Bursty event
         #time in seconds
         [datetime]$sevent =$dt[0][1]
         $xyarray = [ordered]@{}
         for($j=1;$j -lt $dt.Count;$j=$j+1) {
               [timespan]$diff = $dt[$j][1] - $sevent
          nchart $RunspacePool $xyarray "Burst Event" "StepLine" $true 

Write-Host "Done!"


Lessons Learned

Of course, with any mission the point is the journey not the actual goal, right? The key thing I learned is that you can use PowerShell to do security monitoring. For a single directory, on a small system. And only using it sparingly.

While I plan on improving what I just presented by adding real-time graphics, I’m under no illusion that my final software would be anything more than a toy project.

File event monitoring, analysis, and graphical display of information for an entire system is very, very hard to do on your own. You can, perhaps, recode my solution using C++, but you’ll still have to deal with the lags and hiccups of processing low-level events in the application space. To do this right, you need to have hooks deep in the OS — for starters — and then do far more serious analysis of file events than is performed in my primitive analytics code. That ain’t easy!

I usually end up these DIY posts by saying “you know where this is going.” I won’t disappoint you.

You know where this is going. Our own enterprise-class solution is a true data security platform or DSP – it handles classification, analytics, threat detection, and more for entire IT systems.

By all means, try to roll your own, perhaps based on this project, to learn the difficulties and appreciate what a DSP is actually doing.

Have questions? Feel free to contact us!

Next Steps

If you’re interested in learning more practical, security focused PowerShell, you can unlock the full 3 hour video course on PowerShell and Active Directory Essentials with the code cmdlet.

Working With Windows Local Administrator Accounts, Part III

Working With Windows Local Administrator Accounts, Part III

This article is part of the series "Working With Windows Local Administrator Accounts". Check out the rest:

One point to keep in mind in this series is that we’re trying to limit the powers that are inherent in Administrator accounts. In short: use the Force sparingly. In the last post, we showed it’s possible to remove the local Administrator account and manage it centrally with GPOs. Let’s go over a few things I glossed over last time, and discuss additional ways to secure these accounts.

Restricted Groups: Handle with Care

In my Acme environment, the Restricted Groups GPO is used to push out a domain-level group to the local Administrators group in each of the OUs: one policy for Masa and Pimiento, another for Taco. It’s a neat trick, and for larger domains, it saves IT from having to do this through scripts or spending time performing this manually.

To refresh memories, here’s how my GPO for Restricted Groups looked:

Replaces local  Administrators groups with Acme-IT-1.

By using the “Member of this group” section, I’m forcing the Group Policy Manager to replace, not add, Acme-IT-1 to each local Administrators group in my OU. The problem is you may overwrite existing group members, and you don’t know what services or apps depend on certain local accounts being there.

You’ll likely want to evaluate this idea out on a small sample. This may involve more work— local scripts to re-add those accounts, or possibly creating new domain level accounts that can be added into the above.

Or if you prefer, you can use Group Policy Preferences (GPP). It has an update option for adding a new group (or user) under a local Administrator account (below). We know not to use GPP to reset local Administrator account passwords, right?

With GPP, you can add Acme-IT-2 to the local Administrators groups.

Even More Secure

There is, sigh, a problem in using Restricted Groups and centrally managed domain-level Administrator accounts. Since all users by default,  are under Domain Users, it means that local Administrators can be exploited through Pass-the-Hash (PtH) techniques — get NTLM hash, and pass to psexec — to log on to any other machine in the network.

This was the conundrum we were trying to grapple with in the first place! Recall: local Administrators are usually given simple — easily guessable or hackable — passwords which can then be leveraged to log on to other machines. We wanted to avoid having an Administrator-level local account that can be potentially used globally.

As I mentioned in the second post, this security hole can be addressed by creating a GPO – under User Rights Assignment — to restrict network access all together. This may not be practical in all cases for Administrators accounts.

Another possibility is to limit the machines that these domain-level Administrator accounts can log into. And again we make a lifeline call to User Rights Assignment, but this time enlisting the “Allows log on locally” property, adding the Acme-IT-1 Administrators group (below). We would do the same for the other OU in the Acme domain, but adding the Acme-IT-2 group.

This GPO prevents accounts from logging on to machines outside the specified domain. So even if a clever hacker gets into the Acme company, he could PtH with Administrator account but only within the OU.

It’s a reasonable solution. And I do realize that many companies likely already use this GPO property for ordinary user accounts, just for reasons I noted above.

Additional Thoughts

In writing this brief series, you quickly come to the conclusion that zillions of IT folks already know in their bones: you’re always trying to balance security against convenience. You won’t have a perfect solution, and you’ve probably erred on the side of convenience (to avoid getting shouted at by the user community).

Of course, you live with what you have. But then you should compensate for potential security holes by stepping up your monitoring game! You know where this is going.

One final caveat goes back to my amazing pen testing series where I showed how delegated Administrator groups can be leveraged to allow hacker to hop more freely around a domain—this has to do with accounts being in more than one Active Directory group. Take another look at it!

Working With Windows Local Administrator Accounts, Part II

Working With Windows Local Administrator Accounts, Part II

This article is part of the series "Working With Windows Local Administrator Accounts". Check out the rest:

Before we delve into Restricted Groups, I thought it might be worthwhile to take a closer look at how hackers take advantage of Administrator passwords. For Pass-the-Hash fans, this post will show you how hashes can be used even with local accounts. I also had a chance to try Windows Local Administrator Passwords Solution or LAPS. Spoiler alert: LAPS scares me a little.

Passing Local Hashes

After writing the first post, I realized that you don’t necessarily need hashes of domain accounts. In fact, Windows also stores the hashes of local accounts in its Security Accounts Manager (SAM) database. Hash dumping tools such as crackmapexec and mimikatz let you view these hashes.

This leads to a more direct lateral movement tactic. As I pointed out last time, it is not unusual for local Administrator accounts to have exactly the same password on more than one machine. This would also mean the NTLM hashes would be the same as well.

Let’s say a hacker gains access to a server, and assuming he has enough privileges, then uses mimikatz to see if a local Administrator account is available. He can then try an experiment and pass the Administrator hash into, say, psexec to pop a shell on another server and gain Administrator privileges there as well.

You see what I’m getting at?  If you assume that Administrator passwords are the same on different machines, then you’re no longer dependent on a domain-level user to have left a hash in the LSASS memory of that box. This post explains more about LSASS if you’re confused by the last sentence.

On the other hand, the local user hashes are always there! Being a hacker or pen tester means that you’re always testing different ideas and playing the odds. So let’s go for broke!

Back in my Acme domain, I set the same local Administrator password on both my Masa and Taco servers – Taco is also my domain controller. In this scenario, I’m already on Masa, I’ve uploaded mimikatz and psexec.

By the way, both these tools have source code, so it wouldn’t be that difficult to make them fully undetectable after a few tweaks.

I was now flying under the radar on Masa, but couldn’t find anything interesting there. To begin my lateral move, I loaded mimikatz and dumped the hashes with the lsadump::samcommand.

Assuming that Taco also has the same Administrator password, I then use sekurlsa:pth to launch psexec and gain a shell on Taco (below).

Just try passing-the-hash with the local Administrator account. What do you have to lose?


When I changed the Taco Administrator’s password, this ploy didn’t work, and psexec was unable to pop a shell.

Lesson learned: it’s good idea to have different Administrator passwords.

LAPS and Aspirin

If you’re going to keep the local Administrator passwords, then you need to manage them. As I wrote about last time, these accounts can be disabled, and Restricted Groups can be used to delegate Administrator privileges to domain-level accounts.

In any case, people still want these local accounts. Microsoft apparently heard the collective cry of IT administrators, and in 2015 they released their Local Administrator Passwords Solution. It’s described with these words: “…solution to the issue of using a common local account with an identical password on every computer in a domain. LAPS resolves this issue by setting a different, random password for the common local administrator account on every computer in the domain.”

Seems simple. However as we’ve noted before,  Microsoft never, ever does anything nice and easy.

The first tip off was the LAPS architecture (see below).

Plans for the invasion of Mars.

Hmm, there is a client and server side to this. The documentation also indicates PowerShell scripts have to be run, and then there’s something about changing the Active Directory schema.

I boldly took the LAPS challenge and went as far as I could with the installation before the pounding in my head got to me.

This is not an easy install. LAPS is loaded onto your domain controller as well as on client computers that you want managed. Yeah, you use the Group Management Console to push out LAPS to the clients.

If you do the installation correctly, you’ll see the following interface pop up when you navigate in the GPO editor to Computer Configuration>Administrative Templates>LAPS.

I was afraid to pull the trigger on this. In theory, LAPS generates random passwords that are now centrally located on Active Directory in a new attribute as plaintext — that’s why you needed to update the AD schema.

Some security pros have pointed out that LAPS may, ahem, have its own problems. Essentially, you’re shifting the problem from local computers to Active Directory.

Back to Restricted Groups

After returning from my LAPS detour, I began to see Restricted Groups as the most practical way to manage local Administrator accounts. I started on this process in the previous post when I created a new AD group called Acme-IT, which then was pushed out and placed under the local Administrators group for each machine in the Acme domain

It’s a neat trick, and Restricted Groups allow IT to centrally control local Administrator access.

It would even be neater if I could segment my domain so that one group of users would be local Administrators for a subset of machines, and another group would control a different subset –creating as many sub-groupings as needed.

Otherwise, I’d fall into the trap of allowing a small group of users to have local Administrator access to the entire domain! No bueno.

And that’s where Organizational Units (OUs) come into play. It’s a way to divide up the domain so that you can associate specific GPOs with each OU subgroup.

You first set up these OU sub-divisions in Active Directory Users and Computer (below). For each OU, I assigned a subset of the domain’s computers. In my scenario, Acme-1 is associated with the Masa and Pimiento servers, and Acme-2 is associated with Taco, the domain controller.

Two new OUs join the Acme domain: Acme-1 and Acme-2.

I also had to remember to create Active Directory groups that will be associated with each of these OUs — Acme-IT-1 and  Acme-IT-2.

Now when I’m back in the Group Management Console, these OUs show up under the Acme domain (below). I added a Restricted Groups policy under each OU, making sure that the appropriate AD groups were used.

The OU payoff: segmented GPO policies!

It’s simpler than it sounds. In short: I’m enabling members of Acme-IT-1 to be an Administrator for Masa and Pimiento, and Acme-IT-2 members for Taco.

We’ll finish up this incredibly exciting topic in the next post and, as always, I’ll have a few closing thoughts. In the meantime, take a few aspirins for getting this far in the series.


Continue reading the next post in "Working With Windows Local Administrator Accounts"

Working With Windows Local Administrator Accounts, Part I

Working With Windows Local Administrator Accounts, Part I

This article is part of the series "Working With Windows Local Administrator Accounts". Check out the rest:

In writing about hackers and their techniques, the issue of Windows local Administrator accounts often comes up. Prior to Windows 7, the Administrator account was created by default with no password. This was not a good security practice, and hackers have been taking advantage ever since.

Starting in Windows 7, the local Administrator accounts were disabled by default. And you should disable them in your domain regardless of which Windows OS you have! But for various reasons, some of them based on mistaken ideas, the local Administrator accounts hang around on many installations. They’ve too often been given easily guessable passwords by IT, and sometimes the same password across large parts of the domain.

If hackers can get a foothold on the system, they’ll look for this privileged local Administrator account as part of their evil checklist. They’ll then try to use these accounts as they start the lateral movement phase of their post-exploitation.

In short: they guess the local Administrator’s account password, grab the hashes of domain-level users with mimikatz, and then move around the network.

But I Need It!

Or they may not even have to grab hashes of domain-level users. In many environments, the local Administrator account passwords themselves are set using the same pattern: once you guess one, you can figure out the whole shebang.

In the real world, you may have to deal with IT staff arguing the Administrator accounts need to be available — perhaps because processes or software is dependent on their existence.

Is there a way to keep the Administrator accounts but lessen their power?

Microsoft has a few recommendations on how to secure them. Essentially, you configure a Group Policy Object (GPO) to disable network access, remote desktop, and a few other services through User Rights Assignment.

Enabling “deny access to this computer from the network” in the User Rights Assignment GPO.

Just to see this how would work, I went back to my now famous Acme domain that I set up up in Amazon Web Services.

Let’s assume passwords for Acme’s local Administrator accounts are based on a pattern — the server name followed by a numeric sequence — as a memory convenience for the beleaguered IT staff.

In my pretend scenario, I used the vanilla psexec utility — which by the way played a part in the recent NotPetya ransomware outbreak — to laterally move to the Taco server. Putting on my pen-testing hat, I entered my guess for the Administrator password to Taco, and miraculously it worked.

You can see the results of my psexec activities below before I set the “deny network access” GPO.

Taking advantage of silly Administrator passwords with psexec.

After I set the GPO, the psexec utility complained that my user name or password was invalid — User Rights Assignment was doing its job. If you’re trying this at home, remember to run gpupdate /forceon the domain controller to trigger the GPOs to sync to all the domain members.

This is a compromise solution that lets you keep the local Administrator accounts but prevents hackers from easily exploiting them to move around the network.

Don’t Change Passwords With Group Policy Preferences

The right thing to do is to disable the local Administrator, and then set up domain-level groups with restricted privileges under the local Administrators group. We’ll start discussing how to do this further below.

But let’s say your heart is set on keeping these local Administrator accounts; you realize the passwords were set for convenience (and not for security);  and you now want to improve the passwords on a global level.

Once upon a time, Microsoft introduced Group Policy Preferences (GPP) in Windows server 2008 to extend GPP. One of the new GPP capabilities allowed you to update user and group information on a domain basis.  At one point, you were allowed to change passwords and then push out the updated passwords across the domain

However, hackers found a major vulnerability in GPP’s  password distribution process. While Windows encrypted the password, they released — duh! — the encryption key in their technical documentation.

They’ve since put out a patch for it, and in the AWS instance I was most recently working with, the password entry was disabled — it’s grayed out in the graphic below.

In my AWS environment, the GPP feature for updating passwords is disabled. You may not be so lucky.

However, early on in my experimenting I launched an unpatched AWS instance, where I was able to enter the password. And sure enough I could pull up the AES-encrypted password from the SYSVOL folder.

And there are no doubt many more servers out in this world without the GPP patch. In fact, pen testing utilities such as crackmapexec search for encrypted passwords — see the -gpp-passwords option — in the SYSVOL folder and then conveniently decrypts them.

Thankfully, Microsoft came out with another way to do bulk updates of Administrator passwords, giving it the catchy name of Local Administrator Password Solution, or LAPS. You can download it here. I plan on giving it a try, and I’ll let you know the results.

Restricted Groups

The better way to handle local Administrator accounts is through the Restricted Groups GPO, found under Computer Configuration > Policies > Windows Settings> Security Settings. This GPO manages the local Administrators group by letting you add a domain-level group under it and then pushing the changes out across the domain. In short: you have a special group of users that’s centrally controlled with just the privileges to do the local administrative work they need.

For my Acme domain, I delegated local Administrator powers to the Acme-IT group (below).

Restricted Groups! Control local Administrators across the domain in one swoop.

There are additional subtleties in working with Restricted Groups that we’ll get to next time. And we’ll also take up Organizational Units or OUs, which gives us the power to segment the domain and improve the security of our Restricted Groups.


Continue reading the next post in "Working With Windows Local Administrator Accounts"

Disabling PowerShell and Other Malware Nuisances, Part II

Disabling PowerShell and Other Malware Nuisances, Part II

This article is part of the series "Disabling PowerShell and Other Malware Nuisances". Check out the rest:

Whitelisting apps is nobody’s idea of fun. You need to start with a blank slate, and then carefully add back apps you know to be essential and non-threatening. That’s the the idea behind what we started to do with Software Restriction Policies (SRP) from last time.

As you’ll recall, we ‘cleared the board’ though the default disabling of app execution in the Property Rules. In the Additional Rules section, I then started adding Path rules for apps I though were essential.

The only apps you’ll ever need!

Obviously, this can get a little tedious, so Microsoft helpfully provides two default rules: one to enable execution of apps in the Program folder and the other to enable executables in the Windows system directory.

But this is cheating and then you’d be force then to blacklist apps you don’t really need.

Anyway, when a user runs an unapproved app or a hacker tries to load some malware that’s not in the whitelist, SRP will prevent it.  Here’s what happened when I tried to launch PowerShell, which wasn’t in my whitelist, from the old-style cmd shell, which was in the list:

Damn you Software Restriction Policies!

100% Pure Security

To be ideologically pure, you wouldn’t use the default Windows SRP rules. Instead, you need to start from scratch with bupkes and do the grunt work of finding out what apps are being used and that are truly needed.

To help you get over this hurdle, Microsoft suggests in a Technet article that you turn on a logging feature that writes out an entry whenever SRP evaluates an app.  You’ll need to enable the following registry entry and set a log file location:


String Value: LogFileName, <path to a log file>

Here’s a part of this log file from my AWS test environment.

Log file produced by SRP.

So you have to review the log, question users, and talk it over with your fellow IT admins. Let’s say you work out a list of approved apps (excluding PowerShell), which you believe would make sense for a large part of your user community. You can then leverage the Group Policy Management console to publish the rules to the domain.

In theory, I should be able to drag-and-drop the rules I created in the Group Policy Editor for the machine I was working into the Management console. I wasn’t able to pull that off in my AWS environment.

I instead  had to recreate the rules directly in the Group Policy Management Policy Editor (below), and then let it do the work of distributing it across the domain — in my case, the Acme domain.


You can read more about how to do this here.

A Look at AppLocker

Let’s get back to the issue of PowerShell. We can’t live without it, yet hackers have used it as tool for stealthy post-exploitation.

If I enable it in my whitelist, along with some of the built-in PowerShell protections I mentioned in the last post, there are still so many ways to get around these security precautions that it’s not worth the trouble.

It would be nice if SRP allows you to do the whitelisting selectively based on Active Directory user or group membership. In other words, effectively turn off PowerShell except if you’re, say, an IT admin that’s a member of the ‘Special PowerShell’ AD group.

That ain’t happening in SRP since it doesn’t support this level of granularity!

Starting in Windows 7 (and Windows Server 2008), Microsoft deprecated SPR and introduced the (arguably) more powerful AppLocker. It’s very similar to the what it replaces, but it does provide this user-group level filtering.

We’ll talk more about AppLocker and some of its benefits in the final post in this series. In any case, you can find this policy next to SRP in the Group Policy Editor under Application Control Policies.

For my Acme environment, I set up a rule that enables PowerShell for only users in the Acme-VIP group, Acme’s small group of power IT employees. You can see how I started setting this up as I follow the AppLocker wizard dialog:

PowerShell is an important and useful tool, so you’ll need to weigh the risks in selectively enabling it through AppLocker— dare I say it, perform a risk assessment.

Of course, you should have secondary controls, such as, ahem, User Behavior Analytics, that allows you to protect against PowerShell misuse should the credentials of the PowerShell-enabled group be compromised by hackers or insiders.

We’ll take up  other AppLocker capabilities and final thoughts on whitelisting in the next post.

Continue reading the next post in "Disabling PowerShell and Other Malware Nuisances"

Disabling PowerShell and Other Malware Nuisances, Part I

Disabling PowerShell and Other Malware Nuisances, Part I

This article is part of the series "Disabling PowerShell and Other Malware Nuisances". Check out the rest:

Back in more innocent times, circa 2015, we began to hear about hackers going malware-free and “living off the land.” They used whatever garden-variety IT tools were lying around on the target site. It’s the ideal way to do post-exploitation without tripping any alarms.

This approach has taken off and gone mainstream, primarily because of off-the-shelf post-exploitation environments like PowerShell Empire.

I’ve already written about how PowerShell, when supplemented with PowerView, becomes a potent purveyor of information for hackers. (In fact, all this PowerShell pen-testing wisdom is available in an incredible ebook that you should read as soon as possible.)

Any tool can be used for good or bad, so I’m not implying PowerShell was put on this earth to make the life of hackers easier.

But just as you wouldn’t leave a 28” heavy-duty cable cutter next to a padlock, you probably don’t want to allow, or at least make it much more difficult for, hackers get their hands on PowerShell.

This brings up a large topic in the cybersecurity world: restricting application access, which is known more commonly as whitelisting or blacklisting. The overall idea is for the operating system to strictly control what apps can be launched by users.

For example, as a member of homo blogus, I generally need some basic tools and apps (along with a warm place to sleep at night), and can live without PowerShell, netcat, psexec, and some other cross-over IT tools I’ve discussed in previous posts. The same applies to most employees in an organization, and so a smart IT person should be able come up with a list of apps that are safe to use.

In the Windows world, you can enforce rules on application execution using Software Restriction Policies and more recently AppLocker.

However, before we get into these more advanced ideas, let’s try two really simple solutions and then see what’s wrong with them.

ACLs and Other Simplicities

We often think of Windows ACLs as being used to control access to readable content. But they can also be applied to executables — that is, .exe, .vbs, .ps1, and the rest.

I went back into Amazon Web Services where the Windows domain for the mythical and now legendary Acme company resides and then did some ACL restriction work.

The PowerShell .exe, as any sys admin can tell you, lives in C:\Windows\System32\WindowsPowerShell\v1.0. I navigated to the folder, clicked on properties, and effectively limited execution of PowerShell to a few essential groups: Domain Admins and Acme-SnowFlakes, which is the group of Acme employee power users.

I logged backed into the server as Bob, my go to Acme employee, and tried to bring up PowerShell. You see the results below.

In practice, you could probably come up with a script — why not use PowerShell? — to automate this ACL setting for all the laptops and servers in a small- to mid-size site.

It’s not a bad solution.

If you don’t like the idea of setting ACLs in executable files, PowerShell offers its own execution restriction controls. As a user with admin privileges, you can use, what else but a PowerShell cmdlet called Set-ExecutionPolicy.

It’s not nearly as blunt a force as the ACLs, but you can restrict PowerShell to work only in interactive mode –  with the Restricted parameter — so that it won’t execute scripts that contain the hackers’ malware. PowerShell would still be available in a limited way, but it wouldn’t be capable of running the scripts containing hacker PS malware.

However, this would PowerShell scripts from being run by your IT staff.  To allow IT-approved scripts, but disable evil hacker scripts, you use the RemoteSigned parameter in Set-ExecutionPolicy. Now PowerShell will only launch signed scripts. The IT staff, of course, would need to create their own scripts and sign them using an approved credential.

I won’t go into the details how to do this, mostly because it’s so easy to get around these controls. Someone even has a listicle blog post in which 15 PowerShell  security workarounds are described.

The easiest one is using the Bypass parameter in PowerShell itself. Duh! (below).

Seems like a security hole, no?

So PowerShell has some basic security flaws. It’s somewhat understandable since it is, after all, just a shell program.

But even the ACL restriction approach has a fundamental problem.

If hackers loosen up the “live off the land” philosophy, they can simply download — say, using a remote access trojan (RAT) — their own copy of PowerShell .exe. And then run it directly, avoiding the permission restrictions with the resident PowerShell.

Software Restriction Policies

These basic security holes (and many others) are always an issue with a consumer-grade operating systems. This has led OS researchers to come up with secure secure operating systems that have direct power to control what can be run.

In the Windows world, these powers are known as Software Restriction Policies (SRP) — for a good overview, see this — that are managed through the Group Policy Editor.

With SRP you can control which apps can be run, based on file extension, path names, and whether the app has been digitally signed.

The most effective, though most painful approach, is to disallow everything and then add back application that you really, really need. This is known as whitelisting.

We’ll go into more details in the next post.

Anyway, you’ll need to launch the policy editor, gpedit, and navigate to Local Computer Policy>Windows Settings>Security Settings>Software Restriction Polices>Security Levels. If you click on “Disallowed”, you can then make this the default security policy — to not run any executables!

The whitelist: disallow as default, and then add app policies in “Additional Rules”.

This is more like a scorched earth policy. In practice, you’ll need to enter “Additional Rules” to add back the approved apps (with their path names). If you leave out PowerShell, then you’ve effectively disabled this tool on the site.

Unfortunately, you can’t fine-tune the SRP rules based on AD groups or users. Drat!

And that we’ll bring us to Microsoft’s latest and greatest security enforcer, known as AppLocker, which does provide some nuance to application access. We’ll take that up next time as well.

Continue reading the next post in "Disabling PowerShell and Other Malware Nuisances"

Practical PowerShell for IT Security, Part III: Classification on a Budget

Practical PowerShell for IT Security, Part III: Classification on a Budget

Last time, with a few lines of PowerShell code, I launched an entire new software category, File Access Analytics (FAA). My 15-minutes of fame is almost over, but I was able to make the point that PowerShell has practical file event monitoring aspects. In this post, I’ll finish some old business with my FAA tool and then take up PowerShell-style data classification.

Event-Driven Analytics

To refresh memories, I used the Register-WmiEvent cmdlet in my FAA script to watch for file access events in a folder. I also created a mythical baseline of event rates to compare against. (For wonky types, there’s a whole area of measuring these kinds of things — hits to web sites, calls coming into a call center, traffic at espresso bars — that was started by this fellow.)

When file access counts reach above normal limits, I trigger a software-created event that gets picked up by another part of the code and pops up the FAA “dashboard”.

This triggering is performed by the New-Event cmdlet, which allows you to send an event, along with other information, to a receiver. To read the event, there’s the WMI-Event cmdlet. The receiving part can even be in another script as long as both event cmdlets use the same SourceIdentifier — Bursts, in my case.

These are all operating systems 101 ideas: effectively, PowerShell provides a simple message passing system. Pretty neat considering we are using what is, after all, a bleepin’ command language.

Anyway, the full code is presented below for your amusement.

$cur = Get-Date
$Global:baseline = @{"Monday" = @(3,8,5); "Tuesday" = @(4,10,7);"Wednesday" = @(4,4,4);"Thursday" = @(7,12,4); "Friday" = @(5,4,6); "Saturday"=@(2,1,1); "Sunday"= @(2,4,2)}
$Global:cnts =     @(0,0,0)
$Global:burst =    $false
$Global:evarray =  New-Object System.Collections.ArrayList

$action = { 
    $i= [math]::floor((Get-Date).Hour/8) 


   #event auditing!
   $rawtime =  $EventArgs.NewEvent.TargetInstance.LastAccessed.Substring(0,12)
   $filename = $EventArgs.NewEvent.TargetInstance.Name
   $etime= [datetime]::ParseExact($rawtime,"yyyyMMddHHmm",$null)
   $msg="$($etime)): Access of file $($filename)"
   $msg|Out-File C:\Users\bob\Documents\events.log -Append
   if(!$Global:burst) {
   else { 
     if($Global:start.AddMinutes(15) -gt $etime ) { 
        #File behavior analytics
        $sfactor=2*[math]::sqrt( $Global:baseline["$($d)"][$i])
        if ($Global:Count -gt $Global:baseline["$($d)"][$i] + 2*$sfactor) {
          "$($etime): Burst of $($Global:Count) accesses"| Out-File C:\Users\bob\Documents\events.log -Append 
          $Global:burst =$false
          New-Event -SourceIdentifier Bursts -MessageData "We're in Trouble" -EventArguments $Global:evarray
          $Global:evarray= [System.Collections.ArrayList] @();
     else { $Global:burst =$false; $Global:Count=0; $Global:evarray= [System.Collections.ArrayList]  @();}
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 = 'txt' or targetInstance.Extension = 'doc' or targetInstance.Extension = 'rtf') and targetInstance.LastAccessed > '$($cur)' " -sourceIdentifier "Accessor" -Action $action   

While ($true) {
    $args=Wait-Event -SourceIdentifier Bursts # wait on Burst event
    Remove-Event -SourceIdentifier Bursts #remove event
    foreach ($result in $args.SourceArgs) {
      $obj = New-Object System.Object
      $obj | Add-Member -type NoteProperty -Name File -Value $result[0]
      $obj | Add-Member -type NoteProperty -Name Time -Value $result[1]
      $outarray += $obj  

     $outarray|Out-GridView -Title "FAA Dashboard: Burst Data"

Please don’t pound your laptop as you look through it.

I’m aware that I continue to pop up separate grid views, and there are better ways to handle the graphics. With PowerShell, you do have access to the full .Net framework, so you could create and access objects —listboxes, charts, etc. — and then update as needed. I’ll leave that for now as a homework assignment.

Classification is Very Important in Data Security

Let’s put my file event monitoring on the back burner, as we take up the topic of PowerShell and data classification.

At Varonis, we preach the gospel of “knowing your data” for good reason. In order to work out a useful data security program, one of the first steps is to learn where your critical or sensitive data is located — credit card numbers, consumer addresses, sensitive legal documents, proprietary code.

The goal, of course, is to protect the company’s digital treasure, but you first have to identify it. By the way, this is not just a good idea, but many data security laws and regulations (for example, HIPAA)  as well as industry data standards (PCI DSS) require asset identification as part of doing real-world risk assessment.

PowerShell should have great potential for use in data classification applications. Can PS access and read files directly? Check. Can it perform pattern matching on text? Check. Can it do this efficiently on a somewhat large scale? Check.

No, the PowerShell classification script I eventually came up with will not replace the Varonis Data Classification Framework. But for the scenario I had in mind – a IT admin who needs to watch over an especially sensitive folder – my PowerShell effort gets more than a passing grad, say B+!

WQL and CIM_DataFile

Let’s now return to WQL, which I referenced in the first post on event monitoring.

Just as I used this query language to look at file events in a directory, I can tweak the script to retrieve all the files in a specific directory. As before I use the CIM_DataFile class, but this time my query is directed at the folder itself, not the events associated with it.

$Get-WmiObject -Query "SELECT * From CIM_DataFile where Path = '\\Users\\bob\\' and Drive = 'C:' and (Extension = 'txt' or Extension = 'doc' or Extension = 'rtf')"

Terrific!  This line of code will output an array of file path names.

To read the contents of each file into a variable, PowerShell conveniently provides the Get-Content cmdlet. Thank you Microsoft.

I need one more ingredient for my script, which is pattern matching. Not surprisingly, PowerShell has a regular expression engine. For my purposes it’s a little bit of overkill, but it certainly saved me time.

In talking to security pros, they’ve often told me that companies should explicitly mark documents or presentations containing proprietary or sensitive information with an appropriate footer — say, Secret or Confidential. It’s a good practice, and of course it helps in the data classification process.

In my script, I created a PowerShell hashtable of possible marker texts with an associated regular expression to match it. For documents that aren’t explicitly marked this way, I also added special project names — in my case, snowflake — that would also get scanned. And for kicks, I added a regular expression for social security numbers.

The code block I used to do the reading and pattern matching is listed below. The file name to read and scan is passed in as a parameter.

$Action = {

Param (

[string] $Name


$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}' }

$data = Get-Content $Name

$cnts= @()

foreach ($key in $classify.Keys) {


  if($m.Count -gt 0) {

    $cnts+= @($key,$m.Count)


Magnificent Multi-Threading

I could have just simplified my project by taking the above code and adding some glue, and then running the results through the Out-GridView cmdlet.

But this being the Varonis IOS blog, we never, ever do anything nice and easy.

There is a point I’m trying to make. Even for a single folder in a corporate file system, there can be hundreds, perhaps even a few thousand files.

Do you really want to wait around while the script is serially reading each file?

Of course not!

Large-scale file I/O applications, like what we’re doing with classification, is very well-suited for multi-threading—you can launch lots of file activity in parallel and thereby significantly reduce the delay in seeing results.

PowerShell does have a usable (if clunky) background processing system known as Jobs. But it also boasts an impressive and sleek multi-threading capability known as Runspaces.

After playing with it, and borrowing code from a few Runspaces’ pioneers, I am impressed.

Runspaces handles all the messy mechanics of synchronization and concurrency. It’s not something you can grok quickly, and even Microsoft’s amazing Scripting Guys are still working out their understanding of this multi-threading system.

In any case, I went boldly ahead and used Runspaces to do my file reads in parallel. Below is a bit of the code to launch the threads: for each file in the directory I create a thread that runs the above script block, which returns matching patterns in an array.

$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, 5)


$Tasks = @()

foreach ($item in $list) {

   $Task = [powershell]::Create().AddScript($Action).AddArgument($item.Name)

   $Task.RunspacePool = $RunspacePool

   $status= $Task.BeginInvoke()

   $Tasks += @($status,$Task,$item.Name)

Let’s take a deep breath—we’ve covered a lot.

In the next post, I’ll present the full script, and discuss some of the (painful) details.  In the meantime, after seeding some files with marker text, I produced the following output with Out-GridView:

Content classification on the cheap!

In the meantime, another idea to think about is how to connect the two scripts: the file activity monitoring one and the classification script partially presented in this post.

After all, the classification script should communicate what’s worth monitoring to the file activity script, and the activity script could in theory tell the classification script when a new file is created so that it could classify it—incremental scanning in other words.

Sounds like I’m suggesting, dare I say it, a PowerShell-based security monitoring platform. We’ll start working out how this can be done the next time as well.

Continue reading the next post in "Practical PowerShell for IT Security"

Practical PowerShell for IT Security, Part II: File Access Analytics (FAA)

Practical PowerShell for IT Security, Part II: File Access Analytics (FAA)

In working on this series, I almost feel that with PowerShell we have technology that somehow time-traveled back from the future. Remember on Star Trek – the original of course — when the Enterprise’s CTO, Mr. Spock, was looking into his visor while scanning parsecs of space? The truth is Spock was gazing at the output of a Starfleet-approved PowerShell script.

Tricorders? Also powered by PowerShell.

Yes, I’m a fan of PowerShell, and boldly going where no blogger has gone before. For someone who’s been raised on bare-bones Linux shell languages, PowerShell looks like super-advanced technology. Part of PowerShell’s high-tech prowess is its ability, as I mentioned in the previous post, to monitor low-level OS events, like file updates.

A Closer Look at Register-WmiEvent

Let’s return to the amazing one-line of file monitoring PS code I introduced last time.

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 &gt; '$($cur)' " -sourceIdentifier "Accessor" -Action $action


As you might have guessed, the logic on what to monitor is buried in the WQL contained in Register-WmiEvent’s query parameter.

You’ll recall that WQL allows scripters to retrieve information about Windows system events in general and, specifically in our case, file events – files created, updated, or deleted.  With this query, I’m effectively pulling out of Windows darker depths file modification events that are organized as a CIM_DataFile class.

WQL allows me to set the drive and folder I’m interested in searching — that would be the Drive and Path properties that I reference above.

Though I’m not allowed to use a wild card search — it’s a feature, not a bug — I can instead search for specific file extensions. My goal in developing the script for this post is to help IT security spot excessive activity on readable files.  So I set up a logical condition to search for files with “doc” or “txt” extensions. Makes sense, right?

Now for the somewhat subtle part.

I’d like to collect file events generated by anyone accessing a file, including those who just read a Microsoft Word documents without making changes.

Can that be done?

When we review a file list in Windows Explorer, we’re all familiar with the “Date Modified” field. But did you know there’s also a “Date Accessed” field? Every time you read a file in Windows, this field is, in theory, updated with the current time stamp. You can discover this for yourself—see below—by clicking on the column heads and enabling the access field.

However, in practice, Windows machines aren’t typically configured to update this internal field when a file is just accessed—i.e., read, but not modified. Microsoft says it will slow down performance. But let’s throw caution to the wind.

To configure Windows to always update the file access time, you use the under-appreciated fsutil utility (you’ll need admin access) with the following parameters: 

fsutil set behavior disablelastaccess 0

With file access events now configured in my test environment, I’ve now enabled Windows to also record read-only events.

My final search criteria in the above WQL should make sense:

targetInstance.LastAccessed > '$($cur)'

It says that I’m only interested in file events in which file access has occurred after the Register-WmiEvent is launched. The $cur variable, by the way is assigned the current time pulled from the Get-Date cmdlet.

File Access Analytics (FAA)

We’ve gotten through the WQL, so let’s continue with the remaining parameters in Register-WmiEvent.

SourceIdentifer allows you to name an event. Naming things – people, tabby cats, and terriers—is always a good practice since you can call them when you need ‘em.

And it holds just as true for events! There are few cmdlets that require this identifier. For starters, Unregister-Event for removing a given event subscription, Get-Event for letting you review all the events that are queued, Remove-Event for erasing current events in the queue, and finally Wait-Event for doing an explicit synchronous wait. We’ll be using some of these cmdlets in the completed code.

I now have the core of my script worked out.

That leaves the Action parameter. Since Register-WmiEvent responds asynchronously to events, it needs some code to handle the response to the triggering event, and that’s where the action, so to speak is: in a block of PowerShell code that’s passed in.

This leads to what I really want to accomplish with my script, and so I’m forced to reveal my grand scheme to take over the User Behavior Analytics world with a few lines of PowerShell code.

Here’s the plan: This PS script will monitor file access event rates, compare it to a baseline, and decide whether the event rates fall into an abnormal range, which could indicate possible hacking. If this threshold is reached, I’ll display an amazing dashboard showing the recent activity.

In other words, I’ll have a threat monitor alert system that will spot unusual activity against text files in a specific directory.

Will Powershell Put Security Solutions Out of Business?

No, Varonis doesn’t have anything to worry about, for a few reasons.

One, event monitoring is not really something Windows does efficiently. Microsoft in fact warns that turning on last access file updates through fsutil adds system overhead. In addition, Register-WmiEvent makes the internal event flywheels spin faster: I came across some comments saying the cmdlet may cause the system to slow down.

Two, I’ve noticed that this isn’t real-time or near real-time monitoring: there’s a lag in receiving file events, running up to 30 minutes or longer. At least, that was my experience running the scripts on my AWS virtual machine. Maybe you’ll do better on your dedicated machine, but I don’t think Microsoft is making any kind of promises here.

Three, try as I might, I was unable to connect a file modification event to the user of the app that was causing the event. In other words, I know a file even has occurred, but alas it doesn’t seem to be possible with Register-WMIEvent to know who caused it.

So I’m left with a script that can monitor file access but without assigning attribution. Hmmm …  let’s create a new security monitoring category, called File Access Analytics (FAA), which captures what I’m doing. Are you listening Gartner?

The larger point, of course, is that User Behavior Analytics (UBA) is a far better way to spot threats because user-specific activity contains the interesting information. My far less granular FAA, while useful, can’t reliably pinpoint the bad behaviors since it aggregates events over many users.

However, for small companies and with a few account logged on, FAA may be just enough. I can see an admin using the scripts when she suspects a user who is spending too much time poking around a directory with sensitive data. And there are some honeypot possibilities with this code as well.

And even if my script doesn’t quite do the job, the even larger point is that understanding the complexities of dealing with Windows events using PowerShell (or other language you use) will make you, ahem, appreciate enterprise-class solutions.

We’re now ready to gaze upon the Powershell scriptblock of my Register-WmiEvent:

$action = { 
    $i= [math]::floor((Get-Date).Hour/8) 


   #event auditing!
   $rawtime =  $EventArgs.NewEvent.TargetInstance.LastAccessed.Substring(0,12)
   $filename = $EventArgs.NewEvent.TargetInstance.Name
   $etime= [datetime]::ParseExact($rawtime,"yyyyMMddHHmm",$null)
   $msg="$($etime)): Access of file $($filename)"
   $msg|Out-File C:\Users\bob\Documents\events.log -Append

   if(!$Global:burst) {
   else { 
     if($Global:start.AddMinutes(15) -gt $etime ) { 
        #File behavior analytics
        $sfactor=2*[math]::sqrt( $Global:baseline["$($d)"][$i])
        write-host "sfactor: $($sfactor))"
        if ($Global:Count -gt $Global:baseline["$($d)"][$i] + 2*$sfactor) {
          "$($etime): Burst of $($Global:Count) accesses"| Out-File C:\Users\bob\Documents\events.log -Append 
          $Global:burst =$false
          New-Event -SourceIdentifier Bursts -MessageData "We're in Trouble" -EventArguments $Global:evarray
          $Global:evarray= [System.Collections.ArrayList] @();
     else { $Global:burst =$false; $Global:Count=0; $Global:evarray= [System.Collections.ArrayList]  @();}


Yes, I do audit logging by using the Out-File cmdlet to write a time-stamped entry for each access. And I detect bursty file access hits over 15-minute intervals, comparing the event counts against a baseline that’s held in the $Global:baseline array.

I got a little fancy here, and set up mythical average event counts in baseline for each day of the week, dividing the day into three eight hour periods. When the burst activity in a given period falls at the far end of the “tail” of the bell curve, we can assume we’ve spotted a threat.

The FAA Dashboard

With the bursty event data held in $Global:evarray (files accessed with timestamps), I decided that it would be a great idea to display it as a spiffy dashboard. But rather than holding up the code in the scriptblock, I “queued” up this data on its own event, which can be handled by a separates app.


Let me try to explain. This is where the New-Event cmdlet comes into play at the end of the scriptblock above. It simply allows me to asynchronously ping another app or script, thereby not tying down the code in the scriptblock so it can then handle the next file access event.

I’ll present the full code for my FAA PowerShell script in the next post.  For now, I’ll just say that I set up a Wait-Event cmdlet whose sole purpose is to pick up these burst events and then funnel the output into a beautiful table, courtesy of Out-GridView.

Here’s the end result that will pop on an admin’s console:


Impressive in its own way considering the whole FAA “platform” was accomplished in about 60 lines of PS code.

We’ve covered a lot of ground, so let’s call it a day.

We’ll talk more about the full FAA script the next time, and then we’ll start looking into the awesome hidden content classification possibilities of PowerShell.


Continue reading the next post in "Practical PowerShell for IT Security"

Varonis eBook: Pen Testing Active Directory Environments

Varonis eBook: Pen Testing Active Directory Environments

You may have been following our series of posts on pen testing Active Directory environments and learned about the awesome powers of PowerView. No doubt you were wowed by our cliffhanger ending — spoiler alert — where we applied graph theory to find the derivative admin!

Or maybe you tuned in late, saw this post, and binge read the whole thing during snow storm Nemo.

In any case, we know from the many emails we received that you demanded a better ‘long-form’ content experience. After all, who’d want to read about finding hackable vulnerabilities using Active Directory while being forced to click six-times to access the entire series?

We listened!

Thanks to the miracle of PDF technology, we’ve compressed the entire series into an easy-to-ready, comfy ebook format. Best of all, you can scroll through the entire contents without having to touch messy hyperlinks.

Download the Varonis Pen Testing Active Directory Environments ebook, and enjoy click-free reading today!

Practical PowerShell for IT Security, Part I: File Event Monitoring

Practical PowerShell for IT Security, Part I: File Event Monitoring

Back when I was writing the ultimate penetration testing series to help humankind deal with hackers, I came across some interesting PowerShell cmdlets and techniques. I made the remarkable discovery that PowerShell is a security tool in its own right. Sounds to me like it’s the right time to start another series of PowerShell posts.

We’ll take the view in these posts that while PowerShell won’t replace purpose-built security platforms — Varonis can breathe easier now — it will help IT staff monitor for threats and perform other security functions. And also give IT folks an appreciation of the miracles that are accomplished by real security platforms, like our own Metadata Framework. PowerShell can do interesting security work on a small scale, but it is in no way equipped to take on an entire infrastructure.

It’s a Big Event

To begin, let’s explore using PowerShell as a system monitoring tool to watch files, processes, and users.

Before you start cursing into your browsers, I’m well aware that any operating system command language can be used to monitor system-level happenings. A junior IT admin can quickly put together, say, a Linux shell script to poll a directory to see if a file has been updated or retrieve a list of running processes to learn if a non-standard process has popped up.

I ain’t talking about that.

PowerShell instead gives you direct event-driven monitoring based on the operating system’s access to low-level changes. It’s the equivalent of getting a push notification on a news web page alerting you to a breaking story rather than having to manually refresh the page.

In this scenario, you’re not in an endless PowerShell loop, burning up CPU cycles, but instead the script is only notified or activated when the event — a file is modified or a new user logs in — actually occurs. It’s a far more efficient way to do security monitoring than by brute-force polling.

Further down below, I’ll explain how this is accomplished.

But first, anyone who’s ever taken, as I have, a basic “Operating Systems for Poets” course knows that there’s a demarcation between user-level and system-level processes.

The operating system, whether Linux or Windows, does the low-level handling of device actions – anything from disk reads, to packets being received — and hides this from garden variety apps that we run from our desktop.

So if you launch your favorite word processing app and view the first page of a document, the whole operation appears as a smooth, synchronous activity. But in reality there are all kinds of time-sensitive actions events — disk seeks, disk blocks being read, characters sent to the screen, etc. — that are happening under the hood and deliberately hidden from us.  Thank you Bill Gates!

In the old days, only hard-core system engineers knew about this low-level event processing. But as we’ll soon see, PowerShell scripters can now share in the joy as well.

An OS Instrumentation Language

This brings us to Windows Management Instrumentation (WMI), which is a Microsoft effort to provide a consistent view of operating system objects.

Only a few years old, WMI is itself part of a broader industry effort, known as Web-based Enterprise Management (WBEM), to standardize the information pulled out of routers, switches, storage arrays, as well as operating systems.

So what does WMI actually look and feel like?

For our purposes, it’s really a query language, like SQL, but instead of accessing rows of vanilla database columns, it presents complex OS information organized as a WMI_class hierarchy. Not too surprisingly, the query language is known as, wait for it, WQL.

Windows generously provides a utility, wbemtest, that lets you play with WQL. In the graphic below, you can see the results of my querying the Win32_Process object, which holds information on the current processes running.

WQL on training wheels with wbemtest.

Effectively, it’s the programmatic equivalent of running the Windows task monitor. Impressive, no? If you want to know more about WQL, download Ravi Chaganti’s wonderous ebook on the subject.

PowerShell and the Register-WmiEvent Cmdlet

But there’s more! You can take off the training wheels provided by wbemtest, and try these queries directly in PowerShell.

Powershell’s Get-WMIObject is the appropriate cmdlet for this task, and it lets you feed in the WQL query directly as a parameter.

The graphic below shows the first few results from running select Name, ProcessId, CommandLine from Win32_Process on my AWS test environment.

gwmi is the PowerShell alias for Get-WmiObject.

The output is a bit wonky since it’s showing some hidden properties having to do with underlying class bookkeeping. The cmdlet also spews out a huge list that speeds by on my console.

For a better Win32_Process experience, I piped the output from the query into Out-GridView, a neat PS cmdlet that formats the data as a beautiful GUI-based table.

Not too shabby for a line of PowerShell code. But WMI does more than allow you to query these OS objects.

As I mentioned earlier, it gives you access to relevant events on the objects themselves. In WMI, these events are broadly broken into three types: creation, modification, and deletion.

Prior to PowerShell 2.0, you had to access these events in a clunky way: creating lots of different objects, and then you were forced to synchronously ‘hang’, so it wasn’t true asynchronous event-handling. If you want to know more, read this MS Technet post for the ugly details.

Now in PS 2.0 with the Register-WmiEvent cmdlet, we have a far prettier way to react to all kinds of events. In geek-speak, I can register a callback that fires when the event occurs.

Let’s go back to my mythical (and now famous) Acme Company, whose IT infrastructure is set up on my AWS environment.

Let’s say Bob, the sys admin, notices every so often that he’s running low on file space on the Salsa server. He suspects that Ted Bloatly, Acme’s CEO, is downloading huge files, likely audio files, into one of Bob’s directories and then moving them into Ted’s own server on Taco.

Bob wants to set a trap: when a large file is created in his home directory, he’ll be notified on his console.

To accomplish this, he’ll need to work with the CIM_DataFile class.  Instead of accessing processes, as we did above, Bob uses this class to connect with the underlying file metadata.

CIM_DataFile object can be accessed directly in PowerShell.

Playing the part of Bob, I created the following Register-WmiEvent script, which will notify the console when a very large file is created in the home directory.

Register-WmiEvent -Query "SELECT * FROM __InstanceModificationEvent WITHIN 5 WHERE TargetInstance isa 'CIM_DataFile' and TargetInstance.FileSize &gt; 2000000 and TargetInstance.Path = '\\Users\\bob\\' and targetInstance.Drive = 'C:' "-sourceIdentifier "Accessor3" -Action  { Write-Host "Large file" $EventArgs.NewEvent.TargetInstance.Name  "was created”}


Running this script directly from the Salsa console launches the Register-WmiEvent command in the background, assigning it a job number, and then only interacts with the console when the event is triggered.

In the next post, I’ll go into more details about what I’ve done here. Effectively, I’m using WQL to query the CIM_DataFile object — particularly anything in the \Users\bob directory that’s over 2 million bytes — and set up a notification when a new file is created that fits this criteria —that’s where InstanceModificationEvent comes into play.

Anyway, in my Bob role  I launched the script from the PS command line, and then putting on my Ted Bloatly hat, I copied a large mp4 into Bob’s directory. You can see the results below.

We now know that Bloatly is a fan of Melody Gardot. Who would have thunk it?

You begin to see some of the exciting possibilities with PowerShell as a tool to detect threats patterns and perhaps for doing a little behavior analytics.

We’ll be exploring these ideas in the next post.

Continue reading the next post in "Practical PowerShell for IT Security"

Binge Read Our Pen Testing Active Directory Series

Binge Read Our Pen Testing Active Directory Series

With winter storm Niko now on its extended road trip, it’s not too late, at least here in the East Coast, to make a few snow day plans. Sure you can spend part of Thursday catching up on Black Mirror while scarfing down this slow cooker pork BBQ pizza. However, I have a healthier suggestion.

Why not binge on our amazing Pen Testing Active Directory Environments blog posts?

You’ve read parts of it, or — spoiler alert — perhaps heard about the exciting conclusion involving a depth-first-search of the derivative admin graph. But now’s your chance to totally immerse yourself and come back to work better informed about the Active Directory dark knowledge that hackers have known about for years.

And may we recommend eating these healthy soy-roasted kale chips while clicking below?

Continue reading the next post in "Pen Testing Active Directory Environments"