Tuesday, April 5, 2016

A Deeper Explanation of How Ransomware Sinkholes Work

In our previous article, one of the proactive methods we discussed was a method that used a recursive mountpoint to create a loop forcing the ransomware to perpetually encrypt the same files over and over.  We appreciate all of the feedback and based on some of the discussion, we would like to explain in greater detail what is going on with the recursive sinkhole.  On the surface, it is very understandable to see this as a bad idea because using an "infinite" loop of directories is going to break stuff.  We want clear up some misunderstandings about how it actually works and why it might not impact you as much as you think.  That being said, some things will likely need some adjustments and every environment is unique and any system configuration should be vetted for stability before being established in production.

To help frame this post, let's establish what exactly our ransomware sinkhole is.  The sinkhole is simply a recursive NTFS junction.  An NTFS junction point is a symbolic link to a directory that acts as an alias of that directory. These are not new. In fact, odds are good that you have tons of them on your system right now.
Ever since early 2007, when Vista came out, Microsoft has included an NTFS junction from C:\Documents and Settings to C:\Users to support legacy software. Profile folders are full of them as well.


Recursive junctions are not new either. In 2005, David Solomon and Mark Russinovich included a recursive mountpoint exercise on page 641 of Microsoft Windows Internals 4th Edition. With that understanding, let's get down to business.



Sinkholes do not create an "infinite" loop.

Windows has very specific limits on file path length that prevent an infinitely long recursive path. For the nitty gritty details, this is a pretty good reference. There are many rules but here are the most relevant:
  • In the Windows API (with some exceptions for unicode), the maximum length for a path is MAX_PATH, which is defined as 260 characters

    • The maximum path on drive X is "X:\some 256-character path string<NUL>" 
    • If you speak Python: len('X:\\' + str('a') * 256 + str('\x00'))
    • This length includes slashes, which means the number of possible subfolders is (260 - 4)/2 = 128, assuming you use single character folder name.
    • This works fine for mapped drives:
  • To further complicate things on local drives, to prevent buffer overflows and "infinite" loops, both command prompt and Windows Explorer halt their recursion when the directory depth reaches 32 or the pathname exceeds 256 characters, whichever comes first. (Microsoft Windows Internals, Fourth Edition, pg. 642)

How does this affect Ransomware? Ransomware needs a listing of your files in order to open, encrypt, and ransom. We are using the a recursive mountpoint to keep the ransomware busy with our canary files while we try to detect and kill the process. To illustrate the point, in the example below we are going to create a folder called "F:\!" and put one document inside that folder, "F:\!\super_important.docx". Then we create a junction named "$" and point that to our F: drive.
You can see in the example that getting a directory listing through this configuration makes it appear like there are many directories and files.

With this simple configuration, we see 32 instances of the same file. Though there are numerous ways to circumvent this and many variants of ransomware, in theory, the ransomware should encrypt this one file 32 times. This is not in and of itself all that impressive but we are only using one file and one sinkhole. If we add just one more single character recursive mount point, we see exponential increases in the instances of files. Since this example is working relatively short paths, given two single character folder names, we are looking at a perceived 2**32 or 4,294,967,296 super_important.docx files.

The recursive junctions should not disrupt AV products.

All of the endpoint solutions we have tested do not scan junctions. Why not? It's fairly simple, junctions represent an actual location somewhere else on the volume so from a security scanning standpoint it is inefficient and redundant to waste the cycles. How do they do it? Endpoint solutions should be scanning the attributes of folder objects and ignoring those that have the "Reparse Point" flag set.  Our testing shows that ransomware is ignorant of the fact the folder is really a reparse point, much less a recursive one.  As mentioned previously, your installation of Windows likely has a multiple reparse points but Windows is also using permissions to deny access when you try to list the contents within the junction, in our examples we are not blocking access with permissions.

Here is an example of how this works:

What will break?  Windows Indexing

We would be misleading if we didn't mention the need to adjust Windows Search Indexing to exclude your sinkhole folders.  Windows uses an index to perform very fast searches of the most commonly accessed files but this service will recursively index your sinkhole as well.  To fix this, you will need to exclude your sinkholes from indexing.  

The best way we have found is to exclude this folder in the Control Panel > Indexing Options.


This is not ideal and we are still looking into a more programmatic method of implementing this.  When we find a clean one, we will update.

What else will break?

With all of the countless scripts that get put into production environments, it is hard to definitely predict what a recursive mountpoint would impact.  That is a big reason why we aren't swearing this is practical for everyone and the technique should be tested in your environment prior to implementation.



Friday, March 25, 2016

Proactively Reacting to Ransomware

For our inaugural post, we thought we would share some proof of concept ideas regarding potential reactions and responses to ransomware outbreaks.  We have been tossing around some ideas and scripts, that we think can buy the victim back some time when their machine is being infected by ransomware.  The hope being that this additional time allows the victim time to react to the infection and hopefully provide the victim an opportunity to remedy the situation.

As an incident responder, we all know that ransomware cases can be overly damning to a victim and an organization, so reacting to them as quick as possible is crucial.  Since protections in place, such as AntiVirus are constantly in a rat race, that is usually one step behind, it puts everyone at a disadvantage.  One thing you can say about ransomware is that it isn't hard to spot a successful infection.  Forensically speaking, it is overly noisy and riddles the host with artifacts of its execution.

Knowing that there are numerous artifacts and infection actions you can bank on, things such as file overwrites, an infection order, known extensions targeted and potentially persistence, what would happen if you reacted on those events specifically and not the malware itself.  If we reacted on these events instead of looking for a stage1 or stage2 drop, the majority of ransomware infections could be delayed, buying back some time for the victim and incident responder to react and hopefully stop the infection.

File Canaries:
Take for example the standard scenario where the user receives a malicious email and then unknowingly acts on the attachment, allowing the ransomware to start the initial stages of infections.  Once all the pieces are in play and the ransomware starts the encryption process, it will usually target files of a known type, files such as office docs, audio files, video files, images and in some cases script file extensions.  Besides targeting files of a specific type, it will also start the encryption process by beginning the process down the root of C:\ or beginning by encrypting the files within the running user's profile.  Once the local drive has been encrypted, the malware can then move on to targeting mapped drives and begin encrypting the mounted share's content.

So knowing these elements of the attack, we wondered if we could setup a canary file or a canary directory and then automatically respond when one of these files are written to, aka overwritten by encryption.  Of course, we want to respond in the safest manner possible, so we would need to be careful about creating these canaries.  Knowing that multiple tools and programs comb through your files regularly, you want to ensure your permissions are right.
What type of non-intrusive reactions should we implement
  • Notify a responsible party via email
    • This is likely the lowest level of reaction we thought was appropriate.  It does nothing for protection, just simply alerts someone.
  • Disconnect all mapped drives
    • This action will at least protect any mapped shares or devices, containing the infection to the single endpoint.
  • Slow down the malware and attempt to kill the process
    • Later in the post we will illustrate some interesting techniques that can be performed to slow ransomware down. Some of these techniques are fairly aggressive and could cause problems if triggered by a false positive.

Below is a short Powershell example that could be used as a base to monitor for canary file changes.  The script creates a single canary file, enables monitoring the file for LastWrite changes and responds when the file is written to by sending an email and unmounting any mapped drives.

This is a passive measure aimed at detection and containment. This is a very non-invasive way for companies to confine outbreaks of ransomware within a particular endpoint and prevent network resources from being affected. One of the big down sides of this example is that we aren't actually disrupting the course of the malware and we still have a mess to deal with on the local endpoint.

Example 1:
$DirPath = "C:\Temp\"
$FName = "Redemptio*.docx"
$FilePath = Join-Path -Path $Dirpath -ChildPath $FName

function DriveUnMapper {
$MappedDrives = Get-WmiObject -Class Win32_MappedLogicalDisk
ForEach($Drive in $MappedDrives){
net use $Drive.name /delete
  }
}

function CreateWatcher {
$global:FSWatcherObj = New-Object IO.FileSystemWatcher $DirPath, $FName -Property @{
IncludeSubdirectories = $false;
EnableRaisingEvents = $true;
NotifyFilter = [IO.NotifyFilters]'LastWrite'
  }
} 
function RegisterWatcher {
Register-ObjectEvent $FSWatcherObj Changed -SourceIdentifier FileChanged -Action {
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host "The file '$name' was $changeType at $timeStamp" -fore red
$logdata = "$(Get-Date), $changeType, $FilePath, was altered! Disconnecting Drives"
Add-content "C:\Users\user\Desktop\Redemptio.bla" -value $logdata
$smtp = New-Object Net.Mail.SmtpClient("smtp.yoursite.notreal")
$smtp.Send("SecurityNerds@yoursite.notreal","Management@yoursite.notreal","Ransomware File Canary has been written to on " + $env:computername,"Information on the Event:" + $logdata + "Disconnecting network drives.")
DriveUnMapper
  }
} 
function CreateCanary {
New-Item C:\Temp\Redemptio-canary.docx -ItemType File -value "Redemptio canary file - do not write to file"
}
CreateCanary
CreateWatcher
RegisterWatcher

Below is a C# example that works in conjunction with Object Auditing and Audit Rules.  When Object Auditing is enabled to audit Write Data, we can monitor for those security events and then react.  Most applications installed or resident on the system will potentially read or access these file canaries, so you want to ignore those read events and only focus on the writing of the file.  When you create your canary files you want to enable Create Files/Write Data, Create Folders/Append Data NTFS permissions.  Object Auditing can be enabled in your Local Security policy or via GPO or via .Net but given most likely use a Global Policy it may not be worthy of including in your script.  See Example 3 for some code examples for the Audit Rules.

In this example, the script will monitor the event logs for Security-Auditing events that contain the filename or directory name you picked.  Since it will only trigger when data is written to the canary file, this example script will then kill the process that wrote to the canary.  Depending on your needs you can combine actions, so you could also unmount any mapped shares and send an email, or whatever action you feel appropriate.

Example 2:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading; 
class Redemptio
{
   static AutoResetEvent signal;
   public static void Main()
       public static void Main()
    {
        signal = new AutoResetEvent(false);
        EventLog myNewLog = new EventLog("Security", ".", "Microsoft Windows security auditing.");
        myNewLog.EntryWritten += new EntryWrittenEventHandler(MyOnEntryWritten);
        myNewLog.EnableRaisingEvents = true;
        signal.WaitOne();

    }
 
    public static void MyOnEntryWritten(object source, EntryWrittenEventArgs e)
    {
        string proc = "spiderham";
        string src = "Microsoft-Windows-Security-Auditing";
        bool a = e.Entry.Source.Contains(src);
        bool b = e.Entry.Message.Contains(proc);
        if ((a) && (b))
        {
            Console.WriteLine("Current message entry is: '"
   + e.Entry.MachineName + e.Entry.TimeWritten + e.Entry.Source + e.Entry.Data[0] + "'");
   string str1 = "Process ID:";
   string str2 = "Process Name:";
   int startindex = e.Entry.Message.IndexOf(str1);
   int endindex = e.Entry.Message.LastIndexOf(str2);
   int length = endindex - startindex;
   string fullLine = e.Entry.Message.Substring(startindex, length).Replace(" ", string.Empty);
   Console.WriteLine(fullLine);
   string[] result;
   result = fullLine.Split();
   Console.WriteLine(result[0]);
   Console.WriteLine(result[1]);
   Console.WriteLine(result[2]);
   string strpid = Convert.ToString(result[2]).Remove(0, 2);
   int pid = Convert.ToInt32(strpid, 16);
   Console.WriteLine("Killing Process ID: " + pid);
   Process evilproc = Process.GetProcessById(pid);
   Thread.Sleep(5000);
   evilproc.Kill();
   Console.WriteLine("Process ID: " + pid + " killed.");
        }
    }
}

Below is a Powershell example that sets up your Audit Rules but for a canary directory and all the files within that folder.  In this example, the canary directory is called '$', more on why we named it '$' later.

Example 3:
$AuditUser = "Everyone"
$AuditRules = "WriteData"
$InheritType = "ContainerInherit,ObjectInherit"
$AuditType = "Success"
$AccessRule = New-Object System.Security.AccessControl.FileSystemAuditRule(
    $AuditUser,
    $AuditRules,
    "None",
    "None",
    $AuditType
    )

$HoneyPot = Get-ChildItem 'C:\$'
ForEach($FileCanary in $HoneyPot){
$ACL = Get-Acl $FileCanary.FullName
$ACL.SetAuditRule($AccessRule),$FileCanary.FullName
$ACL | Set-Acl $FileCanary.FullName
}

The first few examples create file canaries in our honeypot folder to give us the time to find, alert on, remove mappings and or kill the malicious process. But depending on where you place your canary folder or files and what you name them, the malware might be able to encrypt hundreds files before it hits your canary files.  Most ransomware uses relatively simple cryptographic methods that computationally take almost no time at all and present us with a hefty challenge on reacting quickly. We needed a way to keep the malware busy doing something harmless, before it got to our actually files.

So why did Example 3 use '$' as the canary directory?  Say you create a mountpoint called '$$'.
Example 4:
Powershell example to create a '$$' mount point to C:

#Let's grab the DeviceID for the C volume
$Volume_info_for_C = Get-WMIObject -Class Win32_Volume -Filter "driveletter='c:'"
$Device_ID_of_C = $Volume_info_for_C.DeviceID
#Normally, everything is mounted only to the root (C:\) but we are going to get creative.
$Sinkholes = @('$$')
ForEach($Sinkhole in $Sinkholes){ 
    New-Item c:\$Sinkhole -ItemType directory
    $Volume_info_for_C.AddMountPoint("c:\$Sinkholes")
}

So what happens when you browse to that directory?  You'll quickly notice you hit a recursive loop.
Screenshot of the C volume in windows explorer.
Here is the C:\$$ "folder".
Here is C:\$$\$$\$$ "folder".

How does this actually help? If the ransomware iterates from the top of the volume, this seemingly infinite loop of folders infinitely increases the "distance" to get the files we want to protect.

Let's take a look at the difference in ProcMon.
Here is an example of Teslacrypt enumerating through a Windows VM without a sinkhole:
In the picture above, we can see that the C:\* is queried and $Recycle.Bin folder is the first one returned, so it starts recursively searching for files.

So what does it look like when Teslacrypt hits our mount point.
This ransomware is not able to recurse the drive because of the mountpoint we created.  It just goes on and on hitting C:\$$.  You may be wondering "What if it doesn't start recursing at the top of the volume?".

In order to crack that nut, you could create multiple sinkholes, for example putting an additional sinkhole in a user profile.

$Sinkholes = @('$$','Users\$GoToJail','Users\username\Documents\$GoDirectlyToJail')
ForEach($Sinkhole in $Sinkholes){ 
New-Item c:\$Sinkhole -ItemType directory
$Volume_info_for_C.AddMountPoint("c:\$Sinkhole")

If the ransomware hits the Users\username\Documents\$GoDirectlyToJail "folder", it will get sent back to the top of the volume where it will be sinkholed by the C:\$$ "folder".

The example below uses the file canaries and the sinkholes concepts together.  The script creates plain text canary files using the extension list, creates the sinkhole mount point.  This script will force the ransomware into a loop, so it perpetually encrypts the same canary files over and over again.

Example 5:
$HoneyPot =  "c:\`$\"
New-Item $HoneyPot -ItemType directory
#Here are a few of the popular extensions for Ransomware to target.
$exts = @(".c",".h",".m",".ai",".cs",".db",".db",".nd",".pl",".ps",".py",
          ".rm",".3dm",".3ds","3fr",".3g2",".3gp",".ach",".arw",".asf",".asx",
          ".avi",".bak",".bay",".cdr",".cer",".cpp",".cr2",".crt",".crw",".dbf",
          ".dcr",".dds",".der",".des",".dng",".doc",".dtd",".dwg",".dxf",".dxg",
          ".eml",".eps",".erf",".fla",".flv",".hpp",".iif",".jpe",".jpg",".kdc",
          ".key",".lua",".m4v",".max",".mdb",".mdf",".mef",".mov",".mp3",".mp4",
          ".mpg",".mrw",".msg",".nef",".nk2",".nrw",".oab",".obj",".odb",".odc",
          ".odm",".odp",".ods",".odt",".orf",".ost",".p12",".p7b",".p7c",".pab",
          ".pas",".pct",".pdb",".pdd",".pdf",".pef",".pem",".pfx",".pps",".ppt",
          ".prf",".psd",".pst",".ptx",".qba",".qbb",".qbm",".qbr",".qbw",".qbx",
          ".qby",".r3d",".raf",".raw",".rtf",".rw2",".rwl",".sql",".sr2",".srf",
          ".srt",".srw",".svg",".swf",".tex",".tga",".thm",".tlg",".txt",".vob",
          ".wav",".wb2",".wmv",".wpd",".wps",".x3f",".xlk",".xlr",".xls",".yuv",
          ".back",".docm",".docx",".flac",".indd",".java",".jpeg",".pptm",".pptx",
          ".xlsb",".xlsm",".xlsx")

ForEach($ext in $exts){
  #For each extension, this will create a puedorandom number of files between 2 and 5
  #We don't want this ransomware to get bored while we are trying to kill it.
  For($i=1; $i -le $(Get-Random -Minimum 2 -Maximum 5 ); $i++){ 
  #Now we are going to create the file contents.
 $textout = ""
      For($i=1; $i -le $(Get-Random -Minimum 10 -Maximum 1000 ); $i++){ 
          #For puedorandom characters
          #$textout += -join ((65..90) + (97..122)|Get-Random -Count 64| % {[char]$_})
          #For some plaintext easy to read
          $textout += "Completely Plaintext File. "
      }
 #More random filenames because...why not?
 $RandomFileName = -join ((65..90) + (97..122)|Get-Random -Count 8|% {[char]$_})
 #Save random file contents to random file name with our extension
 $RandomFilePath = ($HoneyPot + $RandomFileName + $ext)
 $textout| Out-File -FilePath $RandomFilePath -Force -ErrorAction SilentlyContinue
  }
}

#Next we create the sinkhole.
#Let's grab the volume info for C
$Volume_info_for_C = Get-WMIObject -Class Win32_Volume -Filter "driveletter='c:'"
#Now lets snag the DeviceID
$Device_ID_of_C = $Volume_info_for_C.DeviceID
#We are going to use the DeviceID to setup some new mountpoints for this volume.
#Normally, everything is mounted only to C:\ but we're going to get creative.
$Sinkholes = @('$$','Users\$GoToJail','Users\username\Documents\$GoDirectlyToJail')
ForEach($Sinkhole in $Sinkholes){ 
    New-Item c:\$Sinkhole -ItemType directory
    $Volume_info_for_C.AddMountPoint("c:\$Sinkhole")
}

On Windows x86 systems, it is trivial to review the open handles and find the process that has opened the canary file.  Using something like file handles makes it a hair easier than solely relying on Object Auditing and Event Log monitoring.  Below contains a Python script that will look at the open handles and kill the pid associated with the open handle to the canary directory.  It should be noted that this by itself would trigger on any open handle, so read or access changes would trigger this, so you may want to only use this as an option when you have been alerted to an event via file monitoring or Event Log escalations.

Example 6:
#!/usr/bin/env python
import psutil
#setup a loop
while True:
    #loop through each of the running processes
    for proc in psutil.process_iter():
        try:
            #Grab some info
            info = proc.as_dict(attrs=['pid', 'name'])
            for path, fd in proc.open_files():
                if r"C:\$$" in path:
                    #display info about the process
                    print info, path
                    #kill it
                    proc.terminate()
        except:
            continue 

Using the handles solution is great for Windows x86 systems but given those aren't in use as much anymore.  This method falls flat on modern PCs, due to Microsoft's Driver signing policy, starting with 64-bit versions of Windows Vista and later.  For this reason, utilities that display open file handles like Handle, Process Hacker, OpenedFilesView load their own signed driver that is used to extract information from Windows Kernel and if you prefer you can utilize the output of these tools.  There are a handful of MS SysInternals Handles.exe scripts that read in handles.exe's output.

Lastly, here is yet another show stopper action that can be taken after a ransomware trigger.  Unfortunately, this one is much more damning than the others and not really mean to be a potential solution, just something fun to experiment with.

The batch script below creates a fork bomb which consumes all resources bringing your system to a halt, thus halting the ransomware.

Example 7:
Fork Bomb
:START
start %~dp0%~n0%~x0
%~dp0%~n0%~x0|%~dp0%~n0%~x0
GOTO START

The %~dp0%~n0%~x0 is fancy batchfu for the complete file path of the batch script that is running. Every time this runs it creates another instance of itself and then runs another instance and pipes that output into yet another. This consumes all available memory leaving nothing for the malware or any other process to use. While this was able to stop Teslacrypt and others after approximately encrypting 5 canary files but like we mentioned this might not be the most practical idea because it does introduce a vector for Denial of Service.
Screenshot of Fork Bomb Result
As always, there are a handful of ways to skin a cat but we wanted to lay out a handful of low resource intensive reactionary response solutions.  Knowing some of these might come with a risk for your environment, you should test to see what solution may be a possibility for your environment.