<img height="1" width="1" style="display:none;" alt="" src="https://px.ads.linkedin.com/collect/?pid=2815180&amp;fmt=gif">

Improving Visibility and Preventing a Miss - Part 3: Custom PowerShell Rules

A major risk for a SIEM or SOAR is not effectively using key PowerShell logs collected.

We talked about the risk of incorrect and empty logs or lack of logging required for advanced detection, and once you have them we cannot assume machine learning and modeling behavior will detect everything.

In part three of our PowerShell tutorial series, we are looking at augmenting rules out-of-the-box in Exabeam with some custom rules derived from SANS discussions on items to monitor.

One of our friends, Andrew Travis, was kind enough to collaborate and create some rules we use in detecting the things that might not be addressed with your current SIEM or considered by the ML.

We have been collecting and reviewing PowerShell, a critical component for detection within the Windows OS. The rules we are sharing here, are things SANS points out regarding detection when it comes to attackers trying to evade detection.

  • Encrypted argument in a PowerShell command detected
  • ExecutionPolicy Bypass argument in a PowerShell command detected
  • Hidden argument in a PowerShell command detected
  • Hide Powershell History in PSReadLine
  • Invoke-Expression argument in a PowerShell command detected
  • NoProfile argument in a PowerShell command detected
  • Non-Interactive argument in a PowerShell command detected

In the first article, we forced transcription logging so we can revisit previously issued commands in detail. In the second article, we reviewed all the extra channels in which PowerShell logs reside. 

Exabeam has amazing ML and plenty of rules, so our goal is to augment existing rules by contributing risk to sessions (user/account/asset) around scenarios that are much less likely to occur in day-to-day business.

Without further ado, let's look at the rules. Note these are all FACT-based rules, or “finding the needle in the haystack”, which is why writing the correct log and collecting them really matters.


Castra-created-PS-Encypted {
  RuleName = "Encrypted argument in a Powershell command detected"
  RuleDescription = "This behavior may indicate an attempt to bypass detection with malicious activity such as exfiltration software or payloads. Castra Apr 2021"
  ReasonTemplate = "Encrypted argument in a Powershell command detected"
  AggregateReasonTemplate = "Encrypted argument in a Powershell command detected: {default|featureValue}"
  RuleType = "endpoint"
  RuleCategory = "Endpoint Activity"
  ClassifyIf = "TRUE"
  RuleEventTypes = ["process-created"]
  Disabled = "FALSE"
  Model = "FACT"
  FactFeatureName = "user"
  Score = "30.0"
  PercentileThreshold = "0.1"
  RuleExpression = "!wasRuleFired('Castra-created-PS-Encypted') && exists(command_line) && (contains(toLower(command_line),' -enc ') || contains(toLower(command_line),' -encodedcommand ') || contains(toLower(command_line),' -ec ')) && contains(toLower(process_name),'powershell')"
  DependencyExpression = "Powershell-logging"
  RuleLabels {
mitre = ["T1059.001"]
picasso = ["Malware Detection",
  "Ransomware Detection"]
  }
}
 

Feel free to adjust scores as well, but a reminder that 90 is where things become notable, so these alone can create incidents as needed if we skew that 30 up to 90!

Castra-created-PS-Bypass {
  RuleName = "ExecutionPolicy Bypass argument in a Powershell command detected"
  RuleDescription = "This behavior may indicate an attempt to bypass detection with malicious activity such as exfiltration software or payloads. Castra Apr 2021"
  ReasonTemplate = "Execution Bypass argument in a Powershell command detected"
  AggregateReasonTemplate = "Execution Bypass argument in a Powershell command detected: {default|featureValue}"
  RuleType = "endpoint"
  RuleCategory = "Endpoint Activity"
  ClassifyIf = "true"
  RuleEventTypes = ["process-created"]
  Disabled = "false"
  Model = "FACT"
  FactFeatureName = "user"
  Score = "30.0"
  PercentileThreshold = "0.1"
  RuleExpression = "(!wasrulefired('Castra-created-PS-Bypass') && (exists(command_line) && (contains(tolower(command_line), ' -Exec Bypass') && contains(tolower(process_name), 'powershell'))))"
  DependencyExpression = "NA"
}
 

Any Bypass will trigger this rule, minor false positives have occurred, but all were worth review. 

Castra-created-PS-Hidden {
  RuleName = "Hidden argument in a Powershell command detected"
  RuleDescription = "This behavior may indicate an attempt to bypass detection with malicious activity such as exfiltration software or payloads. Castra Apr 2021"
  ReasonTemplate = "Hidden argument in a Powershell command detected"
  AggregateReasonTemplate = "Hidden argument in a Powershell command detected: {default|featureValue}"
  RuleType = "endpoint"
  RuleCategory = "Endpoint Activity"
  ClassifyIf = "true"
  RuleEventTypes = ["process-created"]
  Disabled = "false"
  Model = "FACT"
  FactFeatureName = "user"
  Score = "30.0"
  PercentileThreshold = "0.1"
  RuleExpression = "(!wasrulefired('Castra-created-PS-Hidden') && (exists(command_line) && (contains(tolower(command_line), ' -w hidden') || contains(tolower(command_line), ' -WindowStyle hidden ')) && contains(tolower(process_name), 'powershell')))"
  DependencyExpression = "NA"
}
 
 
Castra-created-PS-History {
  RuleName = "Hide Powershell History in PSReadLine"
  RuleDescription = "This behavior may indicate an attempt to bypass detection with malicious activity such as exfiltration software or payloads. Castra Apr 2021"
  ReasonTemplate = "Hidden argument in a Powershell command detected"
  AggregateReasonTemplate = "Hidden argument in a Powershell command detected: {default|featureValue}"
  RuleType = "endpoint"
  RuleCategory = "Endpoint Activity"
  ClassifyIf = "true"
  RuleEventTypes = ["process-created"]
  Disabled = "false"
  Model = "FACT"
  FactFeatureName = "user"
  Score = "30.0"
  PercentileThreshold = "0.1"
  RuleExpression = "(!wasrulefired('Castra-created-PS-History') && (exists(command_line) && ((contains(tolower(command_line), 'Set-PSReadLineOption -HistorySaveStyle SaveNothing') || contains(tolower(command_line), 'Remove-Module -Name PSReadline')) && contains(tolower(process_name), 'powershell'))))"
  DependencyExpression = "NA"
}
 

We really hope to never see this one. This is one of the reasons for mandatory transcription!

Castra-created-PS-Expression {
  RuleName = "Invoke-Expression argument in a Powershell command detected"
  RuleDescription = "This behavior may indicate an attempt to bypass detection with malicious activity such as exfiltration software or payloads. Castra Apr 2021"
  ReasonTemplate = "Invoke-Expression argument in a Powershell command detected"
  AggregateReasonTemplate = "Invoke-Expression argument in a Powershell command detected: {default|featureValue}"
  RuleType = "endpoint"
  RuleCategory = "Endpoint Activity"
  ClassifyIf = "true"
  RuleEventTypes = ["process-created"]
  Disabled = "false"
  Model = "FACT"
  FactFeatureName = "user"
  Score = "30.0"
  PercentileThreshold = "0.1"
  RuleExpression = "(!wasrulefired('Castra-created-PS-Expression') && (exists(command_line) && ((contains(tolower(command_line), ' Invoke-Expression ') || contains(tolower(command_line), ' IEX ')) && contains(tolower(process_name), 'powershell'))))"
  DependencyExpression = "NA"
}
 

We would be interested in hearing from you if you have scenarios where this would be considered a false positive.

Castra-created-PS-NoProfile {
  RuleName = "NoProfile argument in a Powershell command detected"
  RuleDescription = "This behavior may indicate an attempt to bypass detection with malicious activity such as exfiltration software or payloads. Castra Apr 2021"
  ReasonTemplate = "NoProfile argument in a Powershell command detected"
  AggregateReasonTemplate = "NoProfile argument in a Powershell command detected: {default|featureValue}"
  RuleType = "endpoint"
  RuleCategory = "Endpoint Activity"
  ClassifyIf = "true"
  RuleEventTypes = ["process-created"]
  Disabled = "false"
  Model = "FACT"
  FactFeatureName = "user"
  Score = "15"
  PercentileThreshold = "0.1"
  RuleExpression = "(!wasrulefired('Castra-created-PS-NoProfile') && (exists(command_line) && ((contains(tolower(command_line), ' -NoProfile ') || (contains(tolower(command_line), ' -Nop ')) && contains(tolower(process_name), 'powershell')))))"
  DependencyExpression = "NA"
}
 


Castra-created-PS-nonIA-Arg {
  RuleName = "NonInteractive argument in a Powershell command detected"
  RuleDescription = "This behavior may indicate an attempt to bypass detection with malicious activity such as exfiltration software or payloads. Castra Apr 2021"
  ReasonTemplate = "NonInteractive argument in a Powershell command detected"
  AggregateReasonTemplate = "NonInteractive argument in a Powershell command detected: {default|featureValue}"
  RuleType = "endpoint"
  RuleCategory = "Endpoint Activity"
  ClassifyIf = "true"
  RuleEventTypes = ["process-created"]
  Disabled = "false"
  Model = "FACT"
  FactFeatureName = "user"
  Score = "15"
  PercentileThreshold = "0.1"
  RuleExpression = "(!wasrulefired('Castra-created-PS-nonIA-Arg') && (exists(command_line) && (contains(tolower(command_line), ' -NonInteractive ') || (contains(tolower(command_line), ' -Noni ') && contains(tolower(process_name), 'powershell')))))"
  DependencyExpression = "NA"
}
 

We hope this helps.

If you have any questions about these instructions, please reach out to Castra so that we can help walk you through these concepts. We are constantly attempting to improve our repository of best practice rules, dashboards, and visualizations used in Exabeam.