From e188748724fd83d6dc901e8a46631ca563de867c Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Fri, 30 Sep 2016 12:11:19 -0600 Subject: [PATCH 01/33] Updated script to only include filescreen creation --- DeployCryptoBlocker.ps1 | 137 ---------------------------------------- 1 file changed, 137 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 07fe111..42db3a9 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -11,26 +11,6 @@ ################################ Functions ################################ -Function PurgeNonAdminDirectoryPermissions([string] $directory) -{ - $acl = Get-Acl $directory - - if ($acl.AreAccessRulesProtected) - { - $acl.Access | % { $acl.PurgeAccessRules($_.IdentityReference) } - } - else - { - $acl.SetAccessRuleProtection($true, $true) - } - - $ar = New-Object System.Security.AccessControl.FileSystemAccessRule("SYSTEM","FullControl","Allow") - $acl.AddAccessRule($ar) - $ar = $ar = New-Object System.Security.AccessControl.FileSystemAccessRule("BUILTIN\Administrators","FullControl","Allow") - $acl.AddAccessRule($ar) - Set-Acl -AclObject $acl -Path $directory -} - function ConvertFrom-Json20([Object] $obj) { Add-Type -AssemblyName System.Web.Extensions @@ -142,118 +122,6 @@ ForEach ($group in $fileGroups) { $group | Add-Member -MemberType NoteProperty -Name fileGroupName -Value "$FileGroupName$($group.index)" } -$scriptFilename = "C:\FSRMScripts\KillUserSession.ps1" -$batchFilename = "C:\FSRMScripts\KillUserSession.bat" -$eventConfFilename = "$env:Temp\cryptoblocker-eventnotify.txt" -$cmdConfFilename = "$env:Temp\cryptoblocker-cmdnotify.txt" - -$scriptConf = @' -param([string] $DomainUser) - -Function DenySharePermission ([string] $ShareName, [string] $DomainUser) -{ - $domainUserSplit = $DomainUser.Split("\") - - $trusteeClass = [wmiclass] "ROOT\CIMV2:Win32_Trustee" - $trustee = $trusteeClass.CreateInstance() - $trustee.Domain = $domainUserSplit[0] - $trustee.Name = $domainUserSplit[1] - - $aceClass = [wmiclass] "ROOT\CIMV2:Win32_ACE" - $ace = $aceClass.CreateInstance() - $ace.AccessMask = 2032127 - $ace.AceType = 1 - $ace.Trustee = $trustee - - $shss = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter "Name='$ShareName'" - $sd = Invoke-WmiMethod -InputObject $shss -Name GetSecurityDescriptor | Select -ExpandProperty Descriptor - - $sclass = [wmiclass] "ROOT\CIMV2:Win32_SecurityDescriptor" - $newsd = $sclass.CreateInstance() - $newsd.ControlFlags = $sd.ControlFlags - - foreach ($oace in $sd.DACL) - { - $newsd.DACL += [System.Management.ManagementBaseObject] $oace - } - - $newsd.DACL += [System.Management.ManagementBaseObject] $ace - - $share = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter "Name='$ShareName'" - $setResult = $share.SetSecurityDescriptor($newsd) - - return $setResult.ReturnValue -} - - -# Let's try altering share permissions.. -$Username = $DomainUser.Split("\")[1] - -$affectedShares = Get-WmiObject -Class Win32_Share | - Select Name, Path, Type | - Where { $_.Type -eq 0 } - -$affectedShares | % { - Write-Host "Denying [$DomainUser] access to share [$($_.Name)].." - DenySharePermission -ShareName $_.Name -DomainUser $DomainUser -} - -Write-Host $affectedShares -'@ - -$batchConf = @" -@echo off -powershell.exe -ExecutionPolicy Bypass -File "$scriptFilename" -DomainUser %1 -"@ - -$scriptDirectory = Split-Path -Parent $scriptFilename -$batchDirectory = Split-Path -Parent $batchFilename - -if (-not (Test-Path $scriptDirectory)) -{ - Write-Host "Script directory [$scriptDirectory] not found. Creating.." - New-Item -Path $scriptDirectory -ItemType Directory -} - -if (-not (Test-Path $batchDirectory)) -{ - Write-Host "Batch directory [$batchDirectory] not found. Creating.." - New-Item -Path $batchDirectory -ItemType Directory -} - -# FSRM stipulates that the command directories/files can only be accessible by SYSTEM or Administrators -# As a result, we lock down permissions for SYSTEM and local admin only -Write-Host "Purging Non-Admin NTFS permissions on script directory [$scriptDirectory].." -PurgeNonAdminDirectoryPermissions($scriptDirectory) -Write-Host "Purging Non-Admin NTFS permissions on batch directory [$batchDirectory].." -PurgeNonAdminDirectoryPermissions($batchDirectory) - -Write-Host "Writing defensive PowerShell script to location [$scriptFilename].." -$scriptConf | Out-File -Encoding ASCII $scriptFilename -Write-Host "Writing batch script launcher to location [$batchFilename].." -$batchConf | Out-File -Encoding ASCII $batchFilename - -$eventConf = @" -Notification=E -RunLimitInterval=0 -EventType=Warning -Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server. An attempt has been made at blocking this user. -"@ - -$cmdConf = @" -Notification=C -RunLimitInterval=0 -Command=$batchFilename -Arguments=[Source Io Owner] -MonitorCommand=Enable -Account=LocalSystem -"@ - -Write-Host "Writing temporary FSRM Event Viewer configuration to location [$eventConfFilename].." -$eventConf | Out-File $eventConfFilename -Write-Host "Writing temporary FSRM Command configuration to location [$cmdConfFilename].." -$cmdConf | Out-File $cmdConfFilename - # Perform these steps for each of the 4KB limit split fileGroups ForEach ($group in $fileGroups) { Write-Host "Adding/replacing File Group [$($group.fileGroupName)] with monitored file [$($group.array -Join ",")].." @@ -277,8 +145,3 @@ $drivesContainingShares | % { &filescrn.exe Screen Delete "/Path:$_" /Quiet &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" } - -Write-Host "Removing temporary FSRM Event Viewer configuration file [$eventConfFilename].." -Write-Host "Removing temporary FSRM Event Viewer configuration file [$cmdConfFilename].." -Remove-Item $eventConfFilename -Remove-Item $cmdConfFilename From 6e858ff97faec8d5316c91b48bd44174d347f827 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Fri, 30 Sep 2016 12:11:59 -0600 Subject: [PATCH 02/33] Unnecessary for creating filescreen --- BlockFileExtensionsGPO.ps1 | 154 ------------------------------------- 1 file changed, 154 deletions(-) delete mode 100644 BlockFileExtensionsGPO.ps1 diff --git a/BlockFileExtensionsGPO.ps1 b/BlockFileExtensionsGPO.ps1 deleted file mode 100644 index 1b8bde0..0000000 --- a/BlockFileExtensionsGPO.ps1 +++ /dev/null @@ -1,154 +0,0 @@ -# -------------------------------------------------- -# BlockFileExtensionsGPO.ps1 -# -------------------------------------------------- -# This script creates a GPO to block certain questionable file extensions using a software restriction policy -# GPO Name and extensions to block can be modified below. By default, the GPO is linked to the domain - -$blockedFileExtensions = "VBS,JS,COM,BAT,SCR,PIF" - -Import-Module ActiveDirectory - -Function ConvertTo-WmiFilter([Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject) -{ - $gpDomain = New-Object -Type Microsoft.GroupPolicy.GPDomain - $ADObject | ForEach-Object { - $path = 'MSFT_SomFilter.Domain="' + $gpDomain.DomainName + '",ID="' + $_.Name + '"' - - try - { - $filter = $gpDomain.GetWmiFilter($path) - } - catch { } - - if ($filter) - { - [Guid]$guid = $_.Name.Substring(1, $_.Name.Length - 2) - $filter | Add-Member -MemberType NoteProperty -Name Guid -Value $Guid -PassThru | Add-Member -MemberType NoteProperty -Name Content -Value $_."msWMI-Parm2" -PassThru - } - } -} - -Function New-SoftwareRestrictionGPO($GpoName, $ParanoidExtensions, $WmiFilter) -{ - Set-StrictMode -Version 2 - - # Just in case GPMC modules are missing.. - Import-Module ServerManager - Add-WindowsFeature GPMC - Import-Module GroupPolicy - - $existingGpo = Get-GPO -Name $GpoName - - if ($existingGPO -ne $null) - { - Remove-GPO -Name $GpoName - } - - $newGPO = New-GPO -Name $GpoName - - $newGPO.WmiFilter = $WmiFilter - - $nLevel = 0 - $settingsKey = "HKLM\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers" - - $fileTimeNow = (Get-Date).ToFileTime() - - # Set global parameters - Set-GPRegistryValue -Name $GpoName -Key "$settingsKey" ` - -Type MultiString -ValueName "ExecutableTypes" -Value "" | Out-Null - - Set-GPRegistryValue -Name $GpoName -Key "$settingsKey" ` - -Type DWord -ValueName "DefaultLevel" -Value 262144 | Out-Null - - Set-GPRegistryValue -Name $GpoName -Key "$settingsKey" ` - -Type DWord -ValueName "PolicyScope" -Value 0 | Out-Null - - Set-GPRegistryValue -Name $GpoName -Key "$settingsKey" ` - -Type DWord -ValueName "TransparentEnabled" -Value 1 | Out-Null - - Set-GPRegistryValue -Name $GpoName -Key "$settingsKey" ` - -Type DWord -ValueName "AuthenticodeEnabled" -Value 0 | Out-Null - - Set-GPRegistryValue -Name $GpoName -Key "$settingsKey" ` - -Type QWord -ValueName "LastModified" -Value $fileTimeNow | Out-Null - - $ParanoidExtensionsSplit = $ParanoidExtensions.Split(",") - foreach ($paranoidExtension in $ParanoidExtensionsSplit) - { - $newPathGUID = [System.Guid]::NewGuid() - $newPathGUID = "{$newPathGUID}" - - Set-GPRegistryValue -Name $GpoName -Key "$settingsKey\$nLevel\Paths\$newPathGUID" ` - -Type String -ValueName "ItemData" -Value "*.$paranoidExtension" | Out-Null - - Set-GPRegistryValue -Name $GpoName -Key "$settingsKey\$nLevel\Paths\$newPathGUID" ` - -Type DWord -ValueName "SaferFlags" -Value 0 | Out-Null - - Set-GPRegistryValue -Name $GpoName -Key "$settingsKey\$nLevel\Paths\$newPathGUID" ` - -Type QWord -ValueName "LastModified" -Value $fileTimeNow | Out-Null - } - - $domain = (Get-ADDomain).DistinguishedName - New-GPLink -Name "$GpoName" -Target "$domain" -} - -Function New-WMIFilter($FilterName, $FilterDescription, $FilterNamespace, $FilterExpression) -{ - $guid = [System.Guid]::NewGuid() - $defaultNamingContext = (Get-ADRootDSE).DefaultNamingContext - $msWMIAuthor = (Get-ADUser $env:USERNAME).UserPrincipleName - $msWMICreationDate = (Get-Date).ToUniversalTime().ToString("yyyyMMddhhmmss.ffffff-000") - $wmiGUID = "{$guid}" - $wmiDistinguishedName = "CN=$wmiGUID,CN=SOM,CN=WMIPolicy,CN=System,$defaultNamingContext" - $msWMIParm1 = "$FilterDescription " - $msWMIParm2 = $FilterExpression.Count.ToString() + ";" - - $FilterExpression | ForEach-Object { - $msWMIParm2 += "3;" + $FilterNamespace.Length + ";" + $_.Length + ";WQL;" + $FilterNamespace + ";" + $_ + ";" - } - - $existingWmiFilter = Get-ADObject -Filter 'objectClass -eq "msWMI-Som"' -Properties "msWMI-Name", "msWMI-Parm1" | Where-Object { $_."msWMI-Name" -eq $FilterName } | Select -First 1 - - - if ($existingWmiFilter -ne $null) - { - Remove-ADObject -Identity $existingWmiFilter -Confirm:$false - } - - $attributes = @{ - "msWMI-Name" = $FilterName; - "msWMI-Parm1" = $msWMIParm1; - "msWMI-Parm2" = $msWMIParm2; - "msWMI-Author" = $msWMIAuthor; - "instanceType" = 4; - "msWMI-ID" = $wmiGUID; - "showInAdvancedViewOnly" = "TRUE"; - "distinguishedname" = $wmiDistinguishedName; - "msWMI-ChangeDate" = $msWMICreationDate; - "msWMI-CreationDate" = $msWMICreationDate; - } - - $wmiPath = ("CN=SOM,CN=WMIPolicy,CN=System,$defaultNamingContext") - - $adObject = New-ADObject -Name $wmiGUID -Type "msWMI-Som" -Path $wmiPath -OtherAttributes $attributes -PassThru - - ConvertTo-WmiFilter $adObject | Write-Output -} - -$workstationFilterName = "Workstations" -$2K3TSFilterName = "2003 Terminal Servers" -$2K8TSFilterName = "2008+ Terminal Servers" - -# Create WMI filters for workstations, and 200 -$workstationFilter = New-WMIFilter -FilterName $workstationFilterName -FilterDescription "Filter on workstations" ` --FilterNamespace "ROOT\CIMV2" -FilterExpression "SELECT * FROM Win32_ComputerSystem WHERE DomainRole = 0 OR DomainRole = 1" - -$2K3TSFilter = New-WMIFilter -FilterName $2K3TSFilterName -FilterDescription "Filter on 2003 terminal servers" ` --FilterNamespace 'ROOT\CIMV2' -FilterExpression 'SELECT * FROM Win32_TerminalServiceSetting WHERE LicensingType > 1' - -$2K8TSFilter = New-WMIFilter -FilterName $2K8TSFilterName -FilterDescription "Filter on 2008+ terminal servers" ` --FilterNamespace 'ROOT\CIMV2\TerminalServices' -FilterExpression 'SELECT * FROM Win32_TerminalServiceSetting WHERE LicensingType > 1' - -New-SoftwareRestrictionGPO -GpoName "Block File Extensions - Workstations" -ParanoidExtensions $blockedFileExtensions -WmiFilter $workstationFilter -New-SoftwareRestrictionGPO -GpoName "Block File Extensions - 2K3 TS" -ParanoidExtensions $blockedFileExtensions -WmiFilter $2K3TSFilter -New-SoftwareRestrictionGPO -GpoName "Block File Extensions - 2K8+ TS" -ParanoidExtensions $blockedFileExtensions -WmiFilter $2K8TSFilter From c6dc8ffd55edf0b67f9202b98e933669239b9748 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Fri, 30 Sep 2016 14:54:06 -0600 Subject: [PATCH 03/33] Removing broken notification code --- DeployCryptoBlocker.ps1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 42db3a9..213277e 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -136,8 +136,6 @@ $screenArgs = 'Template','Add',"/Template:$fileTemplateName" ForEach ($group in $fileGroups) { $screenArgs += "/Add-Filegroup:$($group.fileGroupName)" } -$screenArgs += "/Add-Notification:E,$eventConfFilename","/Add-Notification:C,$cmdConfFilename",'/Type:Passive' -&filescrn.exe $screenArgs Write-Host "Adding/replacing File Screens.." $drivesContainingShares | % { From 7b33ea39c6b35a56449fd5ef4ff6ea442dd05c13 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Fri, 30 Sep 2016 17:32:40 -0600 Subject: [PATCH 04/33] Removed too many lines; putting this one back --- DeployCryptoBlocker.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 213277e..f15da0d 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -137,6 +137,8 @@ ForEach ($group in $fileGroups) { $screenArgs += "/Add-Filegroup:$($group.fileGroupName)" } +&filescrn.exe $screenArgs + Write-Host "Adding/replacing File Screens.." $drivesContainingShares | % { Write-Host "`tAdding/replacing File Screen for [$_] with Source Template [$fileTemplateName].." From c6f7f4327dd1d5631cd953bd36b7b064fb5ed6d2 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Thu, 6 Oct 2016 14:06:19 -0600 Subject: [PATCH 05/33] Removed notes since they didn't apply to our version --- DeployCryptoBlocker.ps1 | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index f15da0d..b9d5b2f 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -1,14 +1,5 @@ -# DeployCryptoBlocker.ps1 +# DeployCryptoBlocker.ps1 # -# This script performs the following actions: -# 1) Checks for network shares -# 2) Install File Server Resource Manager (FSRM) if missing -# 3) Creates Batch and PowerShell scripts used by FSRM -# 4) Creates a File Group within FSRM containing malicious extensions to screen on -# 5) Creates a File Screen Template utilising this File Group, with an Event notification and Command notification -# to run the scripts created in Step 3) -# 6) Creates File Screens utilising this template for each drive containing network shares - ################################ Functions ################################ function ConvertFrom-Json20([Object] $obj) From c744192fcde8626ae71e61b0943d9a40325dde8d Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Thu, 23 Feb 2017 11:37:01 -0700 Subject: [PATCH 06/33] Added exclusion code --- DeployCryptoBlocker.ps1 | 7 +++++++ SkipList.txt | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 SkipList.txt diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index b9d5b2f..2d0e415 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -107,6 +107,13 @@ $webClient = New-Object System.Net.WebClient $jsonStr = $webClient.DownloadString("https://fsrm.experiant.ca/api/v1/get") $monitoredExtensions = @(ConvertFrom-Json20($jsonStr) | % { $_.filters }) +$exclusions = Get-Content .\SkipList.txt +ForEach ($exclusion in $exclusions) { + if ($exclusion) { + $monitoredExtensions = $monitoredExtensions.replace($exclusion.trim(),$null) + } +} + # Split the $monitoredExtensions array into fileGroups of less than 4kb to allow processing by filescrn.exe $fileGroups = New-CBArraySplit $monitoredExtensions ForEach ($group in $fileGroups) { diff --git a/SkipList.txt b/SkipList.txt new file mode 100644 index 0000000..cd05905 --- /dev/null +++ b/SkipList.txt @@ -0,0 +1,13 @@ +# +# 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. +# + From bccdbca93b8bf8fe1d7a7715aad77b479a5b6ccf Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Thu, 23 Feb 2017 11:41:56 -0700 Subject: [PATCH 07/33] Update README.md with instructions about SkipList.txt --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 84c53b7..6a16ce8 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ The script will install File Server Resource Manager (FSRM), and set up the rele Script Deployment Steps +Before running, please add any known good file extensions used in your environment to SkipList.txt, one per line. This will ensure that if a filescreen is added to the list in the future that blocks that specific file extension, your environment won't be affected as they will be automatically removed. + 1. Checks for network shares 2. Installs FSRM 3. Create batch/PowerShell scripts used by FSRM From ab9570c2cdae2c5354247290cf84cdbb2d5836e3 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Thu, 23 Feb 2017 11:57:20 -0700 Subject: [PATCH 08/33] Updated script to dynamically create SkipList to avoid over-writing in future --- DeployCryptoBlocker.ps1 | 33 +++++++++++++++++++++++++++++---- SkipList.txt | 13 ------------- 2 files changed, 29 insertions(+), 17 deletions(-) delete mode 100644 SkipList.txt diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 2d0e415..d7e17ef 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -107,12 +107,37 @@ $webClient = New-Object System.Net.WebClient $jsonStr = $webClient.DownloadString("https://fsrm.experiant.ca/api/v1/get") $monitoredExtensions = @(ConvertFrom-Json20($jsonStr) | % { $_.filters }) -$exclusions = Get-Content .\SkipList.txt -ForEach ($exclusion in $exclusions) { - if ($exclusion) { - $monitoredExtensions = $monitoredExtensions.replace($exclusion.trim(),$null) + +If (TestPath .\SkipList.txt) { + $exclusions = Get-Content .\SkipList.txt + ForEach ($exclusion in $exclusions) { + if ($exclusion) { + $monitoredExtensions = $monitoredExtensions.replace($exclusion.trim(),$null) + } } } +Else +{ + $emptyFile = ` +` +"# +# 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. +# + +" + Add-Content .\SkipList.txt $emptyFile + +} + # Split the $monitoredExtensions array into fileGroups of less than 4kb to allow processing by filescrn.exe $fileGroups = New-CBArraySplit $monitoredExtensions diff --git a/SkipList.txt b/SkipList.txt deleted file mode 100644 index cd05905..0000000 --- a/SkipList.txt +++ /dev/null @@ -1,13 +0,0 @@ -# -# 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. -# - From 125e95d90f46d98139cc56e878c639c788f08cf3 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Thu, 23 Feb 2017 11:59:28 -0700 Subject: [PATCH 09/33] Added SkipList.txt file to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..55bf58e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SkipList.txt \ No newline at end of file From 511b8495b742a8430851c794410f52de1c226ee1 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Fri, 24 Feb 2017 09:00:46 -0700 Subject: [PATCH 10/33] Fixed "TestPath" to "Test-Path" --- DeployCryptoBlocker.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index d7e17ef..3050580 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -108,7 +108,7 @@ $jsonStr = $webClient.DownloadString("https://fsrm.experiant.ca/api/v1/get") $monitoredExtensions = @(ConvertFrom-Json20($jsonStr) | % { $_.filters }) -If (TestPath .\SkipList.txt) { +If (Test-Path .\SkipList.txt) { $exclusions = Get-Content .\SkipList.txt ForEach ($exclusion in $exclusions) { if ($exclusion) { From 44462f5cdc60d6b13e1a464213809585a2f2283f Mon Sep 17 00:00:00 2001 From: HumanEquivalentUnit Date: Fri, 24 Feb 2017 16:42:09 +0000 Subject: [PATCH 11/33] Update DeployCryptoBlocker.ps1 (#3) * Update DeployCryptoBlocker.ps1 One major change and some minor tweaks pull request. - New-CBArraySplit does string splitting and joining to test the 4Kb limit, but file extensions can have commas in them so it risks splitting in the middle of a file extension and breaking. Reworked it to build up a temporary array on the fly, checking the extra string length each time won't push the array over the 4Kb limit. Updated it to output to the pipeline. Merged the FileGroupName property into it. - Normalise ConvertFrom-Json20 to use Param() block. Added comment. - Updated the Get-WmiObject call for drives with shares, formatting, added comments, does the share root with a DirectoryInfoCast which won't error if the item doesn't exist, and calls te Name property instead of string interpolation, forced it to be an array with @() and removed the following $null test. - Windows version comment, tweaked "other version" message. - ConvertFrom-Json20 call was using parens, which isn't PowerShell function call syntax. Changed, and expanded ForEach-Object alias in that line. - Reworked SkipList to do the trim() call when loading and use array -notcontains to remove the exclusions. - Changed the emtpy file message to use a here-string instead of backtick continuations. - Updated the call to New-CBArraySplit - Expanded % alias at end of file. * Fix two typos Selec-Object corrected to Select-Object in Get-WmiObject lines. SplitPath corrected to Split-Path in SkipList.txt code. --- DeployCryptoBlocker.ps1 | 141 +++++++++++++++++++++++----------------- 1 file changed, 82 insertions(+), 59 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 3050580..1dc95e3 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -2,59 +2,87 @@ # ################################ Functions ################################ -function ConvertFrom-Json20([Object] $obj) +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 { - +Function New-CBArraySplit +{ + <# + Takes an array of file extensions and checks if they would make a string >4Kb, + if so, turns it into several arrays + #> param( - $extArr, - $depth = 1 + $Extensions ) - $extArr = $extArr | Sort-Object -Unique - - # Concatenate the input array - $conStr = $extArr -join ',' - $outArr = @() - - # If the input string breaks the 4Kb limit - If ($conStr.Length -gt 4096) { - # Pull the first 4096 characters and split on comma - $conArr = $conStr.SubString(0,4096).Split(',') - # Find index of the last guaranteed complete item of the split array in the input array - $endIndex = [array]::IndexOf($extArr,$conArr[-2]) - # Build shorter array up to that indexNumber and add to output array - $shortArr = $extArr[0..$endIndex] - $outArr += [psobject] @{ - index = $depth - array = $shortArr - } + $Extensions = $Extensions | Sort-Object -Unique + + $workingArray = @() + $WorkingArrayIndex = 1 + $LengthOfStringsInWorkingArray = 0 + + # TODO - is the FSRM limit for bytes or characters? + # maybe [System.Text.Encoding]::UTF8.GetBytes($_).Count instead? + # -> in case extensions have Unicode characters in them + # and the character Length is <4Kb but the byte count is >4Kb + + # 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 4096) + { + # Adding this item to the working array (with +1 for a comma) + # pushes the contents past the 4Kb 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++ - # Then call this function again to split further - $newArr = $extArr[($endindex + 1)..($extArr.Count -1)] - $outArr += New-CBArraySplit $newArr -depth ($depth + 1) - - return $outArr + } + else #adding this item to the workingArray is fine + { + $workingArray += $_ + $LengthOfStringsInWorkingArray += (1 + $_.Length) #1 for imaginary joining comma + } } - # If the concat string is less than 4096 characters already, just return the input array - Else { - return [psobject] @{ - index = $depth - array = $extArr - } + + # The last / only workingArray won't have anything to push it past 4Kb + # and trigger outputting it, so output that one as well + [PSCustomObject]@{ + index = ($WorkingArrayIndex) + FileGroupName = "$Script:FileGroupName$WorkingArrayIndex" + array = $workingArray } } ################################ Functions ################################ -# Add to all drives -$drivesContainingShares = Get-WmiObject Win32_Share | Select Name,Path,Type | Where-Object { $_.Type -eq 0 } | Select -ExpandProperty Path | % { "$((Get-Item -ErrorAction SilentlyContinue $_).Root)" } | Select -Unique -if ($drivesContainingShares -eq $null -or $drivesContainingShares.Length -eq 0) +# 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) { Write-Host "No drives containing shares were found. Exiting.." exit @@ -62,6 +90,8 @@ if ($drivesContainingShares -eq $null -or $drivesContainingShares.Length -eq 0) 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 @@ -95,32 +125,30 @@ if ($majorVer -ge 6) else { # Assume Server 2003 - Write-Host "Other version of Windows detected! Quitting.." + Write-Host "Unsupported version of Windows detected! Quitting.." return } + $fileGroupName = "CryptoBlockerGroup" $fileTemplateName = "CryptoBlockerTemplate" $fileScreenName = "CryptoBlockerScreen" +# 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) | % { $_.filters }) +$monitoredExtensions = @(ConvertFrom-Json20 $jsonStr | ForEach-Object { $_.filters }) +If (Test-Path .\SkipList.txt) +{ + $Exclusions = Get-Content .\SkipList.txt | ForEach-Object { $_.Trim() } + $monitoredExtensions = $monitoredExtensions | Where-Object { $Exclusions -notcontains $_ } -If (Test-Path .\SkipList.txt) { - $exclusions = Get-Content .\SkipList.txt - ForEach ($exclusion in $exclusions) { - if ($exclusion) { - $monitoredExtensions = $monitoredExtensions.replace($exclusion.trim(),$null) - } - } } Else { - $emptyFile = ` -` -"# + $emptyFile = @' +# # Add one filescreen per line that you want to ignore # # For example, if *.doc files are being blocked by the list but you want @@ -132,18 +160,13 @@ Else # The script will check this file every time it runs and remove these # entries before applying the list to your FSRM implementation. # - -" - Add-Content .\SkipList.txt $emptyFile - +'@ + 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 -ForEach ($group in $fileGroups) { - $group | Add-Member -MemberType NoteProperty -Name fileGroupName -Value "$FileGroupName$($group.index)" -} +$fileGroups = @(New-CBArraySplit $monitoredExtensions) # Perform these steps for each of the 4KB limit split fileGroups ForEach ($group in $fileGroups) { @@ -155,7 +178,7 @@ ForEach ($group in $fileGroups) { Write-Host "Adding/replacing File Screen Template [$fileTemplateName] with Event Notification [$eventConfFilename] and Command Notification [$cmdConfFilename].." &filescrn.exe Template Delete /Template:$fileTemplateName /Quiet # Build the argument list with all required fileGroups -$screenArgs = 'Template','Add',"/Template:$fileTemplateName" +$screenArgs = 'Template', 'Add', "/Template:$fileTemplateName" ForEach ($group in $fileGroups) { $screenArgs += "/Add-Filegroup:$($group.fileGroupName)" } @@ -163,7 +186,7 @@ ForEach ($group in $fileGroups) { &filescrn.exe $screenArgs Write-Host "Adding/replacing File Screens.." -$drivesContainingShares | % { +$drivesContainingShares | ForEach-Object { Write-Host "`tAdding/replacing File Screen for [$_] with Source Template [$fileTemplateName].." &filescrn.exe Screen Delete "/Path:$_" /Quiet &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" From 9fef9bbd0b39c68def893eb916922d487fcc46e3 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Fri, 24 Feb 2017 12:52:00 -0700 Subject: [PATCH 12/33] Added email and event log setup --- DeployCryptoBlocker.ps1 | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 1dc95e3..dc1c148 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -185,9 +185,34 @@ ForEach ($group in $fileGroups) { &filescrn.exe $screenArgs +# Create a random string of 5 ASCII characters for temporary file names +$RandomString = -Join ((65..90) + (97..122) | Get-Random -Count 5 | % {[char]$_}) + +If (Test-Path .\EmailNotification$RandomString.txt) { + Remove-Item .\EmailNotification$RandomString.txt +} +If (Test-Path .\EventNotification$RandomString.txt) { + Remove-Item .\EventNotification$RandomString.txt +} + +# Write the email options to the temporary file +"Notification=m" >> .\EmailNotification$RandomString.txt +"To=[Admin Email]" >> .\EmailNotification$RandomString.txt +"Subject=Unauthorized file from the [Violated File Group] file group detected" >> .\EmailNotification.txt +"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> .\EmailNotification$RandomString.txt + +# Write the event log options to the temporary file +"Notification=e" >> .\EventNotification$RandomString.txt +"EventType=Warning" >> .\EventNotification$RandomString.txt +"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> .\EventNotification$RandomString.txt + Write-Host "Adding/replacing File Screens.." $drivesContainingShares | ForEach-Object { Write-Host "`tAdding/replacing File Screen for [$_] with Source Template [$fileTemplateName].." &filescrn.exe Screen Delete "/Path:$_" /Quiet - &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" + &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" /Add-Notification:m,EmailNotification$RandomString.txt /Add-Notification:e,EventNotification$RandomString.txt } + +# Remote the temporary files +Remove-Item .\EmailNotification$RandomString.txt +Remove-Item .\EventNotification$RandomString.txt \ No newline at end of file From 02269aca3a96e91c339d6534ea56522a5fa7198d Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Fri, 24 Feb 2017 13:07:46 -0700 Subject: [PATCH 13/33] Note about SkipList.txt --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a16ce8..072168c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The script will install File Server Resource Manager (FSRM), and set up the rele Script Deployment Steps -Before running, please add any known good file extensions used in your environment to SkipList.txt, one per line. This will ensure that if a filescreen is added to the list in the future that blocks that specific file extension, your environment won't be affected as they will be automatically removed. +Before running, please add any known good file extensions used in your environment to SkipList.txt, one per line. This will ensure that if a filescreen is added to the list in the future that blocks that specific file extension, your environment won't be affected as they will be automatically removed. If SkipList.txt does not exist, it will be created automatically. 1. Checks for network shares 2. Installs FSRM From bf4630002e4e73d0bab75056299356c8aab0e63d Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Fri, 24 Feb 2017 13:25:56 -0700 Subject: [PATCH 14/33] Changed temp file creation to use New-TemporaryFile --- DeployCryptoBlocker.ps1 | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index dc1c148..edb2f72 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -185,34 +185,26 @@ ForEach ($group in $fileGroups) { &filescrn.exe $screenArgs -# Create a random string of 5 ASCII characters for temporary file names -$RandomString = -Join ((65..90) + (97..122) | Get-Random -Count 5 | % {[char]$_}) - -If (Test-Path .\EmailNotification$RandomString.txt) { - Remove-Item .\EmailNotification$RandomString.txt -} -If (Test-Path .\EventNotification$RandomString.txt) { - Remove-Item .\EventNotification$RandomString.txt -} +$EmailNotification = New-TemporaryFile +$EventNotification = New-TemporaryFile # Write the email options to the temporary file -"Notification=m" >> .\EmailNotification$RandomString.txt -"To=[Admin Email]" >> .\EmailNotification$RandomString.txt -"Subject=Unauthorized file from the [Violated File Group] file group detected" >> .\EmailNotification.txt -"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> .\EmailNotification$RandomString.txt +"Notification=m" >> $EmailNotification.FullName +"To=[Admin Email]" >> $EmailNotification.FullName +"Subject=Unauthorized file from the [Violated File Group] file group detected" >> $EmailNotification.FullName +"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> $EmailNotification.FullName # Write the event log options to the temporary file -"Notification=e" >> .\EventNotification$RandomString.txt -"EventType=Warning" >> .\EventNotification$RandomString.txt -"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> .\EventNotification$RandomString.txt +"Notification=e" >> $EventNotification.FullName +"EventType=Warning" >> $EventNotification.FullName +"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> $EventNotification.FullName Write-Host "Adding/replacing File Screens.." $drivesContainingShares | ForEach-Object { Write-Host "`tAdding/replacing File Screen for [$_] with Source Template [$fileTemplateName].." &filescrn.exe Screen Delete "/Path:$_" /Quiet - &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" /Add-Notification:m,EmailNotification$RandomString.txt /Add-Notification:e,EventNotification$RandomString.txt + &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" /Add-Notification:m,$EmailNotification.FullName /Add-Notification:e,$EventNotification.FullName } -# Remote the temporary files -Remove-Item .\EmailNotification$RandomString.txt -Remove-Item .\EventNotification$RandomString.txt \ No newline at end of file +Remove-Item $EmailNotification.FullName -Force +Remove-Item $EventNotification.FullName -Force From 6cd3ebf26a67ab03acbf99acd3d928f9cdc8f9b0 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Fri, 24 Feb 2017 13:42:13 -0700 Subject: [PATCH 15/33] Replaced New-TemporaryFile with homemade version --- DeployCryptoBlocker.ps1 | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index edb2f72..b60a9f6 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -185,26 +185,26 @@ ForEach ($group in $fileGroups) { &filescrn.exe $screenArgs -$EmailNotification = New-TemporaryFile -$EventNotification = New-TemporaryFile +$EmailNotification = $env:TEMP + "\tmpEmail001.tmp" +$EventNotification = $env:TEMP + "\tmpEvent001.tmp" # Write the email options to the temporary file -"Notification=m" >> $EmailNotification.FullName -"To=[Admin Email]" >> $EmailNotification.FullName -"Subject=Unauthorized file from the [Violated File Group] file group detected" >> $EmailNotification.FullName -"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> $EmailNotification.FullName +"Notification=m" >> $EmailNotification +"To=[Admin Email]" >> $EmailNotification +"Subject=Unauthorized file from the [Violated File Group] file group detected" >> $EmailNotification +"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> $EmailNotification # Write the event log options to the temporary file -"Notification=e" >> $EventNotification.FullName -"EventType=Warning" >> $EventNotification.FullName -"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> $EventNotification.FullName +"Notification=e" >> $EventNotification +"EventType=Warning" >> $EventNotification +"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> $EventNotification Write-Host "Adding/replacing File Screens.." $drivesContainingShares | ForEach-Object { Write-Host "`tAdding/replacing File Screen for [$_] with Source Template [$fileTemplateName].." &filescrn.exe Screen Delete "/Path:$_" /Quiet - &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" /Add-Notification:m,$EmailNotification.FullName /Add-Notification:e,$EventNotification.FullName + &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" /Add-Notification:m,$EmailNotification /Add-Notification:e,$EventNotification } -Remove-Item $EmailNotification.FullName -Force -Remove-Item $EventNotification.FullName -Force +Remove-Item $EmailNotification -Force +Remove-Item $EventNotification -Force From 324742eab977815fce099d26b390adacf124a2d0 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 27 Feb 2017 14:14:48 -0700 Subject: [PATCH 16/33] Added quotes to screen add command --- DeployCryptoBlocker.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index b60a9f6..dfdda6f 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -203,7 +203,7 @@ Write-Host "Adding/replacing File Screens.." $drivesContainingShares | ForEach-Object { Write-Host "`tAdding/replacing File Screen for [$_] with Source Template [$fileTemplateName].." &filescrn.exe Screen Delete "/Path:$_" /Quiet - &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" /Add-Notification:m,$EmailNotification /Add-Notification:e,$EventNotification + &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" "/Add-Notification:m,$EmailNotification" "/Add-Notification:e,$EventNotification" } Remove-Item $EmailNotification -Force From 37ac557e41987bf136db71e2172a8a438105a20e Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 3 Apr 2017 13:34:44 -0600 Subject: [PATCH 17/33] Fixed wording --- README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.md b/README.md index 072168c..5c2b10a 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,7 @@ The script will install File Server Resource Manager (FSRM), and set up the rele How it Works -If the user writes a malicious file (as contained in the file group) to a network share, FSRM will run the deployed script which will add a Deny permission for that user against every share. - -This has been tested fairly thoroughly, and I find that at most ransomware ends up encrypting one directory before the user is blocked. - -The script has now been modified to pull the list of extensions from a JSON API. Credit to https://fsrm.experiant.ca/ for this list. -Make sure you review the list (https://fsrm.experiant.ca/api/v1/get) before deploying, in case any false positives are listed (e.g. I have seen CAD software legitimately use *.encrypted before). -When this list is updated, review it and simply run the script again to redeploy. +If the user attempts to write a malicious file (as described in the filescreen) to a protected network share, FSRM will prevent the file from being written and send an email to the configured administrators notifying them of the user and file location where the attempted file write occured. NOTE: This will NOT stop variants which use randomised file extensions, don't drop README files, etc From 0d4d8de44f067d5236814d807592684e9c41e608 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Tue, 25 Apr 2017 09:04:03 -0600 Subject: [PATCH 18/33] Made SkipList note more visible --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c2b10a..9c93609 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The script will install File Server Resource Manager (FSRM), and set up the rele Script Deployment Steps -Before running, please add any known good file extensions used in your environment to SkipList.txt, one per line. This will ensure that if a filescreen is added to the list in the future that blocks that specific file extension, your environment won't be affected as they will be automatically removed. If SkipList.txt does not exist, it will be created automatically. +NOTE: Before running, please add any known good file extensions used in your environment to SkipList.txt, one per line. This will ensure that if a filescreen is added to the list in the future that blocks that specific file extension, your environment won't be affected as they will be automatically removed. If SkipList.txt does not exist, it will be created automatically. 1. Checks for network shares 2. Installs FSRM From 3bfe7981ed8e4752dff7527ca8738e270adfc7a9 Mon Sep 17 00:00:00 2001 From: Andreas H Date: Mon, 15 May 2017 17:11:02 +0200 Subject: [PATCH 19/33] Extended functionality and polished ouput (#5) * Extended functionality and polished ouput - added the notification options to the template - removed notification options from the file screen - added active/passiv screen type configuration - added german translation to the notification messages - output polished a bit ;-) - added a few more comments * changed default language to english * changed default type to active --- DeployCryptoBlocker.ps1 | 124 ++++++++++++++++++++++++++++------------ 1 file changed, 89 insertions(+), 35 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index dfdda6f..eb812ed 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -1,5 +1,40 @@ # DeployCryptoBlocker.ps1 -# +# Version: 1.1 +##### + +################################ 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 unathorized files (use for monitoring) +#$fileTemplateType = "Passiv" + +# Write the email options to the temporary file - comment out the entire block if no email notification should be set +$EmailNotification = $env:TEMP + "\tmpEmail001.tmp" +"Notification=m" >> $EmailNotification +"To=[Admin Email]" >> $EmailNotification +## en +"Subject=Unauthorized file from the [Violated File Group] file group detected" >> $EmailNotification +"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> $EmailNotification +## de +#"Subject=Nicht autorisierte Datei erkannt, die mit Dateigruppe [Violated File Group] übereinstimmt" >> $EmailNotification +#"Message=Das System hat erkannt, dass Benutzer [Source Io Owner] versucht hat, die Datei [Source File Path] unter [File Screen Path] auf Server [Server] zu speichern. Diese Datei weist Übereinstimmungen mit der Dateigruppe [Violated File Group] auf, die auf dem System nicht zulässig ist." >> $EmailNotification + +# Write the event log options to the temporary file - comment out the entire block if no event notification should be set +$EventNotification = $env:TEMP + "\tmpEvent001.tmp" +"Notification=e" >> $EventNotification +"EventType=Warning" >> $EventNotification +## en +"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> $EventNotification +## de +#"Message=Das System hat erkannt, dass Benutzer [Source Io Owner] versucht hat, die Datei [Source File Path] unter [File Screen Path] auf Server [Server] zu speichern. Diese Datei weist Übereinstimmungen mit der Dateigruppe [Violated File Group] auf, die auf dem System nicht zulässig ist." >> $EventNotification + +################################ USER CONFIGURATION ################################ + ################################ Functions ################################ Function ConvertFrom-Json20 @@ -73,6 +108,8 @@ Function New-CBArraySplit ################################ Functions ################################ +################################ Program code ################################ + # 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) @@ -84,17 +121,20 @@ $DrivesContainingShares = @(Get-WmiObject Win32_Share | # all shares if ($drivesContainingShares.Count -eq 0) { + Write-Host "`n####" Write-Host "No drives containing shares were found. Exiting.." exit } +Write-Host "`n####" Write-Host "The following shares needing to be protected: $($drivesContainingShares -Join ",")" -#### Identify Windows Server version, and install FSRM role +# 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 @@ -106,39 +146,43 @@ if ($majorVer -ge 6) if ($minorVer -ge 2 -and $checkFSRM.Installed -ne "True") { # Server 2012 + Write-Host "`n####" Write-Host "FSRM not found.. Installing (2012).." Install-WindowsFeature -Name FS-Resource-Manager -IncludeManagementTools } elseif ($minorVer -ge 1 -and $checkFSRM.Installed -ne "True") { # Server 2008 R2 - Write-Host "FSRM not found.. Installing (2008 R2).." + Write-Host "`n####" + Write-Host "FSRM not found.. Installing (2008 R2).." Add-WindowsFeature FS-FileServer, FS-Resource-Manager } elseif ($checkFSRM.Installed -ne "True") { # Server 2008 - Write-Host "FSRM not found.. Installing (2008).." + Write-Host "`n####" + Write-Host "FSRM not found.. Installing (2008).." &servermanagercmd -Install FS-FileServer FS-Resource-Manager } } else { # Assume Server 2003 - Write-Host "Unsupported version of Windows detected! Quitting.." + Write-Host "`n####" + Write-Host "Unsupported version of Windows detected! Quitting.." return } - -$fileGroupName = "CryptoBlockerGroup" -$fileTemplateName = "CryptoBlockerTemplate" -$fileScreenName = "CryptoBlockerScreen" - # Download list of CryptoLocker file extensions +Write-Host "`n####" +Write-Host "Dowloading CryptoLocker file extensions list from fsrm.experiant.ca api.." $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 +Write-Host "`n####" +Write-Host "Processing SkipList.." If (Test-Path .\SkipList.txt) { $Exclusions = Get-Content .\SkipList.txt | ForEach-Object { $_.Trim() } @@ -164,47 +208,57 @@ Else 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) # Perform these steps for each of the 4KB limit split fileGroups +Write-Host "`n####" +Write-Host "Adding/replacing File Groups.." ForEach ($group in $fileGroups) { - Write-Host "Adding/replacing File Group [$($group.fileGroupName)] with monitored file [$($group.array -Join ",")].." - &filescrn.exe filegroup Delete "/Filegroup:$($group.fileGroupName)" /Quiet + #Write-Host "Adding/replacing File Group [$($group.fileGroupName)] with monitored file [$($group.array -Join ",")].." + Write-Host "`nFile Group [$($group.fileGroupName)] with monitored files from [$($group.array[0])] to [$($group.array[$group.array.GetUpperBound(0)])].." + &filescrn.exe filegroup Delete "/Filegroup:$($group.fileGroupName)" /Quiet &filescrn.exe Filegroup Add "/Filegroup:$($group.fileGroupName)" "/Members:$($group.array -Join '|')" } -Write-Host "Adding/replacing File Screen Template [$fileTemplateName] with Event Notification [$eventConfFilename] and Command Notification [$cmdConfFilename].." +# Create File Screen Template with Notification +Write-Host "`n####" +Write-Host "Adding/replacing [$fileTemplateType] File Screen Template [$fileTemplateName] with eMail Notification [$EmailNotification] and Event Notification [$EventNotification].." &filescrn.exe Template Delete /Template:$fileTemplateName /Quiet -# Build the argument list with all required fileGroups -$screenArgs = 'Template', 'Add', "/Template:$fileTemplateName" +# Build the argument list with all required fileGroups and notifications +$screenArgs = 'Template', 'Add', "/Template:$fileTemplateName", "/Type:$fileTemplateType" ForEach ($group in $fileGroups) { $screenArgs += "/Add-Filegroup:$($group.fileGroupName)" } - +If ($EmailNotification -ne "") { + $screenArgs += "/Add-Notification:m,$EmailNotification" +} +If ($EventNotification -ne "") { + $screenArgs += "/Add-Notification:e,$EventNotification" +} &filescrn.exe $screenArgs -$EmailNotification = $env:TEMP + "\tmpEmail001.tmp" -$EventNotification = $env:TEMP + "\tmpEvent001.tmp" - -# Write the email options to the temporary file -"Notification=m" >> $EmailNotification -"To=[Admin Email]" >> $EmailNotification -"Subject=Unauthorized file from the [Violated File Group] file group detected" >> $EmailNotification -"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> $EmailNotification - -# Write the event log options to the temporary file -"Notification=e" >> $EventNotification -"EventType=Warning" >> $EventNotification -"Message=User [Source Io Owner] attempted to save [Source File Path] to [File Screen Path] on the [Server] server. This file is in the [Violated File Group] file group, which is not permitted on the server." >> $EventNotification - +# Create File Screens for every drive containing shares +Write-Host "`n####" Write-Host "Adding/replacing File Screens.." $drivesContainingShares | ForEach-Object { - Write-Host "`tAdding/replacing File Screen for [$_] with Source Template [$fileTemplateName].." + Write-Host "File Screen for [$_] with Source Template [$fileTemplateName].." &filescrn.exe Screen Delete "/Path:$_" /Quiet - &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" "/Add-Notification:m,$EmailNotification" "/Add-Notification:e,$EventNotification" + &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" +} + +# Cleanup temporary files if they were created +Write-Host "`n####" +Write-Host "Cleaning up temporary stuff.." +If ($EmailNotification -ne "") { + Remove-Item $EmailNotification -Force } +If ($EventNotification -ne "") { + Remove-Item $EventNotification -Force +} + +Write-Host "`n####" +Write-Host "Done." +Write-Host "####" -Remove-Item $EmailNotification -Force -Remove-Item $EventNotification -Force +################################ Program code ################################ From 1057d4e454fa91c2e8d4fa2f218e3e9380dc9be2 Mon Sep 17 00:00:00 2001 From: Greg S Date: Mon, 15 May 2017 21:09:36 -0400 Subject: [PATCH 20/33] Added FSRM install failure check. (#9) --- DeployCryptoBlocker.ps1 | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index eb812ed..fcc3fc5 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -148,21 +148,38 @@ if ($majorVer -ge 6) # Server 2012 Write-Host "`n####" Write-Host "FSRM not found.. Installing (2012).." - Install-WindowsFeature -Name FS-Resource-Manager -IncludeManagementTools + + $install = Install-WindowsFeature -Name FS-Resource-Manager -IncludeManagementTools + if ($? -ne $True) + { + Write-Host "Install of FSRM failed." + exit + } } elseif ($minorVer -ge 1 -and $checkFSRM.Installed -ne "True") { # Server 2008 R2 Write-Host "`n####" Write-Host "FSRM not found.. Installing (2008 R2).." - Add-WindowsFeature FS-FileServer, FS-Resource-Manager + $install = Add-WindowsFeature FS-FileServer, FS-Resource-Manager + if ($? -ne $True) + { + Write-Host "Install of FSRM failed." + exit + } + } elseif ($checkFSRM.Installed -ne "True") { # Server 2008 Write-Host "`n####" Write-Host "FSRM not found.. Installing (2008).." - &servermanagercmd -Install FS-FileServer FS-Resource-Manager + $install = &servermanagercmd -Install FS-FileServer FS-Resource-Manager + if ($? -ne $True) + { + Write-Host "Install of FSRM failed." + exit + } } } else From 8017c2510abf282c4c3813744b131f4682307b4a Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Tue, 16 May 2017 10:49:14 -0600 Subject: [PATCH 21/33] Replaced $drivesContainingShares logic with code from davidande --- DeployCryptoBlocker.ps1 | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index fcc3fc5..2affa68 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -111,12 +111,18 @@ Function New-CBArraySplit ################################ Program code ################################ # 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 +#$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 + +$drivesContainingShares = @(Get-WmiObject Win32_Share + | Select Name,Path,Type + | Where-Object { $_.Type -match '0|2147483648' } + | Select -ExpandProperty Path + | Select -Unique) if ($drivesContainingShares.Count -eq 0) From 3b7902093e6d4e4e2b7ff8ba79426963e15cd22c Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Tue, 16 May 2017 10:52:25 -0600 Subject: [PATCH 22/33] Bars in the wrong place --- DeployCryptoBlocker.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 2affa68..8559532 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -118,11 +118,11 @@ Function New-CBArraySplit # ([System.IO.DirectoryInfo]$_).Root.Name # Extract the driveletter, as a string # } | Sort-Object -Unique) # remove duplicates -$drivesContainingShares = @(Get-WmiObject Win32_Share - | Select Name,Path,Type - | Where-Object { $_.Type -match '0|2147483648' } - | Select -ExpandProperty Path - | Select -Unique) +$drivesContainingShares = @(Get-WmiObject Win32_Share | + Select Name,Path,Type | + Where-Object { $_.Type -match '0|2147483648' } | + Select -ExpandProperty Path | + Select -Unique) if ($drivesContainingShares.Count -eq 0) From 6f3a41235bbf8866008588fb1419ed291f18d93b Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 22 May 2017 13:35:33 -0600 Subject: [PATCH 23/33] Updating as 4096 byte entry broke script --- DeployCryptoBlocker.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 8559532..fb5b5f1 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -73,7 +73,7 @@ Function New-CBArraySplit # temporary workingarray, tracking the length of the items in it and future commas $Extensions | ForEach-Object { - if (($LengthOfStringsInWorkingArray + 1 + $_.Length) -gt 4096) + if (($LengthOfStringsInWorkingArray + 1 + $_.Length) -gt 4095) { # Adding this item to the working array (with +1 for a comma) # pushes the contents past the 4Kb limit From b378bcc1d916e5204d64564c6b31dbf17645fad1 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Fri, 23 Jun 2017 09:07:09 -0600 Subject: [PATCH 24/33] Changed "4KB" to 4000bytes Dropping it down by a byte didn't help; due to some bad counting, this should fix it. --- DeployCryptoBlocker.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index fb5b5f1..4d3858c 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -73,7 +73,7 @@ Function New-CBArraySplit # temporary workingarray, tracking the length of the items in it and future commas $Extensions | ForEach-Object { - if (($LengthOfStringsInWorkingArray + 1 + $_.Length) -gt 4095) + if (($LengthOfStringsInWorkingArray + 1 + $_.Length) -gt 4000) { # Adding this item to the working array (with +1 for a comma) # pushes the contents past the 4Kb limit From c17231be135ca95587918bd7c7d22faee5d475b1 Mon Sep 17 00:00:00 2001 From: John Murphy Date: Mon, 28 Aug 2017 10:34:21 -0500 Subject: [PATCH 25/33] Proof of Concept (#27) * initial commit of proof of concept scripts * Initial draft of README.md for PoC --- PoC/README.md | 63 ++++++++++++++++++++ PoC/RegisterScheduledTasks.ps1 | 19 ++++++ PoC/downloadExperiantList.ps1 | 18 ++++++ PoC/monitorFileShare.ps1 | 103 +++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+) create mode 100644 PoC/README.md create mode 100644 PoC/RegisterScheduledTasks.ps1 create mode 100644 PoC/downloadExperiantList.ps1 create mode 100644 PoC/monitorFileShare.ps1 diff --git a/PoC/README.md b/PoC/README.md new file mode 100644 index 0000000..024ce9c --- /dev/null +++ b/PoC/README.md @@ -0,0 +1,63 @@ +CryptoBlocker +============== + +This solution allows for numerous types of storage devices to be monitored from a machine running a Windows OS. + + How it Works + +If a user attempts to modify or create a malicious file on a monitored storage device, the main script of this proof of concept (PoC) will notify the defined administrator via email of the attempted malicious activity. If the monitored storage device is distributed from a FreeNAS server, the Samba_server service will be stopped to prevent futher malicious activity. In addition, all activity occurring on the monitored device will be written to a log file. + + + +Prerequisites + +There are two known prerequitesites that must be installed on the machine where the main script will be executed from. [Prerequitesites can be found here.](http://www.powershelladmin.com/wiki/SSH_from_PowerShell_using_the_SSH.NET_library#Downloads) + +1. Windows Management Framework version 5 or higher +2. PowerShell module SSHSessions, available in the PowerShell gallery + + +Script Definitions + +There are three scripts included in the PoC. + +1. RegisterScheduledTasks.ps1 + + This script will create two separate scheduled tasks within Window's Task Scheduler. + +2. downloadExeriantList.ps1 + + This script will execute once daily. It will pull an updated file extension list from [Experiant Consulting](https://fsrm.experiant.ca/api/v1/get) and save it to a file that will later be used to determine if saved files are malicious in nature. + +3. monitorFileshare.ps1 + + This script will execute at each system startup and continue running while the system is on. It monitors a predefined storage locations for changes and analyzes the changes for malicious content. + +Implementation of Proof of Concept + +1. Download PoC folder to the computer that will be used for monitoring +2. Update the variables within the scripts to match the environment the scipt will be ran in. + * downloadExperiantList.ps1 contains two variables that require envirnoment-spcicific values + * monitorFileshare.ps1 contains numerous environment-specific variables that must be provided. +3. Execute the 'RegisterScheduledTasks.ps1' script. +4. Execute the 'downloadExperiantList.ps1' script to initialize the extension file. +5. Restart the computer to activate the scheduled start-up event - to start the monitor script. + + +Usage + +This proof of concpet does not stop a ransomware infection from occurring or spreading to other systems. + +These scripts are provided as-is. This proof of concept does not prevent the loss of data. It creates an opportunity for an administrator to respond to suspicious activity. + +Acknowledgements + + Portions of this script were modeled from the following sources: + + https://superuser.com/a/844034/413983 + http://www.jonathanmedd.net/2013/08/using-ssh-to-access-linux-servers-in-powershell.html + http://www.powershelladmin.com/wiki/SSH_from_PowerShell_using_the_SSH.NET_library + https://stackoverflow.com/questions/23953926/how-to-execute-a-powershell-script-automatically-using-windows-task-scheduler + https://stackoverflow.com/a/36355678/1885954_ + https://blogs.technet.microsoft.com/heyscriptingguy/2015/10/08/playing-with-json-and-powershell/ + diff --git a/PoC/RegisterScheduledTasks.ps1 b/PoC/RegisterScheduledTasks.ps1 new file mode 100644 index 0000000..a226fda --- /dev/null +++ b/PoC/RegisterScheduledTasks.ps1 @@ -0,0 +1,19 @@ +#------------------------------------- +# Script: RegisterScheduledTasks.ps1 +# Version: v20170717.1.1 +# Comments: Creates Scheduled Tasks +# Author: John Murphy +#------------------------------------- +##Scheduled Task for updataing extension list daily +## $filePath1 must be the full path of the 'downloadExperiantList.ps1' script included in the repository +$filePath1 = "downloadExperiantList.ps1" +$trigger1 = New-ScheduledTaskTrigger -Daily -At 5am + +Register-ScheduledJob -Name DownloadList -FilePath $filePath1 -Trigger $trigger1 + +##Scheduled Task for starting monitor script during startup of computer +## $filePath2 must be the full path of the 'monitorFileShare.ps1' script included in the repository +$filePath2 = "monitorFileShare.ps1" +$trigger2 = New-ScheduledTaskTrigger -AtStartup + +Register-ScheduledJob -Name MonitorFileShare -FilePath $filePath2 -Trigger $trigger2 \ No newline at end of file diff --git a/PoC/downloadExperiantList.ps1 b/PoC/downloadExperiantList.ps1 new file mode 100644 index 0000000..228944d --- /dev/null +++ b/PoC/downloadExperiantList.ps1 @@ -0,0 +1,18 @@ +#------------------------------------- +# Script: downloadExperiantList.ps1 +# Version: v20170717.1.1 +# Comments: Downloads current list of extensions from Experiant Consulting +# Author: John Murphy +#------------------------------------- +##Website to download file extensions from## +$website = "https://fsrm.experiant.ca/api/v1/get" +##Location of flat file of extensions## +$filePath = "" +##Name of flat file## +$fileName ="" + +$webclient = New-Object System.Net.WebClient +$jsonString = $webclient.DownloadString($website) + +$filePath = $filePath + $fileName +$jsonString | Out-File $filePath diff --git a/PoC/monitorFileShare.ps1 b/PoC/monitorFileShare.ps1 new file mode 100644 index 0000000..40063e0 --- /dev/null +++ b/PoC/monitorFileShare.ps1 @@ -0,0 +1,103 @@ +<#---------------------------------------------------------- + Script: monitorFileShare.ps1 + Version: v20170717 + Comments: Sets up FileSystemWatchers and responds + to potentially harmful file changes + Author: John Murphy +----------------------------------------------------------#> +<#VARIABLE DECLARATION#> +## A FileSystemWatcher is defined for each storage area that requires monitoring. + $watcher = New-Object System.IO.FileSystemWatcher ## Defines Storage area to monitor + $watcher.Path = "" ## Path to storage area to monitor + $watcher.Filter = "*.*" ## Defines what files to monitor within Storage area + $watcher.IncludeSubdirectories = $true ## Watcher monitoring depth + $watcher.EnableRaisingEvents = $true ## Allows $watcher to take action on events +## ------------------------------------------------------------------------------------------------ +## JSON extension file loading - Loads extension to monitor for into local variable + $jsonFile = "" ## Complete path to file saved by script 'downloadExperiantList.ps1' + $jsonString = Get-Content -Raw -Path $jsonFile ## Converts file to string + $json = $jsonString | ConvertFrom-json ## Converts string to object containing the extensions to monitor +## ------------------------------------------------------------------------------------------------ +## Email notification - Used to send email notification to defined administrator + $senderEmail = "" ## From email address + $senderPassword = "" ## Password for 'From' email address + $receiverEmail = "" ## Email of administrator to notify + $smtpServer = "" ## SMTP server to use when sending email + $smtpSendPort = "" ## port used to send SMTP email (587) +## ------------------------------------------------------------------------------------------------ +## FreeNAS Samba Server - Used to disable Samba server on FreeNAS + $freenasRoot = "root" ## FreeNAS user with access to shutting down a service + $rootPassword = "" ## Password of above FreeNAS user + $freenasName = "" ## Server name, FQDN, or IP address of FreeNAS server +## ------------------------------------------------------------------------------------------------ +## Log file - used to log events detected by $watcher + $logFile = "" ## Full path to location where log file will be stored +<#END VARIABLE DECLARATION#> +##------------------------------------------- +## Function: Send-ToEmail +## Purpose: Sends email to predefined administrator +## using credentials provided in variable declaration +##------------------------------------------- +function Send-ToEmail($extension, $path){ + $emailMessage = New-Object Net.Mail.MailMessage + $emailMessage.From = $senderEmail + $emailMessage.To.Add($receiverEmail) + $emailMessage.Subject = "Suspicious File Detected" + $emailMessage.Body = "A suspicious file was detected located at $path" + + $smtpClient = New-Object Net.Mail.SmtpClient($smtpServer,$smtpSendPort) + $smtpClient.EnableSsl = $true + $smtpClient.Credentials = New-Object System.Net.NetworkCredential($senderEmail,$senderPassword) + $smtpClient.Send($emailMessage) + Write-Host "Message Sent" +} +##------------------------------------------- +## Function: Disable-FreeNAS-Samba +## Purpose: Stops Samba-Server service on FreeNAS server +## Prerequisites: Requires WMF v5 and higher +## Requires Module = SshSessions +##------------------------------------------- +function Disable-FreeNAS-Samba(){ + New-SshSession -ComputerName $freenasName -Username $freenasRoot -Password $rootPassword + Invoke-SshCommand -ComputerName $freenasName -Command "service samba_server stop" + Remove-SshSession -ComputerName $freenasName + Write-Host "Samba has been stopped" +} +##------------------------------------------ +## Define Actions to take after an event is detected +##------------------------------------------ +$action = { $path = $Event.SourceEventArgs.FullPath + $fileName = Split-Path $path -Leaf + $changeType = $Event.SourceEventArgs.ChangeType + $user = $env:USERNAME + $logline = "$(Get-Date), $changeType, $path, $user, $computer" + Add-content $logFile -value $logline + Write-Host "The file '$fileName' was '$changeType' by '$user'" + foreach($obj in $json.filters) + { + if("$fileName" -like "$obj") + { + Disable-FreeNAS-Samba + Send-ToEmail($obj,$path) + } + } + Write-Host "Action Complete" +} +##------------------------------------------ +## Register events to monitor +##------------------------------------------ +Register-ObjectEvent $watcher "Created" -Action $action +Register-ObjectEvent $watcher "Changed" -Action $action +Register-ObjectEvent $watcher "Deleted" -Action $action +Register-ObjectEvent $watcher "Renamed" -Action $action +while($true){Start-Sleep 5} + + +<# Portions of this script were modeled from the following sources: + https://superuser.com/a/844034/413983 + http://www.jonathanmedd.net/2013/08/using-ssh-to-access-linux-servers-in-powershell.html + http://www.powershelladmin.com/wiki/SSH_from_PowerShell_using_the_SSH.NET_library + https://stackoverflow.com/questions/23953926/how-to-execute-a-powershell-script-automatically-using-windows-task-scheduler + https://stackoverflow.com/a/36355678/1885954_ + https://blogs.technet.microsoft.com/heyscriptingguy/2015/10/08/playing-with-json-and-powershell/ +#> \ No newline at end of file From 2f0749bbe5838a00728c2869cca87902fa5e8d9d Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Thu, 30 Nov 2017 10:06:13 -0700 Subject: [PATCH 26/33] Updated minimum PowerShell version, changed to use Invoke-WebRequest --- DeployCryptoBlocker.ps1 | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 4d3858c..b337dae 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -110,36 +110,18 @@ Function New-CBArraySplit ################################ Program code ################################ -# 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 - -$drivesContainingShares = @(Get-WmiObject Win32_Share | - Select Name,Path,Type | - Where-Object { $_.Type -match '0|2147483648' } | - Select -ExpandProperty Path | - Select -Unique) - +# Identify Windows Server version, PowerShell version and install FSRM role +$majorVer = [System.Environment]::OSVersion.Version.Major +$minorVer = [System.Environment]::OSVersion.Version.Minor +$powershellVer = $PSVersionTable.PSVersion.Major -if ($drivesContainingShares.Count -eq 0) +if ($powershellVer -le 2) { Write-Host "`n####" - Write-Host "No drives containing shares were found. Exiting.." + Write-Host "ERROR: PowerShell v3 or higher required." 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.." @@ -196,11 +178,29 @@ else return } +## Enumerate shares +$drivesContainingShares = @(Get-WmiObject Win32_Share | + Select Name,Path,Type | + Where-Object { $_.Type -match '0|2147483648' } | + Select -ExpandProperty Path | + Select -Unique) + + +if ($drivesContainingShares.Count -eq 0) +{ + Write-Host "`n####" + Write-Host "No drives containing shares were found. Exiting.." + exit +} + +Write-Host "`n####" +Write-Host "The following shares needing to be protected: $($drivesContainingShares -Join ",")" + # Download list of CryptoLocker file extensions Write-Host "`n####" Write-Host "Dowloading CryptoLocker file extensions list from fsrm.experiant.ca api.." -$webClient = New-Object System.Net.WebClient -$jsonStr = $webClient.DownloadString("https://fsrm.experiant.ca/api/v1/get") + +$jsonStr = Invoke-WebRequest -Uri https://fsrm.experiant.ca/api/v1/get $monitoredExtensions = @(ConvertFrom-Json20 $jsonStr | ForEach-Object { $_.filters }) # Process SkipList.txt From 3e0e3ddb3e5196e5fd1fc8ed3fd99d33de18e101 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 10 Jan 2018 10:09:00 -0700 Subject: [PATCH 27/33] Updated for Server 2016 Quick change as reported privately ---- Quote: Today I ran into a problem when executing your script on Windows Server 2016, it fails with error "servermanagercmd is not recognized as internal or external command..." The problem is that it incorrectly identifies Server 2016 as Server 2008. The Major version number of Server 2016 is 10, which returns true for the first clause. The script then goes into checking the minor version, and since the minor version number is 0, it returns false for all clauses, until it runs into the Server 2008 elseif and tries to run the unsopported command. --- DeployCryptoBlocker.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index b337dae..180dd52 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -131,11 +131,11 @@ if ($majorVer -ge 6) { $checkFSRM = Get-WindowsFeature -Name FS-Resource-Manager - if ($minorVer -ge 2 -and $checkFSRM.Installed -ne "True") + if (($minorVer -ge 2 -or $majorVer -eq 10) -and $checkFSRM.Installed -ne "True") { - # Server 2012 + # Server 2012 / 2016 Write-Host "`n####" - Write-Host "FSRM not found.. Installing (2012).." + Write-Host "FSRM not found.. Installing (2012 / 2016).." $install = Install-WindowsFeature -Name FS-Resource-Manager -IncludeManagementTools if ($? -ne $True) From adca7a74133e1810cc4884bbdfec11c15a354221 Mon Sep 17 00:00:00 2001 From: StarDestroyer78 Date: Thu, 11 Jan 2018 13:33:11 -0500 Subject: [PATCH 28/33] Add support for a "ProtectList.txt" ... if this file exists, only protect those shares. If not, then protect all the shares on the system. (#43) --- DeployCryptoBlocker.ps1 | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 180dd52..a2310a4 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -179,11 +179,17 @@ else } ## Enumerate shares -$drivesContainingShares = @(Get-WmiObject Win32_Share | - Select Name,Path,Type | - Where-Object { $_.Type -match '0|2147483648' } | - Select -ExpandProperty Path | - Select -Unique) +f (Test-Path .\ProtectList.txt) +{ + $drivesContainingShares = Get-Content .\ProtectList.txt | ForEach-Object { $_.Trim() } +} +Else { + $drivesContainingShares = @(Get-WmiObject Win32_Share | + Select Name,Path,Type | + Where-Object { $_.Type -match '0|2147483648' } | + Select -ExpandProperty Path | + Select -Unique) +} if ($drivesContainingShares.Count -eq 0) From d121bde2f507ab60ad83796aef240e9bdf6e0608 Mon Sep 17 00:00:00 2001 From: StarDestroyer78 Date: Thu, 11 Jan 2018 14:21:17 -0500 Subject: [PATCH 29/33] Typo Fix in Last Pull Request (#44) * Add support for a "ProtectList.txt" ... if this file exists, only protect those shares. If not, then protect all the shares on the system. * Fix a typo in the script. Also, add some text to README to describe the ProtectList.txt file. --- DeployCryptoBlocker.ps1 | 2 +- README.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index a2310a4..d90d4d6 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -179,7 +179,7 @@ else } ## Enumerate shares -f (Test-Path .\ProtectList.txt) +if (Test-Path .\ProtectList.txt) { $drivesContainingShares = Get-Content .\ProtectList.txt | ForEach-Object { $_.Trim() } } diff --git a/README.md b/README.md index 9c93609..23a8cb8 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ Just run the script. You can easily use this script to deploy the required FSRM An event will be logged by FSRM to the Event Viewer (Source = SRMSVC, Event ID = 8215), showing who tried to write a malicious file and where they tried to write it. Use your monitoring system of choice to raise alarms, tickets, etc for this event and respond accordingly. +ProtectList.txt + +By default, this script will enumarate all the shares running on the server and add protections for them. If you would like to override this, you can create a ProtectList.txt file in the script's running directory. The contents of this file should be the folders you would like to protect, one per line. If this file exists, only the folders listed in it will be protected. If the file is empty or only has invalid entries, there will be no protected folders. + Disclaimer This script is provided as is. I can not be held liable if this does not thwart a ransomware infection, causes your server to spontaneously combust, results in job loss, etc. From eadf4f44b7c8a38405a63522afa463a957fe4768 Mon Sep 17 00:00:00 2001 From: StarDestroyer78 Date: Thu, 11 Jan 2018 17:51:01 -0500 Subject: [PATCH 30/33] Add IncludeList.txt (#45) * Add support for a "ProtectList.txt" ... if this file exists, only protect those shares. If not, then protect all the shares on the system. * Fix a typo in the script. Also, add some text to README to describe the ProtectList.txt file. * Add support for a file named "IncludeList.txt" that adds file screens to the list sent to FSRM. * Add ProtectList.txt and IncludeList.txt to .getignore --- .gitignore | 4 +++- DeployCryptoBlocker.ps1 | 7 +++++++ README.md | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 55bf58e..7040945 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -SkipList.txt \ No newline at end of file +SkipList.txt +ProtectList.txt +IncludeList.txt diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index d90d4d6..b7cebb6 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -237,6 +237,13 @@ Else Set-Content -Path .\SkipList.txt -Value $emptyFile } +# Check to see if we have any local patterns to include +If (Test-Path .\IncludeList.txt) +{ + $includeExt = Get-Content .\IncludeList.txt | ForEach-Object { $_.Trim() } + $monitoredExtensions = $monitoredExtensions + $includeExt +} + # Split the $monitoredExtensions array into fileGroups of less than 4kb to allow processing by filescrn.exe $fileGroups = @(New-CBArraySplit $monitoredExtensions) diff --git a/README.md b/README.md index 23a8cb8..80cf320 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,10 @@ An event will be logged by FSRM to the Event Viewer (Source = SRMSVC, Event ID = By default, this script will enumarate all the shares running on the server and add protections for them. If you would like to override this, you can create a ProtectList.txt file in the script's running directory. The contents of this file should be the folders you would like to protect, one per line. If this file exists, only the folders listed in it will be protected. If the file is empty or only has invalid entries, there will be no protected folders. +IncludeList.txt + +Sometimes you have file screens that you want to add that are not included in the download from Experiant. In this case, you can simply create a file named IncludeList.txt and put the screens you would like to add, one per line. If this file does not exist, only the screens from Experiant are included. + Disclaimer This script is provided as is. I can not be held liable if this does not thwart a ransomware infection, causes your server to spontaneously combust, results in job loss, etc. From 3d859a745297e6bff211c2c319c812188a67bc55 Mon Sep 17 00:00:00 2001 From: Andreas H Date: Tue, 3 Jul 2018 15:58:28 +0200 Subject: [PATCH 31/33] absolute Path for included Files (#57) * Extended functionality and polished ouput - added the notification options to the template - removed notification options from the file screen - added active/passiv screen type configuration - added german translation to the notification messages - output polished a bit ;-) - added a few more comments * changed default language to english * changed default type to active * Changed SkipList from relative Path to automatic detected Path of the Script * Changed relative path to $PSScriptRoot Variable * Always use correct path for included files * Ignore working directory * works with task scheduler (does not write to Windows System root!) * Included files are always in the directory of the script * existing files in relative path will be moved to the script path if they exist * fixed bug in skiplist moveing --- DeployCryptoBlocker.ps1 | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index b7cebb6..47dbf78 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -179,9 +179,17 @@ else } ## Enumerate shares +Write-Host "`n####" +Write-Host "Processing ProtectList.." +### move file from C:\Windows\System32 or whatever your relative path is to the directory of this script if (Test-Path .\ProtectList.txt) { - $drivesContainingShares = Get-Content .\ProtectList.txt | ForEach-Object { $_.Trim() } + Move-Item -Path .\ProtectList.txt -Destination $PSScriptRoot\ProtectList.txt -Force +} + +if (Test-Path $PSScriptRoot\ProtectList.txt) +{ + $drivesContainingShares = Get-Content $PSScriptRoot\ProtectList.txt | ForEach-Object { $_.Trim() } } Else { $drivesContainingShares = @(Get-WmiObject Win32_Share | @@ -212,9 +220,15 @@ $monitoredExtensions = @(ConvertFrom-Json20 $jsonStr | ForEach-Object { $_.filte # Process SkipList.txt Write-Host "`n####" Write-Host "Processing SkipList.." -If (Test-Path .\SkipList.txt) +### move file from C:\Windows\System32 or whatever your relative path is to the directory of this script +if (Test-Path .\SkipList.txt) +{ + Move-Item -Path .\SkipList.txt -Destination $PSScriptRoot\SkipList.txt -Force +} + +If (Test-Path $PSScriptRoot\SkipList.txt) { - $Exclusions = Get-Content .\SkipList.txt | ForEach-Object { $_.Trim() } + $Exclusions = Get-Content $PSScriptRoot\SkipList.txt | ForEach-Object { $_.Trim() } $monitoredExtensions = $monitoredExtensions | Where-Object { $Exclusions -notcontains $_ } } @@ -234,13 +248,20 @@ Else # entries before applying the list to your FSRM implementation. # '@ - Set-Content -Path .\SkipList.txt -Value $emptyFile + Set-Content -Path $PSScriptRoot\SkipList.txt -Value $emptyFile } # Check to see if we have any local patterns to include -If (Test-Path .\IncludeList.txt) +Write-Host "`n####" +Write-Host "Processing IncludeList.." +### move file from C:\Windows\System32 or whatever your relative path is to the directory of this script +if (Test-Path .\IncludeList.txt) +{ + Move-Item -Path .\IncludeList.txt -Destination $PSScriptRoot\IncludeList.txt -Force +} +If (Test-Path $PSScriptRoot\IncludeList.txt) { - $includeExt = Get-Content .\IncludeList.txt | ForEach-Object { $_.Trim() } + $includeExt = Get-Content $PSScriptRoot\IncludeList.txt | ForEach-Object { $_.Trim() } $monitoredExtensions = $monitoredExtensions + $includeExt } From 70b013f075cc0d3f2e33e544ca492608263befe2 Mon Sep 17 00:00:00 2001 From: insanefreakv1 <37694420+insanefreakv1@users.noreply.github.com> Date: Thu, 5 Jul 2018 05:35:19 +1200 Subject: [PATCH 32/33] Add ExcludePaths.txt (#56) Create FSRM Exclusions for Specific Folders/Paths Listed in ExcludePaths.txt --- DeployCryptoBlocker.ps1 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 47dbf78..8ca23d4 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -304,6 +304,22 @@ $drivesContainingShares | ForEach-Object { &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName" } +# Add Folder Exceptions from ExcludeList.txt +If (Test-Path .\ExcludePaths.txt) { + Write-Host "`n####" + Write-Host "Processing Folder Exclusions.." + Get-Content .\ExcludePaths.txt | ForEach-Object { + If (Test-Path $_) { + # Build the argument list with all required fileGroups + $ExclusionArgs = 'Exception', 'Add', "/Path:$_" + ForEach ($group in $fileGroups) { + $ExclusionArgs += "/Add-Filegroup:$($group.fileGroupName)" + } + &filescrn.exe $ExclusionArgs + } + } +} + # Cleanup temporary files if they were created Write-Host "`n####" Write-Host "Cleaning up temporary stuff.." From 3596f7c3b9d0636d57d2a15dbfe370c15b9dee06 Mon Sep 17 00:00:00 2001 From: Andreas H Date: Wed, 10 Oct 2018 18:44:38 +0200 Subject: [PATCH 33/33] Fixed relative path bug in ExcludePath.txt (#59) * Extended functionality and polished ouput - added the notification options to the template - removed notification options from the file screen - added active/passiv screen type configuration - added german translation to the notification messages - output polished a bit ;-) - added a few more comments * changed default language to english * changed default type to active * Changed SkipList from relative Path to automatic detected Path of the Script * Changed relative path to $PSScriptRoot Variable * Always use correct path for included files * Ignore working directory * works with task scheduler (does not write to Windows System root!) * Included files are always in the directory of the script * existing files in relative path will be moved to the script path if they exist * fixed bug in skiplist moveing * Change relative path of ExcludePath.txt to $PSScriptRoot and added ExcludePath to .gitignore --- .gitignore | 1 + DeployCryptoBlocker.ps1 | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 7040945..1c286eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ SkipList.txt ProtectList.txt IncludeList.txt +ExcludePaths.txt \ No newline at end of file diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index 8ca23d4..3465b8a 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -305,10 +305,15 @@ $drivesContainingShares | ForEach-Object { } # Add Folder Exceptions from ExcludeList.txt -If (Test-Path .\ExcludePaths.txt) { - Write-Host "`n####" - Write-Host "Processing Folder Exclusions.." - Get-Content .\ExcludePaths.txt | ForEach-Object { +Write-Host "`n####" +Write-Host "Processing ExcludeList.." +### move file from C:\Windows\System32 or whatever your relative path is to the directory of this script +if (Test-Path .\ExcludePaths.txt) +{ + Move-Item -Path .\ExcludePaths.txt -Destination $PSScriptRoot\ExcludePaths.txt -Force +} +If (Test-Path $PSScriptRoot\ExcludePaths.txt) { + Get-Content $PSScriptRoot\ExcludePaths.txt | ForEach-Object { If (Test-Path $_) { # Build the argument list with all required fileGroups $ExclusionArgs = 'Exception', 'Add', "/Path:$_"