Thursday, April 28, 2016

PowerForensics and Remote Machines

One of the biggest issues I have with PowerForensics is that it can only be executed on the local system.  This is great if you have something like F-Response which allows you to locally mount the hard drive of a remote system, after which you can execute PowerForensics commandlets against the remote system's hard drive.  But what if I don't have F-Response?

A few months ago I spoke with Jared Atkinson, the developer of PowerForensics, about this topic.  He mentioned the idea of reading the contents of the PowerForensics DLL into a byte array, establishing a remote PowerShell session and using reflection to load the byte array.  This doesn't actually import the commandlets into the session, but you do gain access to the various classes and their methods.

I tested this process, and was trying out PowerForensics in general but came across some issues.  While executing processing such as this in a PowerShell session on a remote system you end up using a large amount of processing and memory (the latter of which makes me cringe).  I saw spikes up to 1.5 GB of memory utilization depending on which methods you call.  I abandoned the idea, but eventually came up with an idea:

The idea

With F-Response, the client is reading the raw bytes of the hard drive and transmitting that to the examiner's machine where the data structures are then processed.  This means the processing and memory utilization is mostly on the examiner side.

So why not emulate this process in PowerForensics?

From what I can tell, it will take a bit of work to add this functionality to PowerForensics. For the time being I will supplement with some scripts I have already written, and work on the C# code as I can (or as Jared can).

Proof of Concept Process

Step 1: Establish a session with a remote system (easy enough)

PS> $Session = New-PSSession -ComputerName DeathStar

Step 2: Inject a function to get a read handle on local drives or volumes

In order to read the raw bytes directly from disk you have to utilize the CreateFile or CreateFileW Windows API (there is no way around this for a live system, or at least none known to me).  Rather than P/Invoke the C# code for this method, we want to use .NET Reflection to avoid runtime compilation.  Luckily I already wrote a function for this in PowerShell (you can find it here).  It creates a new function Get-FileStream, and accepts a parameter -DriverNumber or -VolumeLetter, acquires a read handle to the drive or volume, and returns a .NET FileStream object using that handle.

The following command executes the Get-FileStream PowerShell script in the Session we've already established:
PS> Invoke-Command -Session $Session -FilePath .\Get-FileStream.ps1
Then we can execute the Get-FileStream function and get a read handle and file stream on Drive 0:
PS> Invoke-Command -Session $Session -ScriptBlock { $FileStream = Get-FileStream -DriveNumber 0 } 

 Step 3: Inject a function to read chunks of disk and return a byte array

This is also pretty simple, it just needs to accept a FileStream, a Byte Offset, and a Length of bytes to return.  I also already have a function for this in PowerShell (you can find it here).

First we inject the function into the session:
PS> Invoke-Command -Session $Session -FilePath .\Get-BytesFromStream.ps1
Then we can execute the function to return the first 512 bytes of the hard drive as a byte array:
PS> $ByteArray = Invoke-Command -Session $Session -ScriptBlock { Get-BytesFromStream -Stream $FileStream -Offset 0 -Length 512 }

Step 4: Parse the Byte Array with PowerForensics

This part was a little tricky (only since I'm not great with C#).  Since the MasterBootRecord class is not exposed when you import the module, and there is no ByteArray input to Get-ForensicMasterBootRecord, we need to make some changes to the module.
I installed Visual Studio and downloaded the latest release's source code (from here).
After loading up the C# Project in Visual Studio, I opened MasterBootRecord.cs and made the MasterBootRecord class public:
public class MasterBootRecord
Examining the methods we see a Get method which takes a drivePath input.  We need to create a new Get method that accepts a Byte Array as input, which is easy enough:
public static MasterBootRecord Get(byte[] byteArray)
{
    // Read Master Boot Record (first 512 bytes) from disk
    return new MasterBootRecord(byteArray);
}
NOTE: Upon trying to build I get a weird error saying the PartionEntry class is less accessible than MasterBootRecord.PartitionEntry, so I modified the PartitionEntry class to also be public.

After building my project I import my new modified PowerForensics DLL into PowerShell:
PS> Import-Module .\PowerForensics.dll
Then I take a look at the now Public MasterBootRecord class, and its Get method:
PS> [PowerForensics.MasterBootRecord]::Get
OverloadDefinitions
-------------------
static PowerForensics.MasterBootRecord Get(string drivePath)
static PowerForensics.MasterBootRecord Get(byte[] byteArray)
Success!  Now we can see the Get method accepting a byte array as input.  Lets test it against our $ByteArray variable from earlier:
PS> [PowerForensics.MasterBootRecord]::Get([byte[]]$ByteArray) | Format-List
MbrSignature   :
DiskSignature  : 2A255ADF
CodeSection    : {51, 192, 142, 208...}
PartitionTable : {NTFS, NTFS, NTFS}
Success again.  We have just parsed the Master Boot Record of a remote system with minimal impact to the target system all using tools built into the Windows OS [since Windows 7].

Next Steps?

So now that the proof of concept is complete there are a few roads I can take.

Enhancement within PowerForensics

In order to include this process within PowerForensics itself there are some complex topics that need to be addressed. For example, if we want to have a -ComputerName switch on the commandlets, PowerForensics will need a helper function to generate a RemoteRunspace object, then create PowerShell instances within the Runspace and inject commands.

If we want to be minimally invasive in this process we need to find a way to reduce the amount of times these steps are performed, meaning creating this runspace and maintaining it, rather than creating it, executing the command, and disposing of it each time you run a commandlet.  In all honesty it will be easier (at least for me) to utilize the built in New-PSSession command to establish the session, then add a -Session switch to each commandlet and appropriate processing for a session.

This will be my ultimate goal, but since I am not great with C# it will likely take me a while to implement, which leads me to the other option:

Light Modification to PowerForensics, and an external "wrapper" script temporarily

This just means insuring the appropriate classes are public and have a public method that accepts a Byte Array as input.  Then I can create a PowerShell script that utilizes the Get-FileStream and Get-BytesFromStream functions I've already created to perform the actions outlined above.


I will likely work on the latter since it is quick, and hope to dedicate time to the former solution in the future.  If you have any questions, don't hesitate to ask!


UPDATE:
As I make changes to the PowerForensics project, I'll publish my changes to GitHub in my fork here:
https://github.com/davidhowell-tx/PowerForensics
Once it's established and working I'll talk to Jared about incorporating the changes into his main project.

1 comment:

  1. Great blog... Thanks for sharing steps to an effective cybersecurity incident response. I found this information very helpful.

    ReplyDelete