Sunday, December 18, 2016

RAM File System in Python

One of the first things you are taught when studying Computer Forensic Investigation is the concept of evidence volatility. Simply put, some pieces of evidence will be available for collection for a much shorter amount of time than other pieces. You must always collect evidence from the most to least volatile. For a concrete example of "why" this practice must be applied you can consider the bot I have developed below.
Like most bots these days this example loads it's tool modules from a remote Command and Control server. The difference is: it uses a completely RAM based file system to store it's tools. This means there is little forensic trace of those tools when they are not directly loaded into memory. I should note that this is not an original idea. According to the ICIT briefing on Advanced Persistent Threats, Uroburos (also known as Epic Turla) uses an Encrypted Virtual File System as part of it's Rootkit.

First let's look at the primary Module we will be using: PyFilesystem. You can check the documentation out at http://docs.pyfilesystem.org There is a second module I use for managing the zip file in the script (so I do not need to rely on the OS unzip functions). You can find the documentation on zipfile here https://docs.python.org/2/library/zipfile.html

Design

There are a lot of possible options for how one could load the tools remotely into the RAM file system (MemoryFS). My first extension was an HTTP based File System API. While that provided a good starting point it was heavy on the CnC side. It required a special server-side component to translate requests for files. Next, I chose to load the tools from a zip File System retrieved across the network. In an actual deployment this maybe as simple as a base64-encoded, encrypted, byte string copied off of a pseudo-anonymous source (like Pastebin or Gists). This turned out to be the much simpler option.

Creating the Virtual FS

The first thing we need is a File System we want to import onto a machine.  I created one for testing which mirrored what an attacker might want to include.


As you might imagine you can add any Binary tools, Python packages, etc. to this. The one constraint I would recommend is to keep it small. There are two reasons for this stance. One being the initial transfer across the network is more likely to cause issues with larger core file systems. This may cause network timeouts or worse (from an attackers perspective) set off alerts. The other reason being memory hogging. Loading too many files into the RAM FS will cause the program to eat up RAM. Keep it lean.

The last file on the list, vfs.zip, contains all the files and directories above it. This is the file we use to transfer the data. To format it I created a script which read the files binary data and converted it to a base64 encoded string. I wrote the string to a file, which I then uploaded to GitHub. I set the BASE_URL to the file's raw data URL (You can look at GitHub tutorials on finding this information).

Using the Virtual FS in RAM


As you can see from the above code the algorithm and implementation are fairly simple. I use urllib2 to retrieve the base64 encoded string from the previously mentioned BASE_URL. Now to keep from having to write this file to disk, I simply use the RAM File System. The safeopen() function can be used to create a file that does not exist. If it does exist it will open the same as a call to open() would. Once the zip file is written to the RAM FS it is opened immediately, and fed to the zipfile module for unpacking. I loop over each file in the zip file and once again use safeopen() to create each file in it's proper directory.

Using the Modules 


The Filesystem module comes with a handy class designed to help with this very function. It is the FSImportHook class. It takes, as an argument to to it's constructor, a Filesystem object. Here is what the Source Code has to say about the class:
"Expose an FS object to the python import machinery, via a PEP-302 loader.
This module allows you to import python modules from an arbitrary FS object, by placing FS urls on sys.path and/or inserting objects into sys.meta_path. The main class in this module is FSImportHook, which is a PEP-302-compliant module finder and loader. If you place an instance of FSImportHook on sys.meta_path, you will be able to import modules from the exposed filesystem"
The short-hand of that is now the call  from syshelp import PrinterFuncs will also look inside the RAM FS for modules. The call to get_application_version() uses this module to print information about the version of the RAM FS. This is the result of running the bot.

  

Problems with Analysis

After stopping the bot, and rebooting there will be very little evidence left of the syshelp module, or any other part of the RAM FS for that matter, left to analyze. When paired with other counter-forensic efforts this method of delivery can prove quite difficult to track down. Imagine extending the bot above with some Sandbox checks, adding encryption to the zip file, using an pseudo-anonymous host, and periodically forcing the bot to shut itself down and reload the RAM FS would all frustrate the efforts to identify the capabilities of the underlying system.

Conclusion

The advancement in Malware technology requires modern computer forensic investigators to be even more rigid in their practice. Cutting power to a compromised machine should always be a last resort. When approaching a cold machine it is important to have accurate logs. If you can rely on your resource usage logs then you may be able to detect this type of behavior.programs which hold an abnormally high amount of RAM for a long period of time are good suspects. If that behavior is paired with unexplained network traffic logs (such as SSH traffic from a machine with no SSH application installed), it would strongly suggest the presence of this type of behavior.

No comments:

Post a Comment