MaxFocus Script to Check for CryptoWall Infection

Mark Berry November 14, 2014

The CryptoWall ransomware virus is getting a lot of attention lately but this is one angle I haven’t seen covered:  how to get an alert in the MaxFocus (formerly GFI) dashboard if a machine is infected. The sooner you know, the sooner you can work on restoring a current backup.

Here’s a standalone PowerShell script that checks a few file and registry locations for evidence of CryptoWall and raises an error if found.

This approach has been called “canary in the coalmine” virus notification:  if it trips, the machine is infected. Note that it does nothing about preventing an infection; it is purely an after-the-fact alert.

Test and use this at your own risk. If you find a bug, or have a suggestion for a file or registry path that is not checked, leave a comment.

Compatibility and Functionality

The script should work on XP/2003 and above, and PowerShell 2 and above. It only works on U.S. English (and possibly other English) locales due to some hard-coded file paths (“Documents” etc.).

The script checks for DECRYPT_INST*.* files in two public folders and in three folders in each user’s profile. It also checks the registry for known CryptoLocker Decrypter keys. See more details in the script code and comments.

Update 11 December 2015 The script has been enhanced to also check for the patterns HELP_DECRYPT*.* and HELP_YOUR_FILES*.* so it can detect CryptoWall 3.0 and 4.0 infections respectively. See details in this post.

Update 19 April 2016 Additional patterns have been added and may be added in the future. See the changelog in the script for details.

Primary references used:

Use in MaxFocus

If you deploy the script to MaxFocus, you should see a quick summary of the results in the dashboard:

CryptoWall Check 1

Hopefully you won’t see this:

CryptoWall Check 2

If you click on the blue text, a window appears with more detail:

CryptoWall Check 3

The script runs very fast, a few seconds at most. Set it up in MaxFocus as a 24×7 check with Outage notification by email and/or SMS. Depending on how often your checks run, you should know of an infection within an hour at most.

The Script

Copy and paste this as a PowerShell script (e.g. CheckCryptoWall.ps1).

    Check for the existence of some common CryptoWall files and registry keys.
  This is not a comprehensive threat detection engine, and it does NOTHING to
  prevent or block the threat.  It just checks for the presence of decryption 
  instructions in some common folders, and for the presence of some registry
  keys used by the CryptoWall Decrypter.  Hopefully if you find out right away 
  that you have an encrypted machine, you can restore it from a recent backup.
  Only handles U.S. (and maybe other English) locales.

  Copyright (c) 2016 by MCB Systems.  All rights reserved.
  Free for personal or commercial use.  May not be sold.
  No warranties.  Use at your own risk.

  Can be used with MaxFocus Advanced Monitoring Agent.
  Returns summary line first, followed by list of paths checked.  The
  summary will be visible in the MaxFocus dashboard as a hyperlink.  
  When you click on the hyperlink, the details display in a window.
  Returns exit code 1001 if matching path found.  This will cause the
  MaxFocus check to fail and optionally to send an alert.

    Name:       MCB.CheckCryptoWall.ps1
    Author:     Mark Berry, MCB Systems
    Created:    11/14/2014
    Last Edit:  05/21/2016
  12/11/2015  Allow $FilePatterns parameter to be a comma-separated list of patterns.
              Add HELP_DECRYPT*.* and HELP_YOUR_FILES*.* to the list, to detect
              CryptoWall 3.0 and 4.0 infections respectively.  See 
  03/09/2016  Add *RECOVER_INSTRUCTIONS.* to $FilePatterns to include Locky cryptoware per
  04/19/2016  Add *.FUN, *.KKK, *.GWS, and *.BTC to detect Jigsaw ransomware per 
  05/21/2016  Add DE_CRYPT*.* to detect CryptXXX.  Note that CryptXXX 2.0 uses unique file names
              for ransom messages and cannot be detected with this script.

.Parameter FilePatterns
  Comma-separated list of filenames to look for in the hard-coded list of paths.  
  Wildcards allowed.  Do not include backslash.
    Default:  Updated frequently. See parameter below.
.Parameter LogFile
  Path to a log file. Required by GFI MAX script player.  Not used here.
  Default:  "".
    [Parameter(Mandatory = $false,
                    Position = 0,
                    ValueFromPipelineByPropertyName = $true)]

      [Parameter(Mandatory = $false,
                    Position = 1,
                    ValueFromPipelineByPropertyName = $true)]

# ----------------------------------------------------------
# Define functions
# ----------------------------------------------------------
function CheckFilePath( `
    [String]$FilePath="", `
    [String[]]$FilePatternArray="", `
  [ref]$OutputDetail) {
  $CheckFilePath = $false # Default to false; exceptions below

  # Loop through each $FilePattern in the $FilePatternArray
  ForEach ($FilePattern in $FilePatternArray) {  
    $FileFullPath = $FilePath + "\" + $FilePattern
    if (Test-Path $FileFullPath) { 
      $OutputDetail.value += ("`n--->File path """ + $FileFullPath + """ FOUND <---")
      $CheckFilePath = $true 
    } else {
      $OutputDetail.value += ("`nFile path """ + $FileFullPath + """ not found")
  return $CheckFilePath

function CheckRegistryPath( `
    [String]$RegistryPath="", `
  [ref]$OutputDetail) {

  if (Test-Path $RegistryPath) { 
    $OutputDetail.value += ("`n--->Registry path """ + $RegistryPath + """ FOUND <---")
    $CheckRegistryPath = $true 
  } else {
    $OutputDetail.value += ("`nRegistry path """ + $RegistryPath + """ not found")
    $CheckRegistryPath = $false
  return $CheckRegistryPath

# ----------------------------------------------------------
# Initialize variables
# ----------------------------------------------------------
$CryptoWallFound = $false
$OutputDetail = "" # Will build up a string in this variable to be output AFTER summary line

# ----------------------------------------------------------
# Check file paths
# ----------------------------------------------------------
# Parse $FilePatterns parameter into an array by splitting on commas
$FilePatternArray = $FilePatterns.split(",")

# Per, CryptoWall
# puts DECRYPT_INSTRUCTION.TXT, .URL, and .HTML files in every encrypted folder.  Check a few common folders.
# 12/11/2015:  CryptoWall 3.0 and 4.0 place HELP_DECRYPT*.* and HELP_YOUR_FILES*.*, respectively.
$OutputDetail += "`nCheck file paths"
$OutputDetail += "`n----------------"
$OutputDetail += "`nCommon paths"
$SpecialFolder = [environment]::getfolderpath("CommonApplicationData")
if (CheckFilePath($SpecialFolder) $FilePatternArray ([ref]$OutputDetail)) { $CryptoWallFound = $true }

# Because we run this as the SYSTEM user under MaxFocus, we need to "manually" derive 
# the paths to users' folders (on XP or Vista/7/8), then loop through them.  

$ProfileRoot = $env:SystemDrive + "\Users"
if (Test-Path $ProfileRoot) { # Windows Vista/7/8/10/2008/2008R2/2012/2012R2
  # [environment]::getfolderpath("CommonDesktopDirectory") not available in PowerShell 2 so code path directly
  if (CheckFilePath($ProfileRoot + "\Public\Desktop") $FilePatternArray ([ref]$OutputDetail)) { $CryptoWallFound = $true }

  $OutputDetail += "`nUser-specific paths"
  # List directories ("containers") only in the $ProfileRoot path. Exclude hidden folders and junction points.
  get-childitem $ProfileRoot |  Where-Object { $_.PSIsContainer } | ForEach-Object {
    # $_.Name is directory name only; $_.FullName includes full path 
    if ($_.Name -ne "Public") { # We handle common (Public) paths above
      if (CheckFilePath($_.FullName + "\Documents") $FilePatternArray ([ref]$OutputDetail)) { $CryptoWallFound = $true }    
      if (CheckFilePath($_.FullName + "\Desktop") $FilePatternArray ([ref]$OutputDetail)) { $CryptoWallFound = $true }    
      if (CheckFilePath($_.FullName + "\AppData\Local") $FilePatternArray ([ref]$OutputDetail)) { $CryptoWallFound = $true }    
} else { # should be XP/2003

  $ProfileRoot = $env:SystemDrive + "\Documents and Settings"
  if (Test-Path $ProfileRoot) {
    # [environment]::getfolderpath("CommonDesktopDirectory") not available in PowerShell 2 so code path directly
    if (CheckFilePath($ProfileRoot + "\All Users\Desktop") $FilePatternArray ([ref]$OutputDetail)) { $CryptoWallFound = $true }

    $OutputDetail += "`nUser-specific paths"
    # List directories ("containers") only in the $ProfileRoot path. Exclude hidden folders and junction points.
    get-childitem $ProfileRoot |  Where-Object { $_.PSIsContainer } | ForEach-Object {
      # $_.Name is directory name only; $_.FullName includes full path 
      if ($_.Name -ne "All Users") { # We handle common (All Users) paths above
        if (CheckFilePath($_.FullName + "\My Documents") $FilePatternArray ([ref]$OutputDetail)) { $CryptoWallFound = $true }    
        if (CheckFilePath($_.FullName + "\Desktop") $FilePatternArray ([ref]$OutputDetail)) { $CryptoWallFound = $true }    
        if (CheckFilePath($_.FullName + "\Local Settings\Application Data") $FilePatternArray ([ref]$OutputDetail)) { $CryptoWallFound = $true }    

# ----------------------------------------------------------
# Check registry paths
# ----------------------------------------------------------
$OutputDetail += "`n`nCheck registry paths"
$OutputDetail += "`n--------------------"
# From
# This only checks the existence of registry _keys_, not values
# Most keys are "CryptoWall Decrypter" but use a wildcard "CryptoWall*" for  a slightly more generic search.
if (CheckRegistryPath "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CryptoWall*" ([ref]$OutputDetail)) { $CryptoWallFound = $true }
if (CheckRegistryPath "HKLM:\SOFTWARE\CryptoWall*" ([ref]$OutputDetail)) { $CryptoWallFound = $true }
# I'm also adding searches under Wow6432Node in case it gets deployed as a 32-bat app on a 64-bit platform:
if (CheckRegistryPath "HKLM:\SOFTWARE\Wow6432Node\CryptoWall*" ([ref]$OutputDetail)) { $CryptoWallFound = $true }
if (CheckRegistryPath "HKCU:\Software\CryptoWall*" ([ref]$OutputDetail)) { $CryptoWallFound = $true }
# Note:  Not all keys in those articles are added here.  Some keys look like legitimate Windows settings 
#        that CryptoWall manipulates, e.g. turning off System Restore.  Some keys require checking values,
#        which PowerShell's Test-Path does not do.

if ($CryptoWallFound) {
    $ExitCode = 1001
else {
  $Status = "No CryptoWall files or registry keys found."
    $ExitCode = 0
#Print SummaryLine for display in monitoring system. Abbreviate date/time.
$Status + " [" + (Get-Date -Format "MM/dd HH:mm") + "]"

"`nLocal Machine Time:  " + (Get-Date -Format G)
"Exit Code: " + $ExitCode

Exit $ExitCode

Please use the lower comment form while we work out a formatting issue.

Leave a Reply

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


Notify me of followup comments via e-mail. You can also subscribe without commenting.