Category Archives: FSRM

SCCM – Automatically Protecting Windows File Servers From Ransomware Using FSRM

Hi folks,

Perhaps the title is a bit misleading.  This is by no means the only protection to potentially have for a file server at risk of ransomware.  Not even close!  This is just one option to work in conjunction with existing security and, as always, should be regularly revisited.

This was prepared in response to WannaCry as an additional layer of protection and I’ve meant to post it for a while and am just getting to it now.

First I will post the code then some explanation…

The Code:

####
# FSRM_POWERSHELL_FINAL.PS1
# Created By: Neal Britton
#
#
# Credit where credit is due: http://www.scconfigmgr.com/2017/03/21/protect-file-servers-from-ransomware-with-sccm-cicb/
# Credit where credit is due: https://fsrm.experiant.ca/
# Credit where credit is due: https://github.com/nexxai/CryptoBlocker
#
#
####

################################ USER CONFIGURATION ################################

# Names to use in FSRM
$fileGroupName = "CryptoBlockerGroup"
$fileTemplateName = "CryptoBlockerTemplate"
# set screening type to
# Active screening: Do not allow users to save unathorized files
$fileTemplateType = "Active"
# Passive screening: Allow users to save unauthorized files (use for monitoring)
#$fileTemplateType = "Passive"

################################ END USER CONFIGURATION ################################

################################ FUNCTIONS ################################

Function ConvertFrom-Json20
{
 # Deserializes JSON input into PowerShell object output
 Param (
 [Object] $obj
 )
 Add-Type -AssemblyName System.Web.Extensions
 $serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer
 return ,$serializer.DeserializeObject($obj)
}

Function New-CBArraySplit
{
 <# 
 Takes an array of file extensions and checks if they would make a string >1Kb, 
 if so, turns it into several arrays
 #>
 param(
 $Extensions
 )

$Extensions = $Extensions | Sort-Object -Unique

$workingArray = @()
 $WorkingArrayIndex = 1
 $LengthOfStringsInWorkingArray = 0

# Take the items from the input array and build up a 
 # temporary workingarray, tracking the length of the items in it and future commas
 $Extensions | ForEach-Object {

if (($LengthOfStringsInWorkingArray + 1 + $_.Length) -gt 1023) 
 { 
 # Adding this item to the working array (with +1 for a comma)
 # pushes the contents past the 1Kb limit
 # so output the workingArray
 [PSCustomObject]@{
 index = $WorkingArrayIndex
 FileGroupName = "$Script:FileGroupName$WorkingArrayIndex"
 array = $workingArray
 }
 
 # and reset the workingArray and counters
 $workingArray = @($_) # new workingArray with current Extension in it
 $LengthOfStringsInWorkingArray = $_.Length
 $WorkingArrayIndex++

}
 else #adding this item to the workingArray is fine
 {
 $workingArray += $_
 $LengthOfStringsInWorkingArray += (1 + $_.Length) #1 for imaginary joining comma
 }
 }

# The last / only workingArray won't have anything to push it past 1Kb
 # and trigger outputting it, so output that one as well
 [PSCustomObject]@{
 index = ($WorkingArrayIndex)
 FileGroupName = "$Script:FileGroupName$WorkingArrayIndex"
 array = $workingArray
 }
}

function CryptoBlocker
{

$SMTPServer = "server name"
 $SMTPFrom = "$env:COMPUTERNAME@domain name"
 $SMTPTo = "help desk email"
 $AdminEmail = "help desk email"

# Get all drives with shared folders, these drives will get FRSRM protection
 $DrivesContainingShares = @(Get-WmiObject Win32_Share | # all shares on this computer, filter:
 Where-Object { $_.Type -eq 0 } | # 0 = disk drives (not printers, IPC$, C$ Admin shares)
 Select-Object -ExpandProperty Path | # Shared folder path, e.g. "D:\UserFolders\"
 ForEach-Object { 
 ([System.IO.DirectoryInfo]$_).Root.Name # Extract the driveletter, as a string
 } | Sort-Object -Unique) # remove duplicates

if ($drivesContainingShares.Count -eq 0)
 {
 # No drives containing shares were found
 exit
 }

#Write-Host "`n####"
 #Write-Host "The following shares needing to be protected: $($drivesContainingShares -Join ",")"

# Identify Windows Server version, and install FSRM role
 $majorVer = [System.Environment]::OSVersion.Version.Major
 $minorVer = [System.Environment]::OSVersion.Version.Minor

#Write-Host "`n####"
 #Write-Host "Checking File Server Resource Manager.."

Import-Module ServerManager

if ($majorVer -eq 10)
 {
 $checkFSRM = Get-WindowsFeature -Name FS-Resource-Manager

if ($minorVer -ge 0 -and $checkFSRM.Installed -ne "True")
 {
 #Server 2016

$install = Install-WindowsFeature -Name FS-Resource-Manager -IncludeManagementTools
 if ($? -ne $True)
 {
 #Install of FSRM failed
 exit
 }
 }
 }
 elseif ($majorVer -eq 6)
 {
 $checkFSRM = Get-WindowsFeature -Name FS-Resource-Manager

if ($minorVer -ge 2 -and $checkFSRM.Installed -ne "True")
 {
 # Server 2012 or 2012 R2

$install = Install-WindowsFeature -Name FS-Resource-Manager -IncludeManagementTools
 if ($? -ne $True)
 {
 #Install of FSRM failed
 exit
 }
 }
 elseif ($minorVer -ge 1 -and $checkFSRM.Installed -ne "True")
 {
 # Server 2008 R2
 $install = Add-WindowsFeature FS-FileServer, FS-Resource-Manager
 if ($? -ne $True)
 {
 #Install of FSRM failed
 exit
 }
 
 }
 elseif ($checkFSRM.Installed -ne "True")
 {
 # Server 2008
 $install = &servermanagercmd -Install FS-FileServer FS-Resource-Manager
 if ($? -ne $True)
 {
 #Install of FSRM failed
 exit
 }
 }
 }
 else
 {
 # Assume Server 2003
 return
 }

# Download list of CryptoLocker file extensions
 $webClient = New-Object System.Net.WebClient
 $jsonStr = $webClient.DownloadString("https://fsrm.experiant.ca/api/v1/get")
 $monitoredExtensions = @(ConvertFrom-Json20 $jsonStr | ForEach-Object { $_.filters })

# Process SkipList.txt
 If (Test-Path .\SkipList.txt)
 {
 $Exclusions = Get-Content .\SkipList.txt | ForEach-Object { $_.Trim() }
 $monitoredExtensions = $monitoredExtensions | Where-Object { $Exclusions -notcontains $_ }

}
 Else 
 {
 #
 # Add one filescreen per line that you want to ignore
 #
 # For example, if *.doc files are being blocked by the list but you want 
 # to allow them, simply add a new line in this file that exactly matches 
 # the filescreen:
 #
 # *.doc
 #
 # The script will check this file every time it runs and remove these 
 # entries before applying the list to your FSRM implementation.
 #
 # In our environment we have a large number of false positives on *.one and WindowsApplication1.exe files
 #
 $emptyFile = @'
*.acc
*.one
WindowsApplication1.exe
'@
 Set-Content -Path .\SkipList.txt -Value $emptyFile
 }

# Split the $monitoredExtensions array into fileGroups of less than 4kb to allow processing by filescrn.exe
 $fileGroups = @(New-CBArraySplit $monitoredExtensions)
 $screenArgs = @()

# Perform these steps for each of the 1KB limit split fileGroups
 ForEach ($group in $fileGroups)
 {
 $FGExists = Get-FsrmFileGroup | Where-Object { $_.Name -eq "$($group.fileGroupName)" }
 if ($FGExists -gt $null)
 {
 Remove-FSRMFileGroup -name $($group.fileGroupName) -Confirm:$false | Out-Null
 }
 
 New-FSRMFileGroup -name $($group.fileGroupName) -IncludePattern $($group.array) -Description "Crypto Extension Detection" | Out-Null

$screenArgs += $($group.fileGroupName)
 }

# Create File Screen Template with Notification
 # Remove existing if found and create new
 $FSTExists = Get-FsrmFileScreenTemplate | Where-Object { $_.Name -eq "CryptoExtensions" }

if ($FSTExists -gt $null)
 {
 Remove-FsrmFileScreenTemplate -Name CryptoExtensions -Confirm:$false | Out-Null
 }

$screenArgList = [String[]]@($screenArgs)

$EmailNotification = New-FSRMAction -Type Email -Subject "Crypto File Activity Detected - $env:COMPUTERNAME" -Body "User [Source IO Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in violation of the [Violated File Group] file group. This file could be a marker for malware infection, and should be investigated immediately." -RunLimitInterval 30 -MailTo $SMTPTo
 $EventNotification = New-FSRMAction -Type Event -EventType Error -Body "User [Source IO Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in violation of the [Violated File Group] file group. This file could be a marker for malware infection, and should be investigated immediately." -RunLimitInterval 30

New-FsrmFileScreenTemplate -Name CryptoExtensions -Description "Known CryptoLocker File Extesions" -IncludeGroup($($screenArgList)) -Active:$true -Notification $EmailNotification,$EventNotification | Out-Null


# Create File Screens for every drive containing shares
 $drivesContainingShares | ForEach-Object {
 # Skip C:
 # All actual shares on our File Servers are on other drives. We only care about these.
 if ($_ -ne "C:\")
 {
 $drive = $_

# Remove File Screen is exists and create new
 $FSExists = Get-FsrmFileScreen | Where-Object { $_.Path -eq $drive }
 
 if ($FSExists -gt $null)
 {
 Remove-FsrmFileScreen -Path $_ -Confirm:$false | Out-Null
 }
 
 New-FSRMFileScreen -Path $_ -Active: $true -Description "Crypto Extension Monitoring" -Template CryptoExtensions -Notification $EmailNotification,$EventNotification | Out-Null
 }
 }

# Check for FSRM File Screen and report TRUE or FALSE for Configuration Item/Baseline Reporting
 $CryptoScreen = Get-FSRMFileScreen | Where-Object { $_.Description -eq "Crypto Extension Monitoring" }
 
 if ($CryptoScreen -gt $null)
 {
 $CryptoCICompliant = $true
 }
 else
 {
 $CryptoCICompliant = $false
 }
 Return $CryptoCICompliant
}

################################ END FUNCTIONS ################################

CryptoBlocker

 

The Explanation:

So, the above is just my implementation after finding similar items and expanding on them to meet my need.  It is not comprehensive and should definitely not stand alone.

Pros of this approach:

  • Automatic, just add a server to be protected to the collection this is deployed to
  • Can be regularly updated
  • The source list of ransomware files/extensions is somewhat trustworthy(?)
  • The protection groups are removed before they are rebuilt to ensure protection lists are up to date
  • Fast
  • No manual changes

Cons:

  • The source list is somewhat trustworthy
  • Is updated by manual submissions
  • Code has to be edited to exclude a certain file name or file extension (but only has to be done in one place)

 

The Implementation:

In your SCCM console in the Assets and Compliance work space, expand ‘Compliance Settings’ and in ‘Configuration Items’ create a new configuration item:

  1. In my example, give it a name of “CryptoBlocker FSRM Script” and hit next
  2. In Support Platforms, deselect all and the manually select your server platforms.  for me it was Server 2012, Server 2012 R2, and Server 2016
  3. Create a new setting
    1. Name = “CryptoBlocker FSRM Script”
    2. Setting Type: Script
    3. Data Type: Boolean
    4. Discovery Script (Powershell): Use above script
  4. Create a new compliance rule
    1. Name = “Crypto FSRM Protection
    2. Selected setting = The one we just created
    3. Set value returned by the specified script to equals true
    4. Check to enable “Remote noncompliance if this setting instance is not found”

Next, in Configuration Baselines, create a new baseline:

  1. Name = “Crypto File Block”
  2. Evaluation Conditions: Add the Configuration Item we created earlier

Deploy to a test collection first!  Pictures of the finished configuration item are as follows:

fsrm_ci1fsrm_ci2fsrm_ci3fsrm_ci4fsrm_ci5