Monitoring with PowerShell: Preventing PowerShell based attacks (LoLBas)

In the last Huntress Tradecraft Tuesdays there was some discussion about using “Living of the land” techniques. Living of the land means using the tools available on the operation system to achieve access. There’s a lot of ways that bad actors are now using these system tools to deploy ransomware for example.

PowerShell is one of these tools; during Tradecraft Tuesday I’ve suggested using a technique to block some of these attacks. Some of my friends came to me afterwards for some more information so I figured I’d make a small blog about it 🙂

Most of the LoLBin and LoLBas techniques make use of PowerShell commands that execute a script directly in memory – This is a ‘file-less’ technique. These PowerShell commands can actually be blocked quite easily by changing their alias to something else. This does mean that scripts that utilize these commands might break too. I’ll be showing a way to work around that for your own scripts.

I like using the following steps to try and prevent these type of attacks. Just a small disclaimer: This is a tiny part of your security suite and you should not rely on this 100%. Using this you are locking one of the doors in your house. It won’t help if you keep all the others open.

Deployment script

So first we’ll start with a very small deployment script you can run on all your workstations. The script takes care of the following things:

  • Enables Script Block Logging.
    • Please note that with enabling script block logging, all executed scripts will be logged. If your password is in plain text it will also be shown in these logs. If you have scripts that use plain-text passwords or keys please exit them when script block logging is enabled.
  • Changes the alias of the most dangerous commands to “write-host” – nullifying the risk of these commands.
  • You can add or remove commands as you see fit. The ones in the script are just the most well known.
New-Item -Path "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Force
Set-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Name "EnableScriptBlockLogging" -Value 1 -Force
#Set aliasses to something harmless
$DangerousCommands = @("iwr", "irm", "curl", "saps", "iex", "Invoke-Expression", "Invoke-RestMethod", "Invoke-WebRequest", "Invoke-RestMethod")
foreach ($Command in $DangerousCommands) {
    Set-Alias -Name $Command -Value "write-host" -Option AllScope -Force

So you’ve probably noticed that we’re actually overwriting these commands with an alias. Aliases take precedence over actual cmdlets. This is great for us but will cause scripts that use these commands to fail.

We can prevent this failure by running this script at the top of our execution scripts:

$DangerousCommands = @("iwr", "irm", "curl", "saps", "iex", "Invoke-Expression", "Invoke-RestMethod", "Invoke-WebRequest", "Invoke-RestMethod")
foreach ($Command in $DangerousCommands) {
    get-item alias:$Command | remove-item -Force

This will make the commands available again, except their shortest version such as “iwr” and “iex”. We’re not done yet though – We also want to see when people are doing bad stuff right? That’s why we have a monitoring script to load into your RMM too!

Monitoring script

So this monitoring script picks up the following:

  • If script logging is enabled. The results of this are stored in $ScriptBlockEnable
  • If the most dangerous commands are alliased to write-host, $AliasProtection contains the result.
  • If the PowerShell event logs in the previous hour contain one of the dangerous aliases. You could also add stuff like “HTTPS” to the dangerous aliases.

I’d suggest to run this script as much as possible with your RMM, so you get warned early when something phishy is going on.

$ScriptBlockLogging = get-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
$ScriptBlockEnable = if ($ScriptBlockLogging.EnableScriptBLockLogging -ne 1) { "Error - Script Block Logging is not enabled" } else { "Healthy - Script Block Logging is enabled" }

$DangerousCommands = @("iwr", "irm", "curl", "saps", "iex", "Invoke-Expression", "Invoke-RestMethod", "Invoke-WebRequest", "Invoke-RestMethod")
$Aliasses = get-alias | Where-Object { $ -in $DangerousCommands -and $_.ResolvedCommandName -ne "Write-Host" } 
if (!$Aliasses) {
    $AliasProtection = "Healthy - Dangerous commands are protected."
else {
    $AliasProtection = "Unhealthy - Dangerous commands are not protected. Please investigate."
$logInfo = @{ 
    ProviderName = "Microsoft-Windows-PowerShell"
    StartTime    = (get-date).AddHours(-2)
$PowerShellEvents = Get-WinEvent -FilterHashtable $logInfo | Select-Object TimeCreated, message
$PowerShellLogs = foreach ($Event in $PowerShellEvents) {

    foreach ($command in $DangerousCommands) {
        if ($Event.Message -like "*$Command*") { 
            [pscustomobject] @{
                TimeCreated      = $event.TimeCreated
                EventMessage     = $Event.message
                TriggeredCommand = $command



    $PowerShellLogs = "Healthy"

And that’s it! This should offer you a bit more protection from Lolbin/Lolbas and fileless attacks. As always, Happy PowerShelling.

1 Comment

  1. James G November 1, 2020 at 12:58 pm

    Hi Kelvin.
    Since we’re enabling logging of PS scripts, is there a way to script the max log size of the PowerShell log? The ‘limit-eventlog’ cmdlet can’t even see the “Microsoft-Windows-PowerShell/Operational” log.
    My goal is to set the max size of the log file, as I already do with the app, sys, and sec logs, and then add the PowerShell log to your component that monitors and automatically backs up the event logs.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.