Ponder The Bits

Musings and confusings. All things DFIR.

Windows RDP-Related Event Logs: Identification, Tracking, and Investigation

Early in my DFIR career, I struggled with understanding how exactly to identify and understand all the RDP-related Windows Event Logs. I would read a few things here and there, think I understood it, then move on to the next case – repeating the same loop over and over again and never really acquiring full comprehension. That is until one day I finally got tired of repeating the same questions/research and just made a cheat sheet laying out the most common RDP-related Event ID’s that I’d encountered along with their relevance and descriptions. From that point on, as I sporadically encountered related questions/confusion from others in the community, I would simply refer to my cheat sheet to provide an immediate response or clarification – saving them from the hours of repeated questioning and research I had already done.

However, it seems the community continues to encounter the same struggle in identifying and understanding RDP-related Windows Event Log ID’s, where each is located, and even what some of them mean (no thanks to some of Microsoft’s very confusing documentation and descriptions). As such, I recently set out to try and find an easy route to the solution for this problem (i.e. hopefully find a single website to point to with all this information). Though I’ve found parts of the answer in posts here and there, each of them were missing parts of the puzzle (either missing ID’s, descriptions, explanations, and/or overall how they fit together in a chronological fashion). I will say JPCERTCC did an awesome job capturing a ton of information here, I just can’t quite decipher or discern the clear order of events and some appear out of order (at least how I have encountered them, but maybe I’m reading it wrong…). At any rate, as they say, necessity is the mother of invention.

So, I decided to create a blog post that I hope can serve as a succinct one-stop shop for understanding and identifying the most commonly encountered and empirically useful* RDP-related Windows Event Log ID’s/entries for tracking and investigating RDP usage on a Windows Vista+ endpoint. The Windows Event ID’s in the XP days were different than those in Vista+ Operating Systems. So, I decided to leave those out for now, but perhaps I will add them in the future.

*Yes, there are Event ID’s like 1146, 1147, and 1148 which look great in Microsoft’s documentation as a very useful source of information. However, I’ve yet to see (m)any of these commonly occurring in the wild.

I debated back and forth on the best way to sort/group these. Ultimately, in truly pragmatic fashion, I figured it would likely be most useful to sort them in the (chronological) order in which you might expect to find them. Ergo, the flow/section breakup is the following:

Network Connection
->-> Authentication
->-> Logon
->-> Session Disconnect/Reconnect
->-> Logoff

Network Connection

This section covers the first indications of an RDP logon – the initial network connection to a machine.

Log: Microsoft-Windows-Terminal-Services-RemoteConnectionManager/Operational

Log Location: %SystemRoot%\System32\Winevt\Logs\Microsoft-Windows-TerminalServices-RemoteConnectionManager%4Operational.evtx

Event ID: 1149
Provider Name: Microsoft-Windows-Terminal-Services-RemoteConnectionManager
Description: “User authentication succeeded”
Notes: Despite this seemingly clear-cut description, this event actually DOES NOT indicate a successful user authentication in the sense that many might expect (e.g., successful input and acceptance of a username and password). Instead, “authentication” in this sense is referring to successful network authentication, as in someone successfully executed an RDP network connection to the target machine and it successfully responded and displayed a login window for the next step of entering credentials. For example, if I launched the RDP Desktop Connection program on my computer, input a target IP, and hit enter, it would simply display the target system’s screen and produce an 1149 Event ID indicating I had successfully connected to the target, WELL BEFORE I even entered any credentials. So, repeat after me, “An Event ID 1149 DOES NOT indicate successful authentication to a target, simply a successful RDP network connection”.
TL;DR: NOT AN AUTHENTICATION. Someone launched an RDP client, specified the target machine (possibly with a username and domain), and hit enter to make a successful network connection to the target. Nothing more, nothing less.


Authentication

This section covers the authentication portion of the RDP connection – whether or not the logon is allowed based on success/failure of username/password combo.

Log: Security

Log Location: %SystemRoot%\System32\Winevt\Logs\Security.evtx

Event ID: 4624
Provider Name: Microsoft-Windows-Security-Auditing
LogonType: Type 3 (Network) when NLA is Enabled (and at times even when it’s not) followed by Type 10 (RemoteInteractive / a.k.a. Terminal Services / a.k.a. Remote Desktop) OR Type 7 from a Remote IP (if it’s a reconnection from a previous/existing RDP session)
Description: “An account was successfully logged on”
Notes: I thought this one was pretty straight forward – just look for Type 10 logons for RDP. However, in a bit more research, I discovered that often a Type 3 logon (for NLA) will occur prior to the Type 10 logon. In addition, I also discovered that RDP’ing to a system of which you’d previously RDP’ed and not formally logged off/out would instead yield a Logon Type 7 logon versus the Logon Type 10 we’d expect. This makes sense in a way in that a Logon Type 7 (“This workstation was unlocked”) is essentially what is happening. However, to delineate this from non-RDP Type 7 logons in which a person was sitting at the machine and just unlocked the machine, we can look for remote non-local IP’s in the IpAddress field.
TL;DR: User successfully logged on to this system with the specified TargetUserName and TargetDomainName from the specified IpAddress.


Event ID: 4625
Provider Name: Microsoft-Windows-Security-Auditing
LogonType: Type 3 (Network) when NLA is Enabled (and at times even when it’s not) and/or Type 10 (RemoteInteractive / a.k.a. Terminal Services / a.k.a. Remote Desktop)
Description: “An account failed to log on”
Notes: Why do we care about failures? Well, this is helpful in identifying (brute force) failure attempts and seeing when/where an attacker may be attempting stolen/compromised credentials. The Status/Sub Status Code will also be helpful in delineating legitimate failures (e.g. “expired password”) as well as possibly providing insight into attacker activity (e.g. repetitive “user name does not exist” codes could indicate brute force guessing by a tool and/or a more targeted lack of username knowledge/awareness in the environment by the attacker).
TL;DR: User failed to log on to this system with the specified TargetUserName and TargetDomainName from the specified IpAddress.

#ProTip(s):

1) When NLA is enabled, a failed RDP logon (due to wrong username, password, etc.) will result in a 4625 Type 3 failure. When NLA is not enabled, you *should* see a 4625 Type 10 failure.

2) Both of these entries also contain a “SubjectLogonID” or a “TargetLogonID” field. This ID is unique for each logon session and is also present in various other Event Log entries, making it theoretically useful for tracking/delineating a specific user’s activities, particularly on systems allowing multiple logged on users. However, do take note that a unique *LogonID is assigned for each session, meaning if a user connects, then disconnects (without logging out, thus simply ending the current session), then reconnects (i.e. starting a new session), they will be assigned a different unique *LogonID. All to say that a single user(name) may have multiple unique *LogonID’s to track depending how many sessions they’ve instantiated, not to mention Windows makes it very confusing sometimes with multiple 4624’s with different *LogonID’s for the same session. So, YMMV.

Additional References:

David Cowen’s Forensic Lunch Test Kitchen – RDP Testing (1 , 2 , 3)
Microsoft Forum Answer Re: RDP 4624 Type 3 Logons (link)

Logon

This section covers the ensuing (post-authentication) events that occur upon successful authentication and logon to the system.

Log: Microsoft-Windows-TerminalServices-LocalSessionManager/Operational

Log Location: %SystemRoot%\System32\Winevt\Logs\Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx

Event ID: 21
Provider Name: Microsoft-Windows-TerminalServices-LocalSessionManager
Description: “Remote Desktop Services: Session logon succeeded:”
Notes: This typically immediately precedes an Event ID 22 when the “Source Network Address” contains a remote IP address. Note that a “Source Network Address” of “LOCAL” simply indicates a local logon and does NOT indicate a remote RDP logon. this event with a “Source Network Address” of “LOCAL” will also be generated upon system (re)boot/initialization (shortly before the proceeding associated Event ID 22) . For remote RDP logons, take note of the SessionID as a means of tracking/associating additional Event Log activity with this user’s RDP session.
TL;DR: Indicates successful RDP logon and session instantiation, so long as the “Source Network Address” is NOT “LOCAL”.

Event ID: 22
Provider Name: Microsoft-Windows-TerminalServices-LocalSessionManager
Description: “Remote Desktop Services: Shell start notification received:”
Notes: This typically immediately proceeds an Event ID 21. Note that a “Source Network Address” of “LOCAL” simply indicates a local logon and does NOT indicate a remote RDP logon. This event with a “Source Network Address” of “LOCAL” will also be generated upon system (re)boot/initialization (shortly after the preceding associated Event ID 21).
TL;DR: Indicates successful RDP logon and shell (i.e. Windows GUI Desktop) start, so long as the “Source Network Address” is NOT “LOCAL”.

Session Disconnect/Reconnect

This section covers the various session disconnect/reconnect events that might occur due to either system (idle), network (network disconnect), or purposeful user (X out of the RDP window, Start -> Disconnect, Kicked off by another user, etc.) action.

Log: Microsoft-Windows-TerminalServices-LocalSessionManager/Operational

Log Location: %SystemRoot%\System32\Winevt\Logs\Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx

Event ID: 24
Provider Name: Microsoft-Windows-TerminalServices-LocalSessionManager
Description: “Remote Desktop Services: Session has been disconnected:”
Notes: The user has disconnected from an RDP session, when the “Source Network Address” contains a remote IP address. A “Source Network Address” of “LOCAL” simply indicates a local session disconnection and does NOT indicate a remote RDP disconnection. Note the “Source Network Address” for the source of the RDP connection. This is typically paired with an Event ID 40. Also take note of the SessionID as a means of tracking/associating additional Event Log activity with this user’s RDP session.
TL;DR: The user has disconnected from an RDP session, so long as the “Source Network Address” is NOT “LOCAL”.


Event ID: 25
Provider Name: Microsoft-Windows-TerminalServices-LocalSessionManager
Description: “Remote Desktop Services: Session reconnection succeeded:”
Notes: The user has reconnected to an RDP session, when the “Source Network Address” contains a remote IP address. A “Source Network Address” of “LOCAL” simply indicates a local session reconnection and does NOT indicate a remote RDP session reconnection. Note the “Source Network Address” for the source of the RDP connection. This is typically paired with an Event ID 40. Take note of the SessionID as a means of tracking/associating additional Event Log activity with this user’s RDP session.
TL;DR: The user has reconnected to an existing RDP session, so long as the “Source Network Address” is NOT “LOCAL”.


Event ID: 39
Provider Name: Microsoft-Windows-TerminalServices-LocalSessionManager
Description: “Session <X> has been disconnected by session <Y>”
Notes: This indicates that a user has formally disconnected from an RDP session via purposeful Disconnect (e.g., via the Windows Start Menu Disconnect option) versus simply X’ing out of the RDP window. Cases where the Session ID of <X> differs from <Y> may indicate a separate RDP session has disconnected (i.e. kicked off) the given user.
TL;DR: The user formally disconnected from the RDP session.


Event ID: 40
Provider Name: Microsoft-Windows-TerminalServices-LocalSessionManager
Description: “Session <X> has been disconnected, reason code <Z>”
Notes: In true Microsoft fashion, although the description is always “Session has been disconnected”, these events also indicate/correlate to reconnections, not just disconnections. The most helpful information here is the Reason Code (a function of the IMsRdpClient::ExtendedDisconnectReason property), the list of which can be seen here (and this pairs it with the codes to make it easier to read). Below are some examples of codes I encountered during my research.
0 – “No additional information is available.” (Occurs when a user informally X’es out of a session, typically paired with Event ID 24)
5 – “The client’s connection was replaced by another connection.” (Occurs when a user reconnects to an RDP session, typically paired with an Event ID 25)
11 – “User activity has initiated the disconnect.” (Occurs when a user formally initiates an RDP disconnect, for example via the Windows Start Menu Disconnect option.)
TL;DR: The user disconnected from or reconnected to an RDP session.


Log: Security

Log Location: %SystemRoot%\System32\Winevt\Logs\Security.evtx

Event ID: 4778
Provider Name: Microsoft-Windows-Security-Auditing
Description: “A session was reconnected to a Window Station.”
Notes: Occurs when a user reconnects to an existing RDP session. Typically paired with Event ID 25. The SessionName, ClientAddress, and LogonID can all be useful for identifying the source and associated activity.
TL;DR: The user reconnected to an existing RDP session.


Event ID: 4779
Provider Name: Microsoft-Windows-Security-Auditing
Description: “A session was disconnected from a Window Station.”
Notes: Occurs when a user disconnects from an RDP session. Typically paired with Event ID 24 and likely Event ID’s 39 and 40. The SessionName, ClientAddress, and LogonID can all be useful for identifying the source and associated activity.
TL;DR: The user disconnected from from an RDP session.


Logoff

This section covers the events that occur after a purposeful (Start -> Disconnect, Start -> Logoff) logoff.

Log: Microsoft-Windows-TerminalServices-LocalSessionManager/Operational

Log Location: %SystemRoot%\System32\Winevt\Logs\Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx

Event ID: 23
Provider Name: Microsoft-Windows-TerminalServices-LocalSessionManager
Description: “Remote Desktop Services: Session logoff succeeded:”
Notes: The user has initiated a logoff. This is typically paired with an Event ID 4634 (logoff). Take note of the SessionID as a means of tracking/associating additional Event Log activity with this user’s RDP session. This event with a will also be generated upon a system shutdown/reboot.
TL;DR: The user initiated a formal system logoff (versus a simple session disconnect).


Log: Security

Log Location: %SystemRoot%\System32\Winevt\Logs\Security.evtx

Event ID: 4634
Provider Name: Microsoft-Windows-Security-Auditing
LogonType: 10 (RemoteInteractive / a.k.a. Terminal Services / a.k.a. Remote Desktop) OR Type 7 from a Remote IP (if it’s a reconnection from a previous/existing RDP session)
Description: “An account was logged off.”
Notes: These occur whenever a user simply disconnects from an RDP session or formally logs off (via Windows Start Menu Logoff). This is typically paired with an Event ID 21 (RDP Session Logoff). I’ve also discovered these will also be paired (i.e. occur at the same time) with successful authentications (Event ID 4624). Why, I have no idea.
TL;DR: A user disconnected from, or logged off, an RDP session.


Event ID: 4647
Provider Name: Microsoft-Windows-Security-Auditing
Description: “User initiated logoff:”
Notes: Occurs when a user initiates a formal system logoff and is not necessarily RDP specific. You will need to use some reasoning and temporal analysis to understand if/when it is related to a system logoff via an RDP session or is from a local interactive session as there is no LogonType associated specify which it is.
TL;DR: The user initiated a formal logoff (NOT a simple disconnect).


Log: System

Log Location: %SystemRoot%\System32\Winevt\Logs\System.evtx

Event ID: 9009
Provider Name: Desktop Window Manager
Description: “The Desktop Window Manager has exited with code (<X>).”
Notes: Occurs when a user formally closes an RDP connection and indicates the RDP desktop GUI has been shut down as a result. This is useful to identify a closed/finalized RDP connection. Though, this event is not always produced for reasons I do not know.
TL;DR: A user has closed out an RDP connection.


Wrap-Up

Hopefully that provides a little better insight into some of the most common and (IME) most empirically useful RDP-related Event logs, when/where you might encounter them, what they mean, what they look like, and (most importantly) how they all fit together.

As a result of this post, Richard Davis (@richarddavisg, @13CubedDFIR) of 13Cubed on YouTube has also put together an RDP flow chart that is very helpful in visualizing the expected (though, not guaranteed) flow of these logs. Feel free to check out his short video walkthrough as well.

Generating File System Listings from the Command Line (with Full MACB Timestamps and Hashes)

!!IMPORTANT NOTE!!
———————————-
Before you go testing/implementing the commands that are described in this article, PLEASE ensure you first understand the following major caveat of performing certain actions/commands against files on a live system:

Reading a file changes its atime eventually requiring a disk write, which has been criticized as it is inconsistent with a read only file system.”
https://en.wikipedia.org/wiki/Stat_%28system_call%29#Criticism_of_atime

You DO NOT WANT TO DO THIS on a target of which you are attempting to perform forensic analysis.

Further reading on the matter
https://superuser.com/questions/464290/why-is-cat-not-changing-the-access-time

When in doubt and/or fear of possibly affecting a target system’s access timestamps, you should ensure the following is true before running the below commands:

  • The target file system (or whatever directory you are running this against) has been (re)mounted read-only and/or with the “noatime” and/or “relatime” mount parameters.

If you’re planning to run these commands against a physical disk (image), you can mount the target disk’s filesystem read-only via the following:

$ sudo mount -o ro,... </src/disk> </mount/point>

If you’re planning to run these commands against a live system, you can remount the live root filesystem/directory using the mount command’s --bind option via the following:

$ mkdir /mnt/remount

$ sudo mount --bind / /mnt/remount

Now, while the root filesystem/directory will be re-mounted to a new mount point, it will still be mounted read-write by default. So, before you go accessing anything on it, you need to re-mount it (yes, again) read-only, like so:

$ sudo mount -o remount,ro,bind,noatime /mnt/remount

Note that you don’t necessarily need the noatime attribute given that you’re already mounting the system read-only (and, in theory, should not be modifying any of the file timestamps upon access). However, I’m a “belt and suspenders” kind of guy. So, I’d rather have redundancy, even if unneeded, for the peace of mind.

———————————-

Disclaimer: I did not search the internet for a solution to this article’s challenge as I wanted to come up with one myself. Thus, a solution may already exist that is similar (or not). However, the point of the below article was not to just find a solution and move on. Rather, I wanted to walk readers through a problem statement, step-by-step piecing together a solution, thoroughly documenting and “teaching a man to fish” versus just giving out a fish. That said, I am in no way guaranteeing the below commands to work perfectly in ensuring it finds and properly processes every single file on the filesystem. In fact, when running this live, we are actively avoiding certain areas of the filesystem that are actively changing/ephemeral to minimize the error outputs. The only thing I can guarantee, in true *nix ad-hoc one-liner development, is (dis)function in ways beyond the imagination. ‘Tis a fact we just live with. This post simply describes *options* you can add to your toolkit that can always very much benefit from testing, troubleshooting, and improving.

In addition, while I attempted to identify and explain various aspects of each of my commands, I recognize that there are still improvements that can be done to this command. I attempted to find the balance of thorough explanation and efficiency while not bleeding over into the esoteric.

TL;DR – YYMMV*

*The first Y stands for “Yuuuuuuge”

Enough with the caveats and disclaimers, let’s get down to it…

Linux

Recently, a teammate posed a request to be able to generate a file listing of a directory in Linux showing the size and hash of each file in the output format of “ls -lhS” (list files in long format, with human-readable sizes, in decreasing size output).

As I hit Reply to the email, my initial thoughts were “Why don’t you use FLS?” as that is essentially the de-facto standard for producing a file system listing from an image. However, I got to thinking… FLS doesn’t really provide a comprehensive solution here for a few reasons:

  1. We need a command to run against a LIVE system and FLS only runs against a dead system image*
  2. FLS requires a second step to convert its output to bodyfile format for human-readable timestamps
  3. FLS doesn’t perform any file hashing

*Actually, this is not true. As one of my colleagues ever-so-graciously reminded me… Although it is not well documented, FLS can run against live systems. You can run it against a live Windows system by using named pipes, a la “fls [options] \\.\<X>:“, where <X> is a logical drive letter like C:, D:, etc.  And, a September 2011 SANS blog post here describes it in operation for Windows. To run it against a live Linux or Mac/OS X system, you may do so as such “fls [options] /dev/sd<X><Y>“, where <X> is the physical drive letter like /dev/sda, /dev/sdb, etc. and <Y> is the partition number like /dev/sda1, /dev/sda2, etc.

At any rate, the last two points remain, so it’s good thing I waited to hit send before looking like a dummy.

Instead, I took the challenge in attempting to come up with a command line one-liner to provide what was requested. Initially, I came up with the following:

$ find /path/to/dir -maxdepth 1 -type f -print0 | xargs -0 -r ls -lh | awk '{cmd="md5deep -q "$9; cmd | getline md5; close(cmd); cmd="sha1sum "$9; cmd | getline sha1; close(cmd); print $1","$2","$3","$4","$5","$6" "$7" "$8","$9","md5","sha1}' | awk '{$NF=""}1' | sed 's/ ,/,/g’ | sort -t',' -hr -k5


*You may need to Right-Click and Open/View Image in New Tab to see these inserted screenshots in full resolution. Sorry about that.

However, as we can see here, the timestamps produced from a simple “ls -lh” were rather lacking in both what was provided (solely last modification time by default) as well as precision (only precise to the second by default*, and a LOT can happen on a system in a singular second that we’d need to distinguish during an investigation).

== Sidebar 1 ==
You might be wondering why I am piping find’s output to xargs to execute the “ls -lh” against the results versus simply using find’s built-in “-exec” parameter that ostensibly does the same thing. In short, this is for performance reasons which you can read about at the below links.
https://www.everythingcli.org/find-exec-vs-find-xargs/
https://www.endpoint.com/blog/2010/07/28/efficiency-of-find-exec-vs-find-xargs
== /Sidebar 1 ==

== Sidebar 2 ==
Also note that all timestamps will be in the system’s local time. So, it would behoove you to collect that information from the system as well for future reference during analysis. This can be done a few different ways, as shown below:

== /Sidebar 2 ==

In light of the aforementioned issues (lacking additional timestamps and precision), I worked through a few different solutions and came up with the following which included not only timestamps with much greater precision (now with full nanosecond precision*) but also included all of the GNU “find” command’s printable timestamps (i.e., Last Modified, Last Accessed, and Inode Changed).

$ find /root -maxdepth 1 -type f -printf '%i,%M,%n,%g,%u,%s,%TY-%Tm-%Td %TT,%AY-%Am-%Ad %AT,%CY-%Cm-%Cd %CT,”%p"\n' | awk -F"," '{cmd="md5deep -q "$9; cmd | getline md5; close(cmd); cmd="sha1sum "$9; cmd | getline sha1; close(cmd); print $1","$2","$3","$4","$5","$6","$7","$8","$9","md5","sha1}' | awk '{$NF=""}1' | sed 's/ ,/,/g’ | sort -t',' -hr -k5

**Note that printf’s time field format is precise to 10 digits, while nanoseconds are (by definition) only precise to 9 digits. Thus, it is appending a 0 in the 10-th digit spot. Why? I frankly don’t know. I mean… uhh… “the reason of which will be left as an exercise to the reader.” 🙂

== Sidebar 3 ==
*I later discovered that you can show timestamps with full nanosecond resolution in ls via the “–full-time” parameter as I will show below.

$ ls -l --full-time
total 55088
drwxr-xr-x 2 root root 4096 2017-11-22 14:22:30.165725454 -0800 Desktop

== Sidebar 3 ==

At any rate, we’re making progress, but we’re still missing something. What about Inode (File) Creation? Is that not recorded in Linux? In short, Ext3 filesystems only record Last Modified (mtime, Last Accessed (atime), and Inode Changed (ctime), while Ext4 filesystem (on which a large majority of Linux distress operate) fortunately include the additional Inode Creation time (crtime). Lucky for us, I am doing this on an Ext4 filesystem, so we should be seeing those times if they’re implemented and recorded, right? You’d think so… but you’d be wrong.

Unfortunately, Linux decided not to implement an easy way (aka a natively integrated API) to view/include these (crtime) timestamps in various tools’ output (as seen here in the “find” command, and shortly in the “stat” command). Alas, FRET NOT, as there is a way to extract this timestamp using the debugfs utility. Intended as a “ext2/ext3/ext4 file system debugger”, it provides a “-R” option to execute a given command for debugging purposes. We will (ab)use this option to extract more information (i.e. the crtime timestamp) from the “stat” command than is originally provided by running the command on its own.

First, we will run “stat” against a file:

$ stat /root/VMwareTools-10.1.15-6627299.tar.gz
File: /root/VMwareTools-10.1.15-6627299.tar.gz
Size: 56375699 Blocks: 110112 IO Block: 4096 regular file
Device: fe01h/65025d Inode: 405357 Links: 1
Access: (0444/-r--r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2018-01-12 15:16:03.524147591 -0800
Modify: 2017-11-24 17:38:44.799279489 -0800
Change: 2017-11-24 17:38:44.799279489 -0800
Birth: -

Now, we will use the “debugfs” command to get the Inode/File Birth (crtime) timestamp. Keep in mind, you will need to provide the volume/partition on which the referenced file resides as a parameter to the command, otherwise the command will not work (namely yielding a “No such file or directory while opening filesystem” error). For my example below, my system is using LVM volumes and the file we’re querying resides on my root “k2–vg-root” LVM volume/partition.

$ debugfs -R 'stat /root/VMwareTools-10.1.15-6627299.tar.gz' /dev/mapper/k2--vg-root
Inode: 405357 Type: regular Mode: 0444 Flags: 0x80000
Generation: 763788199 Version: 0x00000000:00000001
User: 0 Group: 0 Project: 0 Size: 56375699
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 110112
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x5a18c9a4:be902604 -- Fri Nov 24 17:38:44 2017
atime: 0x5a5941b3:7cf76e1c -- Fri Jan 12 15:16:03 2018
mtime: 0x5a18c9a4:be902604 -- Fri Nov 24 17:38:44 2017
crtime: 0x5a18c9a4:5740e928 -- Fri Nov 24 17:38:44 2017
Size of extra inode fields: 32
Inode checksum: 0x53c3b2b6
EXTENTS:
(0-10239):1859584-1869823, (10240-12287):1873920-1875967, (12288-13763):1902149-1903624

There’s actually a lot of great output here that can be very useful to us as forensic analysts, but we really only need the crtime for our purposes today. So, we can do a little command-line fu to just extract the human readable portion of the crtime timestamp we care about.

$ debugfs -R 'stat /root/VMwareTools-10.1.15-6627299.tar.gz' /dev/mapper/k2--vg-root |& sed -n 's/^crtime.*- \(.*\)$/\1/p'
Fri Nov 24 17:38:44 2017

To go a bit further and match stat’s default timestamp formatting, we can do a bit more command-line fu to yield the following:

$ date +"%Y-%m-%d %H:%M:%S.%N %z" -d "$(debugfs -R 'stat /root/VMwareTools-10.1.15-6627299.tar.gz' /dev/mapper/k2--vg-root |& sed -n 's/^crtime.*- \(.*\)$/\1/p')"
2017-11-24 17:38:44.000000000 -0800

Great, now we have a crtime (Inode/File Creation) timestamp we know and love. But, wait… anyone else noticing something here? The nanoseconds are all zeroes. Hmmm. Well, if we trace our process back a bit, we can see that this is because we are attempting to produce a nanosecond-precision datetime object from a source that obviously doesn’t include it. Obviously, we can’t extract nanosecond precision from an input that doesn’t contain it. So, where do we go from here?

Well, if we look back at the crtime output (crtime: 0x5a18c9a4:5740e928 -- Fri Nov 24 17:38:44 2017) we can see that the second column there contains two sets of hex digits (0x5a18c9a4:5740e928) delineated by a colon. Could it be that this is simply a hex version of the decimal and nanosecond epoch timestamps? Oh, it could, and it is. It turns out the first entry (previous to the colon) is the epoch seconds and the second entry (after the colon) is the nanoseconds. So, we’ll need to go back to our command and alter it to extract, convert, and construct the nanosecond epoch timestamp we’re looking to produce.

The below command extracts both the first and second set of hex digits (epoch seconds and epoch nanoseconds, respectively), converts both of the hex sets to decimal, converts the epoch seconds to a human-readable datetime object using Awk’s strftime formatting, and then divides the nanoseconds portion by four (essentially performing a two-bit shift) as is necessary per Hal Pomeranz’s article on EXT4 Timestamps here.

**Big thanks to Dan (aka @4n6k) for his assist here in leading me to Hal’s article as I was banging my head on this last portion for a bit until discovering this bitwise shift needed to be done. Also, of course, huge thanks to Hal (@hal_pomeranz) as well for this monumental efforts in painstakingly documenting EXT4 Timestamps and these nuances.**

$ debugfs -R 'stat /root/VMwareTools-10.1.15-6627299.tar.gz' /dev/mapper/k2--vg-root |& sed -n 's/^ mtime: \(0x[0-9a-f]+\):\([0-9a-f]+\).*/\1.0x\2/p' | awk -F'.' '{n = strtonum($2) / 4; print strftime("%Y-%m-%d %T",strtonum($1))"."n}'
2017-11-24 17:38:44.799279489

AWESOME. We can now successfully extract the crtime file timestamps programmatically.

Now, let’s put it alllll together and build our one-liner that’s going to help us reach our original goal here of outputting a file listing with all the available timestamps (in MACB order) as well as file hashes (MD5 and SHA1). We will be using the largely native md5sum and sha1sum utilities to produce our hashes so as to avoid the need to install any additional third-party tools.

And, here it is. I give the ugliest (most epic?) command to date to output everything we’ve been looking for:

# find /path/to/dir -maxdepth 1 -type f -printf '%i#%M#%n#%g#%u#%s#%TY-%Tm-%Td %TT#%AY-%Am-%Ad %AT#%CY-%Cm-%Cd %CT#"%p"\n' | awk -F"#" '{cmd="debugfs -R '\''stat <"$1">'\'' /dev/mapper/k2--vg-root 2>/dev/null | grep -Po \"(?<=crtime: 0x)[0-9a-f]+:[0-9a-f]+(?=.*)\" | tr \":\" \" \" | { read e n; echo \"$(date +\"%Y-%m-%d %H:%M:%S\" -d @$(printf %d 0x$e)).$(printf %09d $(( $(printf %d 0x$n) / 4 )) )\";}"; cmd | getline crt; close(cmd); cmd=“md5sum "$10" | cut -d \" \" -f1"; cmd | getline md5; close(cmd); cmd="sha1sum "$10" | cut -d \" \" -f1"; cmd | getline sha1; close(cmd); print $1","$2","$3","$4","$5","$6","$7","$8","$9","crt","$10","md5","sha1}' | sed 's/ *,/,/g’ | sort -t',' -hr -k6

Note that we had to do a few things to deal with various unsavory characters that may occur within filenames (e.g., spaces, parentheses, comma’s, etc.). First, we can’t use comma’s as our print output delimiter as filenames with comma’s would then screw up our Awk parsing. So, we needed to use a non-standard character (i.e. one we would never expect to see in our output). In this case I chose “#”, but you could use whatever you’d like. To get our debugfs stat output, as well as MD5 and SHA1 hashes, we utilize Awk’s ability to execute commands and retrieve the output with its getline function. You may notice that the debugfs stat command one-liner strings together a RegEx with a Lookbehind assertion, along with some bash read/print/date functions in order to translate the hex -> decimal -> formatted human-readable datetime for us.

So… How ‘bout them apples?? WE’VE DONE IT!! All of that painstaking work has paid off in spades. We’ve put together a command that is essentially FLS on steroids (with hashes) that we can run against BOTH a live and dead system! THIS IS WHAT DREAMS ARE MADE OF!

If you’d like to use this* as an FLS replacement against a (dead) system image, simply mount the image’s file system (Read-Only, of course), adjust the command to point to the root of the file system, remove the last “sort” command (as we can do that later during analysis as needed), and simply output to CSV. Like so:

*AGAIN, I PROVIDE NO GUARANTEES HERE, only a best effort here and initial pass on doing this. For example, in one of my test VM’s I kept getting what appeared to be random “sh: 1: printf: 0x: not completely converted” errors that output a default crtime date of “1969-12-31 16:00:00.000000000” which makes no sense as I’ve verified that the crtimes on these files are present and properly output via stat/debugfs and a manual conversion of the values yields success. Yet, it did not happen in other VM’s. So, just a heads up in case something goes awry on your end.

# echo "Inode,Permissions,HardLinks,GroupName,UserName,Size(Bytes),LastModified,LastAccess,Changed,Birth,Filename,MD5,SHA1" > FS_Listing.csv

# find / -xdev ! -path '/var/run/*' ! -path '/run/*' ! -path '/proc/*' -type f -printf '%i#%M#%n#%g#%u#%s#%TY-%Tm-%Td %TT#%AY-%Am-%Ad %AT#%CY-%Cm-%Cd %CT#"%p"\n' | awk -F"#" '{cmd="debugfs -R '\''stat <"$1”>'\'' /dev/mapper/k2--vg-root 2>/dev/null | grep -Po \”(?<=crtime: 0x)[0-9a-f]+:[0-9a-f]+(?=.*)\" | tr \":\" \" \" | { read e n; echo \"$(date +\"%Y-%m-%d %H:%M:%S\" -d @$(printf %d 0x$e)).$(printf %09d $(( $(printf %d 0x$n) / 4 )) )\";}"; cmd | getline crt; close(cmd); cmd="md5sum "$10" | cut -d \" \" -f1"; cmd | getline md5; close(cmd); cmd="sha1sum "$10" | cut -d \" \" -f1"; cmd | getline sha1; close(cmd); print $1","$2","$3","$4","$5","$6","$7","$8","$9","crt","$10","md5","sha1}' | sed 's/ *,/,/g' >> FS_Listing.csv

Note that we are:

  1. First writing a “header” line to the CSV file for easier reference during analysis
  2. Now operating from a ROOT prompt (e.g. the leading “#” denoting a root prompt versus the “$” denoting a standard user prompt) as we will need root privileges to access/read the entire filesystem
  3. Avoiding traversal of external mounted filesystems (i.e. network shares, external media, etc.) via the “-xdev” parameter, and
  4. Specifically avoiding a few directories via the “! -path /path/to/avoid/*” as the aforementioned paths store ephemeral process information we aren’t interested in collecting (at least not for our purposes here).

Excel ProTips: If you are using Excel to review the CSV file, be aware that Excel only displays time precision down to the milliseconds (and no further). Alas, you will be missing everything beyond the 3 digits past the decimal place. In order to display this millisecond precision, you will want to highlight all the MACB timestamp cells, right-click, select Format Cells, select Custom under the Number tab, input the Type as mm/dd/yyyy hh:mm:ss.000 (or whatever you like, the important part is the timestamp’s trailing .000), then click OK. And, Voila!, millisecond timestamp precision. Obviously, it is most valuable to be able to actually see the full nanosecond precision but at least it’s something for those who are die-hard Excel fans.

Also, for whatever reason, Excel does something weird with displaying some of the leading permissions entries by prepending a “=“ to them. Why, I have no idea. Maybe Excel gets confused and sometimes tries to interpret “-“ text as an intended negative or minus sign and thus attempts to “fix” it for us (in true Microsoft fashion) by denoting it as a formula and prepending the “=”? ¯\_(ツ)_/¯ For whatever reason, it’s happening (see below). Just be aware that this is something Excel is adding and that it is NOT present in the original CSV if you’re using any other tools for analysis.

Now… how about doing this on a Mac? OF COURSE we’re going to translate this over…

Mac

If you’ve been reading my blog (and/or working between Linux and Mac systems for a while), you’ll know that things do not often translate directly to from Linux (GNU) to Mac (BSD) as the core utilities seem to always differ just enough to make your life a pain when working between systems. And, this situation is no different.

As you might assume, we are going to use the “stat” command again as the basis for extracting all of our timestamps. However, we will of course be using the BSD stat command and not the GNU version as used in Linux. Below is the default BSD “stat” output (the format of which is of course different from GNU “stat” because… why not):

$ stat .vimrc
16777220 1451064 -rw-r--r-- 1 jp staff 0 54 "Sep 22 12:08:02 2017" "Dec 26 10:36:32 2016" "Dec 26 10:36:32 2016" "Dec 26 10:12:15 2016" 4096 8 0 .vimrc

The upside here is that, by default, BSD “stat” outputs all 4 HFS+ filesystem timestamps we care about! Great, but which are what? Saving you some time and research, BSD “stat” outputs timestamps in the following order by default:

Last Accessed, Last Modified, Inode Changed, Inode Birth - (A,M,C,B)

Just as we discussed earlier, these reflect the time file was last accessed, the time the file was last modified, the time the inode was last changed, and birth time of the Inode. So, in order to get them into an order we like (okay, the order that I like) such as MACB (because this is how we most often see the timestamp acronym), we can perform the following:

$ stat -t "%b %-d %Y %T %Z" .vimrc | awk -F'"' '{print "Modified: "$4; print "Accessed: "$2; print "Changed: "$6; print "Birth: "$8}'
Modified: Dec 12 2016 10:36:32 PST
Accessed: Sep 9 2017 12:08:02 PDT
Changed: Dec 12 2016 10:36:32 PST
Birth: Dec 12 2016 10:12:15 PST

And, there we have it, full timestamp information in the order we (I) like it. Do note that HFS+ timestamp precision is only down to the second as it does not implement nanosecond resolution like some other filesystems. And, for that, we do a hearty ¯\_(ツ)_/¯. Fortunate for us in the future, APFS has implemented nanosecond timestamp resolution. But, that’s a separate discussion you can read about here.

Now that we’ve taken care of that timestamp acquisition and formatting issue, let’s move on to building the command line statement we’re going to run. While GNU’s find utility provides a “-printf” option to format and customize find’s output, BSD’s find lacks such an option. Alas, we will need to be a bit more creative here. What I ended up doing here was piping find’s output to BSD’s “stat” command which DOES provide a formatting option “-f” that we can utilize. But, again, it’s not as straight forward as just copy/paste of the previous formatting we used on Linux because OF COURSE the print delimiters don’t directly translate over either.

So, first we need to translate over the previous GNU print formatting string ('%i#%M#%n#%g#%u#%s#%TY-%Tm-%Td %TT#%AY-%Am-%Ad %AT#%CY-%Cm-%Cd %CT#”%p”\n') into the correlated BSD values, which end up being the following:

'%i^%Sp^%l^%Sg^%Su^%z^%Sm^%Sa^%Sc^%SB^"%N"'

I’m using “^” as a delimiter this time instead of “#” as I ended up actually having files with hash/pound signs in their name on my system (THANKS, ATOM APP). Also, note that I’m using single-tick’s for the print statement and using full quote encapsulation for the filename. I’m doing this in order to avoid issues with dollar signs ($) in filenames. Again, no, using such delimiters is not very pretty, but it’s required. And, if you for some reason have files with “^” in their names, it will break this as well. So, YMMV.

$ find /Users/jp -maxdepth 1 -type f -print0 | xargs -0 stat -t "%Y-%m-%d %H:%M:%S" -f '%i^%Sp^%l^%Sg^%Su^%z^%Sm^%Sa^%Sc^%SB^"%N"' | sort -t'^' -nr -k6

Note that I also needed to specify stat’s “-t” argument to format the datetime output in the printf statement.

So, there we have it, listing directory output in decreasing file size.

Now, on to calculating and appending our MD5 and SHA1 hashes to the output. For this, we will use BSD’s native md5 and shasum utilities. Using much of the same structure from our Linux one-liner, we then come up with the following:

# find /Users/jp -maxdepth 1 -type f -print0 | xargs -0 stat -t "%Y-%m-%d %H:%M:%S" -f '%i^%Sp^%l^%Sg^%Su^%z^%Sm^%Sa^%Sc^%SB^"%N"' | awk -F"^" '{cmd="md5 "$11" | cut -d \" \" -f4"; cmd | getline md5; close(cmd); cmd="shasum "$11" | cut -d \" \" -f1"; cmd | getline sha1; close(cmd); print $1","$2","$3","$4","$5","$6","$7","$8","$9","$10","$11","md5","sha1}' | sort -t',' -nr -k6

And there we have it, a directory listing with hashes sorted in decreasing file size. Note that were are now again in a root shell to avoid file access permissions.

Now, on to the final one-liner to do a full filesystem listing:

# echo "Inode,Permissions,HardLinks,GroupName,UserName,Size(Bytes),LastModified,LastAccess,Changed,Birth,Filename,MD5,SHA1" > OSX_Listing.csv

# find -x / -type f -print0 | xargs -0 stat -t "%Y-%m-%d %H:%M:%S" -f '%i^%Sp^%l^%Sg^%Su^%z^%Sm^%Sa^%Sc^%SB^"%N"' | awk -F"^" '{cmd="md5 "$11" | cut -d \" \" -f4"; cmd | getline md5; close(cmd); cmd="shasum "$11" | cut -d \" \" -f1"; cmd | getline sha1; close(cmd); print $1","$2","$3","$4","$5","$6","$7","$8","$9","$10","$11","md5","sha1}' >> OSX_Listing.csv

Note that OS X find’s “-x” parameter is equivalent to GNU’s “-xdev”, meaning not to enumerate external disks/mounted filesystems.

When I ran this against my full system, I realized it choked on files containing “$”. So, I needed to add in some Awk substitution to escape the dollar sign with a leading “\” so that the shell wouldn’t attempt to interpret the “$” as (mis)indication of a variable when it was simply a dollar sign in a file name. Full disclosure: it may also choke on other files with special characters, but I’ve shown you how you can use Awk substitution as a way around it. So, update/augment this as needed.

Conclusion

Sooooo, Wow. That was a bit of hard work. Actually, it was A LOT of hard work, much of which was not captured in the blog post for the sake of brevity and everyone’s sanity, as it surely tested mine many a time. However, hopefully you can see the value of spending the time building effective and efficient processes on the front end so you are not always paying for it on the back end. Suffice to say, IMHO, sometimes it is ok to work harder and not smarter, when the process will help you become more of the latter.

If you wanted to run any of the above commands against a mounted evidence image, you’d simply specify its mount point in the find command, like so:

# find /mnt/point/ ...

Note that we don’t use the “-xdev” or “-x” parameter here as we do actually want it to enumerate an external filesystem (i.e. our mounted evidence image’s filesystem which is likely from an external disk or network share).

And, now that we’ve walked through doing all of that the hard way using native Linux utilities, I will say that another filesystem enumeration capability to include hashes has also been built in Python in Jim Clausing’s macrobber.py script. However, due to Python’s os.stat call limitations, this script does not/cannot pull the btime (aka crtime) attributes that we are able to identify and extract through our commands here. Nonetheless, it is another option, which is always great.

Thanks to everyone for hanging in there through this whole post. It obviously takes way more time to painstakingly walk through every step of a process; however, I feel it is well worth my time to teach people to fish, and hopefully you all do too.

Decompressing and Extracting Artifacts from Windows 8 / Server 2012+ Hibernation Files

Windows Hibernation files from a hibernated (or sometimes simply shutdown) machine can be a wealth of information in investigations, often containing a nearly complete memory image of what was running on the system prior to hibernation (shutdown). For years, many in the DFIR community have pillaged the hibernation file for a variety of artifacts, ranging from extraction of simple strings to the use of more specialized analysis tools like Mathieu Suiche’s Hibr2Bin and Volatility. However, since Windows 8, you may (or may not) have noticed that the number of artifacts extracted by the usual methods have at times ranged from substantially less to nearly nonexistent.

So, what gives? Does Microsoft simply no longer store (as many) artifacts in there anymore? I mean, if our trusted tools can’t identify/extract it, it’s surely not there, right? Well, while it would be easy to simply move on and accept the loss of artifacts, I’d like to take the time to dig a bit deeper and find out what is going on here.

Background

Windows hibernation files are compressed at shutdown. Starting in Windows XP, Microsoft began using the Xpress compression algorithm with a defined data structure of which many tools (including the aforementioned Hibr2Bin and Volatility) had down pat to properly decompress and extract/display the contained artifacts. However, beginning in Windows 8/Server 2012, Microsoft changed things up, namely adding a Huffman encoding variant and changing the data structures a bit. This, in turn, rendered existing decompression tools severely hindered without a rewrite to account for the changes. As of this writing, while it appears there is some work in progress to update both Hibr2Bin and Volatility to update the decompression methods, neither of these tools can successfully fully decompress Windows 8+ Hibernation files. Though, I believe the DFIR community eagerly awaits updates to these tools as they have both proven to be incredibly useful in their own respects.

== Sidebar ==

For those interested in the nitty gritty details of Windows hibernation files, refer to Joe T Sylve, Vico Marziale, and Golden G. Richard III’s excellent paper titled “Modern windows hibernation file analysis” that describes in great detail the various Windows hibernation file formats/structures, along with their testing methodology using Hibr2Bin to attempt to decompress each Windows version’s hibernation files. You will see the issue of the changed compression structure evident in their testing with Hibr2Bin in that they saw it only produced a subset of expected decompressed artifacts and “surmised that Hibr2Bin must only decompress the first restoration set of pages that are restored by the boot loader, ignoring the second set of kernel-restored pages.” We can assume the same and/or similar issue(s) of not yet being able to properly read and entirely parse the new data structures also apply to Volatility.

== /Sidebar ==

So, the information is still there, we just have to figure out a different way (or use a different tool) to get to it. But, before we go on, let’s recap a bit about our previous go-to Hibernation file decompression tools.

On September 20, 2016, Matthieu Suiche released (open sourced) his Hibr2bin (Hibernation file decompression) and DumpIt (memory image collection) utilities. However, as of this latest release, the Hibr2bin tool only supports comprehensive decompression of Hibernation files up through Windows 7. Though it states support for Windows 8 and 10 systems, it has been demonstrated to not fully decompress the file (of which Mathieu is currently aware). Though, the tool is open source now, so the community has full access to build these changes in themselves without relying on Mathieu to do it.

Though Volatility’s imagecopy plugin will work to decompress/convert Windows XP through Windows 7Hibernation files to a raw memory dump for analysis, it does not currently support Windows 8/Server 2012+ Hibernation file decompression (https://github.com/volatilityfoundation/volatility/issues/25). That said, it is still able to properly parse and analyze a decompressed Hibernation files through the latest version(s) of Windows 10/Server 2016, should you be able to decompress the Hibernation file by some other means/tool.

So, where does this leave us if our go-to tools no longer work to fully decompress Hibernation files from Windows 8/Server 2012+ systems? Are we up Schitt’s Creek (HILARIOUS show BTW, please do check it out) without a paddle?

Enter Arsenal Recon.

The folks that brought you Registry Recon and Arsenal Image Mounter have since developed Hibernation Recon, which as of this post appears to be the only tool currently available that supports comprehensive decompression of Windows hibernation files through the latest Windows 10 releases.

I’ve extracted the below pertinent information from their web page:

Hibernation Recon has been developed to not only support memory reconstruction from Windows XP, Vista, 7, 8/8.1, and 10 hibernation files, but to properly identify and extract massive volumes of information from the multiple types (and levels) of slack space that often exist within them…

Features:
* Windows XP, Vista, 7, 8/8.1, and 10 hibernation file support
* Active memory reconstruction
* Identification and extraction of multiple levels of slack space
* Brute force decompression of partially overwritten slack
* Segregation of extracted slack based on particular hibernations
* Proper handling of legacy hibernation data found in modern hibernation files
* NTFS metadata recovery with human-friendly decoding
* Parallel processing of multiple hibernation files”

As of the March 7, 2017 release, the team currently offers both a paid and free version…

Hibernation Recon is priced at just $399 to ensure every digital forensics expert can properly arm themselves. If Hibernation Recon is run without a license, a “Free Mode” is provided which supports the extraction of active contents from both legacy and modern Windows hibernation files.

As a major bonus and rarity for the “Free” version of a tool, the “Free Mode” version is allowed for both personal AND commercial use. NOICE! Big kudos to these guys for allowing this!

Do note that the hibernation slack & NTFS metadata recovery functionality is only available within the professional version, which I would imagine could be very useful as well. However, for the sake of brevity, access, and initial focus of my testing (i.e., successful comprehensive decompression) I am simply testing the “Free Mode” version. Perhaps I can get my hands on the Pro version at some point to test those additional recovery features…

At any rate, I downloaded the tool from the website and got on my way to testing using the “Free Mode”.

Testing

For testing, I first enabled hibernation via the command line (> powercfg -h on) and then generated three different hibernation files by performing the following procedures on my Windows 10 Pro desktop system:

Booted

  1. Enable Hibernation
  2. Hibernate the machine via Right-click Windows button -> Hibernate
  3. Boot the machine
  4. Log into the system and copy the existing hiberfil.sys via FTK Imager

Hibernated

  1. Enable Hibernation
  2. Hibernate the machine via Right-click Windows button -> Hibernate
    1. This can also be done via “shutdown /h” on the command line
  3. Boot into live linux environment and copy the existing hiberfil.sys

Shutdown

  1. Enable Hibernation
  2. Shut down the system
  3. Boot into live linux environment and copy the existing hiberfil.sys

With the 3 resulting hibernation files generated by the above methods, I could now test and measure the following:

  1. If, and how well, Hibernation Recon decompresses each Hibernation file
  2. How much information, if any, each Hibernation file contains (i.e., which collection method yields the greatest amount of artifacts and information)

With the data and aforementioned goals in hand, I ran Hibernation Recon* against each Hibernation file so that I had both a native compressed file and (supposedly) fully decompressed file for performing comparison.

*Note: I did not test Hibr2bin against these images as Sylve, Marziale, and Richard III had already done so as outlined in their previously mentioned paper on the subject.

I then ran the following tools against both the native compressed and decompressed images for each collection method (booted, hibernated, and shutdown) to collect a relatively representative set of results for quantitative comparison*:

*I’m no data scientist, I just attempted a testing methodology that I considered to have the greatest layman’s ROI

  1. GNU Strings
    Not too much to explain here, I simply wanted to identify all occurrences of strings (both unicode and ASCII) within each image.
  2. Page_Brute
    This tool is designed to run Yara signatures against each block (4096 bytes) of a pagefile. However, I wanted to test it against the Hibernation file as it also uses 4096 byte pages and well… there’s really nothing to lose in testing it. I added signatures to the default_signatures.yar ruleset file to also identify IP addresses, Email addresses, and URL’s – all useful artifacts of which we’d expect to find in a memory image and thus I figured a good method for comparison.
  3. Bulk_Extractor
    Copied/pasted directly from the user manual, “bulk_extractor operates on disk images, files or a directory of files and extracts useful
    information without parsing the file system or file system structures. The input is split into pages and processed by one or more scanners.
    ” It is a beautiful thing in that it is EXTREMELY well threaded and as such will hog as much of your system’s resources as it is allowed. Though caution must be exercised here in light of that, letting it run full throttle on a dedicated machine yields some insanely fast (not to mention very intelligent) artifact parsing and extraction. The quantity of identified and extracted artifacts are a good measure of how much decompressed data (in terms of Hibernation file decompression, not decompression of standard files like zip, rar, etc. that is also built into the tool) is available within the image.
  4. Volatility 2.6
    Run Volatility’s imagecopy plugin against the native compressed image to attempt to decompress/convert it to a raw image. Then, run Volatility with the appropriate system profile against the Volatility decompressed/converted image and the Hibernation Recon decompressed image. Various plugin output can then be compared across images to see which produces the greatest amount of artifacts parsed from the memory image.

Results

== Legend ==
JPW10_hiberfil.sys = Hibernation file from Shutdown system
JPW10_hiberfil.sys_2 = Hibernation file from Hibernated system
JPW10_hiberfil.sys_3 = Hibernation file from Booted system
ActiveMemory.bin = Decompressed and reconstructed memory image via Hibernation Recon

Strings, Page_Brute, and Bulk_Extractor data:
Hibernation_Testing_Results

“Booted” System Results
See spreadsheet for Strings, Page_Brute, and Bulk_Extractor data.

As we can verify here, the contents of the Hibernation file are zeroed upon system boot, which is stated to be the case in Windows 8+ systems. Thus, as we’d expect, no results from using any of the tools and no reason to use Hibernation Recon against the Hibernation file (nothing there to decompress).

“Hibernated” System Results
See spreadsheet for Strings, Page_Brute, and Bulk_Extractor data.

Volatility
Attempt to convert/decompress native hibernation file to use with Volatility…
$ python ~/volatility/vol.py -f Hibernated/JPW10_hiberfil.sys_2 --profile=Win10x64_14393 imagecopy -O Hibernated/Output/JPW10_hiberfil.sys_2_conv

Run pslist plugin against resulting file…
$ python ~/volatility/vol.py -f Hibernated/Output/JPW10_hiberfil.sys_2_conv --profile=Win10x64_14393 pslist
Volatility Foundation Volatility Framework 2.6
No suitable address space mapping found
Tried to open image as:
MachOAddressSpace: mac: need base
LimeAddressSpace: lime: need base
WindowsHiberFileSpace32: No base Address Space
WindowsCrashDumpSpace64BitMap: No base Address Space
WindowsCrashDumpSpace64: No base Address Space
...
IA32PagedMemory: Incompatible profile Win10x64 selected
OSXPmemELF: ELF Header signature invalid
FileAddressSpace: Must be first Address Space
ArmAddressSpace: No valid DTB found

As you can see, it was not successfully decompressed and is thus not usable.

Now, we will see what happens when we run plugins against the Hibernation Recon (HR) decompressed image…
$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin imageinfo
Volatility Foundation Volatility Framework 2.6
INFO : volatility.debug : Determining profile based on KDBG search...
Suggested Profile(s) : Win10x64_10586, Win10x64_14393, Win10x64, Win2016x64_14393
AS Layer1 : Win10AMD64PagedMemory (Kernel AS)
AS Layer2 : FileAddressSpace (/mnt/hgfs/G/Hibernation_Testing/Hibernated/Output/ActiveMemory.bin)
PAE type : No PAE
DTB : 0x1ab000L
KDBG : 0xf800a82f0500L
Number of Processors : 8
Image Type (Service Pack) : 0
KPCR for CPU 0 : 0xfffff800a8342000L
KPCR for CPU 1 : 0xffffda019e020000L
KPCR for CPU 2 : 0xffffda019e09b000L
KPCR for CPU 3 : 0xffffda019e116000L
KPCR for CPU 4 : 0xffffda019e193000L
KPCR for CPU 5 : 0xffffda019e1d2000L
KPCR for CPU 6 : 0xffffda019e291000L
KPCR for CPU 7 : 0xffffda019e310000L
KUSER_SHARED_DATA : 0xfffff78000000000L
Image date and time : 2017-03-08 02:12:21 UTC+0000
Image local date and time : 2017-03-07 18:12:21 -0800

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin kdbgscan
...
**************************************************
Instantiating KDBG using: Unnamed AS Win10x64_14393 (6.4.14393 64bit)
Offset (V) : 0xf800a82f0500
Offset (P) : 0x469cf0500
KdCopyDataBlock (V) : 0xf800a81d0e00
Block encoded : Yes
Wait never : 0xd6dc0c37f24a0453
Wait always : 0x940ac90a25873204
KDBG owner tag check : True
Profile suggestion (KDBGHeader): Win10x64_14393
Service Pack (CmNtCSDVersion) : 0
Build string (NtBuildLab) : 14393.693.amd64fre.rs1_release.1
PsActiveProcessHead : 0xfffff800a82ff3d0 (39 processes)
PsLoadedModuleList : 0xfffff800a8305060 (189 modules)
KernelBase : 0xfffff800a8000000 (Matches MZ: True)
Major (OptionalHeader) : 10
Minor (OptionalHeader) : 0
KPCR : 0xfffff800a8342000 (CPU 0)
KPCR : 0xffffda019e020000 (CPU 1)
KPCR : 0xffffda019e09b000 (CPU 2)
KPCR : 0xffffda019e116000 (CPU 3)
KPCR : 0xffffda019e193000 (CPU 4)
KPCR : 0xffffda019e1d2000 (CPU 5)
KPCR : 0xffffda019e291000 (CPU 6)
KPCR : 0xffffda019e310000 (CPU 7)
**************************************************
...

Great. We’ve successfully retrieved the kdbg/dtb addresses along with the profile from the image. Now, let’s try to run some plugins against it to see what we’ve got…

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --dtb=0x1ab000 --profile=Win10x64_14393 pslist
Volatility Foundation Volatility Framework 2.6
Offset(V) Name PID PPID Thds Hnds Sess Wow64 Start Exit
------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------
0xffff9e04362eb6c0 System 4 0 209 0 ------ 0 2017-03-07 22:34:32 UTC+0000
0xffff9e043acbf800 smss.exe 372 4 4 0 ------ 0 2017-03-07 22:34:32 UTC+0000
0xffff9e043b4e9080 csrss.exe 540 528 13 -------- 0 0 2017-03-07 22:34:35 UTC+0000
0xffff9e043c439800 wininit.exe 628 528 4 0 0 0 2017-03-07 22:34:36 UTC+0000
0xffff9e043c4d8800 services.exe 708 628 33 -------- 0 0 2017-03-07 22:34:36 UTC+0000
0xffff9e043c540400 lsass.exe 752 628 11 -------- 0 0 2017-03-07 22:34:36 UTC+0000
0xffff9e043c4d4800 svchost.exe 872 708 54 0 0 0 2017-03-07 22:34:36 UTC+0000
0xffff9e043c4ce800 svchost.exe 936 708 16 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c472800 svchost.exe 348 708 104 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c626080 svchost.exe 388 708 54 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c683800 svchost.exe 1032 708 24 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c681800 svchost.exe 1096 708 32 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c67d800 svchost.exe 1212 708 38 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c67b800 svchost.exe 1236 708 34 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c66f800 nvvsvc.exe 1580 708 8 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c66b800 nvscpapisvr.ex 1588 708 7 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043637d080 svchost.exe 2000 708 8 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043ca1f800 svchost.exe 1292 708 12 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043ca8b800 spoolsv.exe 2056 708 32 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c042700 sched.exe 2168 708 17 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c036800 avguard.exe 2428 708 120 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c032800 armsvc.exe 2440 708 5 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c034800 Avira.ServiceH 2448 708 31 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c030800 OfficeClickToR 2476 708 29 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c02e800 IPROSetMonitor 2508 708 4 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c02c800 LogiRegistrySe 2516 708 6 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c02a800 svchost.exe 2524 708 16 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c028800 NvNetworkServi 2544 708 5 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c026800 NvStreamServic 2680 708 11 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c024800 dasHost.exe 2740 1096 26 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be64740 svchost.exe 2760 708 19 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be66800 svchost.exe 2768 708 19 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be75800 vmnetdhcp.exe 2776 708 3 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be70800 vmware-usbarbi 2792 708 5 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be72800 vmnat.exe 2808 708 6 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be74800 vmware-authd.e 2824 708 7 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be91800 MsMpEng.exe 2836 708 8 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be97800 ss_conn_servic 2844 708 6 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043beed040 MemCompression 2952 4 16 0 ------ 0 2017-03-07 22:34:38 UTC+0000

Excellent. Looks like many of the data structures are in tact to provide the types of information we’d expect from a full memory image!

So, let’s keep going…

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --dtb=0x1ab000 --profile=Win10x64_14393 hivelist
Volatility Foundation Volatility Framework 2.6
Virtual Physical Name ------------------ ------------------ ----
0xffff87063f057000 0x00000000059f3000 \REGISTRY\MACHINE\HARDWARE

Uh oh, that doesn’t look right. There should be more hives found than that.

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --dtb=0x1ab000 --profile=Win10x64_14393 userassist
Volatility Foundation Volatility Framework 2.6
The requested key could not be found in the hive(s) searched

No userassist (as it relies on the registry hives).

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --dtb=0x1ab000 --profile=Win10x64_14393 shellbags
Volatility Foundation Volatility Framework 2.6
Scanning for registries....
Gathering shellbag items and building path tree...

And, no shellbags (as it also relies on the registry hives). So, I guess the decompressed image doesn’t contain that.

Well, how about files?

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --dtb=0x1ab000 --profile=Win10x64_14393 filescan
Offset(P) #Ptr #Hnd Access Name ------------------ ------ ------ ------ ----
...
0x0000f000000f0820 3 0 RW-rwd \Device\HarddiskVolume2\$Extend\$RmMetadata\$Repair:$Corrupt:$DATA 0x0000f000000fe2d0 8 0 R--r-d \Device\HarddiskVolume6\Windows\System32\coreaudiopolicymanagerext.dll 0x0000f00000100420 12 0 R--r-d \Device\HarddiskVolume6\Windows\System32\Windows.UI.Xaml.Resources.dll 0x0000f00000100720 3 0 R--rwd \Device\HarddiskVolume6\Program Files\Microsoft Office\root\CLIPART\PUB60COR\NA02386_.WMF
0x0000f00000102ef0 15 0 R--r-d \Device\HarddiskVolume6\Windows\System32\dsreg.dll
0x0000f00000113080 32753 1 ------ \Device\DeviceApi\CMNotify
0x0000f000001132a0 15 0 R--rwd \Device\HarddiskVolume6\Windows\System32\vcruntime140.dll 0x0000f00000114cc0 15 0 R--r-d \Device\HarddiskVolume6\Windows\System32\microsoft-windows-kernel-power-events.dll
0x0000f0000011c370 32708 1 RW-r-- \Device\HarddiskVolume6\Windows\System32\winevt\Logs\Microsoft-Windows-AppXDeploymentServer%4Operational.evtx
0x0000f00000127ef0 16 0 R--r-d \Device\HarddiskVolume6\Windows\System32\DriverStore\FileRepository\iaahcic.inf_amd64_6c0fb3e072c6ec98\iaAHCIC.cat
0x0000f0000012e740 32768 1 ------ \Device\DeviceApi\CMNotify
0x0000f00000135a00 16 0 R--r-- \Device\HarddiskVolume6\Windows\INF\msgpiowin32.PNF
0x0000f00000140cd0 2 0 R--r-- \Device\HarddiskVolume6\Windows\WinSxS\Manifests\x86_microsoft.windows.i..utomation.proxystub_6595b64144ccf1df_1.0.14393.0_none_1e9c04c01886b354.manifest ...

Looks like that works, so mainly (in this short testing) we’re just missing registry hives.

While there are some anomalies here within strings (less identified ASCII and UNI strings in the decompressed hibernation file), we can see that not only does Hibernation Recon’s decompressed hibernation file yield substantially more artifacts across the board in both page_brute and Bulk_Extractor, but it also yields a memory image for use in/with Volatility. However, we can see that there are some pieces of missing information that would otherwise be resident in a memory image collected from a live system (namely registry hives as discovered in our testing, but there could be other missing items). Is Hibernation Recon missing resident information? Is Windows simply not storing that information in the hibernation file itself? I’m not certain, but would be very interested in finding out.

“Shutdown” System Results
See spreadsheet for Strings, Page_Brute, and Bulk_Extractor data.

Volatility
Attempt to convert/decompress native hibernation file to use with Volatility…
$ python ~/volatility/vol.py -f Shutdown/JPW10_hiberfil.sys --profile=Win10x64_14393 imagecopy -O Shutdown/Output/JPW10_hiberfil.sys_conv

Run pslist against the resulting file…
$ python ~/volatility/vol.py -f Shutdown/Output/JPW10_hiberfil.sys_conv --profile=Win10x64_14393 pslist

No results, showing the file wasn’t able to be successfully decompressed/parsed by Volatility.

So, let’s again move on to the HR decompressed file.

$ python ~/volatility/vol.py -f Shutdown/Output/ActiveMemory.bin imageinfo
Volatility Foundation Volatility Framework 2.6
INFO : volatility.debug : Determining profile based on KDBG search...
Suggested Profile(s) : Win10x64_14393, Win2016x64_14393
AS Layer1 : Win10AMD64PagedMemory (Kernel AS)
AS Layer2 : FileAddressSpace (/mnt/hgfs/G/Hibernation_Testing/Shutdown/Output/ActiveMemory.bin)
PAE type : No PAE
DTB : 0x1ab000L
KDBG : 0xf800a82f0500L
Number of Processors : 8
Image Type (Service Pack) : 0
KPCR for CPU 0 : 0xfffff800a8342000L
KPCR for CPU 1 : 0xffffda019e020000L
KPCR for CPU 2 : 0xffffda019e09b000L
KPCR for CPU 3 : 0xffffda019e116000L
KPCR for CPU 4 : 0xffffda019e193000L
KPCR for CPU 5 : 0xffffda019e1d2000L
KPCR for CPU 6 : 0xffffda019e291000L
KPCR for CPU 7 : 0xffffda019e310000L
KUSER_SHARED_DATA : 0xfffff78000000000L
Image date and time : 2017-03-07 22:39:53 UTC+0000
Image local date and time : 2017-03-07 14:39:53 -0800

$ python ~/volatility/vol.py -f Shutdown/Output/ActiveMemory.bin kdbgscan
Volatility Foundation Volatility Framework 2.6
**************************************************
Instantiating KDBG using: /mnt/hgfs/G/Hibernation_Testing/Shutdown/Output/ActiveMemory.bin WinXPSP2x86 (5.1.0 32bit)
Offset (P) : 0x3e0a9730
KDBG owner tag check : True
Profile suggestion (KDBGHeader): Win10x64_14393
PsActiveProcessHead : 0xa82ff3d0
PsLoadedModuleList : 0xa8305060
KernelBase : 0xfffff800a8000000
**************************************************
Instantiating KDBG using: /mnt/hgfs/G/Hibernation_Testing/Shutdown/Output/ActiveMemory.bin WinXPSP2x86 (5.1.0 32bit)
Offset (P) : 0x3e0a9730
KDBG owner tag check : True
Profile suggestion (KDBGHeader): Win2016x64_14393
PsActiveProcessHead : 0xa82ff3d0
PsLoadedModuleList : 0xa8305060
KernelBase : 0xfffff800a8000000

Again, we are able to successfully parse the HR decompressed image to get the initial offsets and profile needed to use Volatility and its plugins for analysis. So, let’s get to them.

$ python ~/volatility/vol.py -f Shutdown/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --dtb=0x1ab000 --profile=Win10x64_14393 pslist
Volatility Foundation Volatility Framework 2.6
Offset(V) Name PID PPID Thds Hnds Sess Wow64 Start Exit
------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------
0xffff9e04362eb6c0 System 4 0 206 0 ------ 0 2017-03-07 22:34:32 UTC+0000
0xffff9e043acbf800 smss.exe 372 4 4 0 ------ 0 2017-03-07 22:34:32 UTC+0000
0xffff9e043b4e9080 csrss.exe 540 528 14 -------- 0 0 2017-03-07 22:34:35 UTC+0000
0xffff9e043c439800 wininit.exe 628 528 7 0 0 0 2017-03-07 22:34:36 UTC+0000
0xffff9e043c4d8800 services.exe 708 628 33 -------- 0 0 2017-03-07 22:34:36 UTC+0000
0xffff9e043c540400 lsass.exe 752 628 9 -------- 0 0 2017-03-07 22:34:36 UTC+0000
0xffff9e043c4d4800 svchost.exe 872 708 46 0 0 0 2017-03-07 22:34:36 UTC+0000
0xffff9e043c4ce800 svchost.exe 936 708 14 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c472800 svchost.exe 348 708 96 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c626080 svchost.exe 388 708 53 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c683800 svchost.exe 1032 708 23 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c681800 svchost.exe 1096 708 24 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c67d800 svchost.exe 1212 708 31 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c67b800 svchost.exe 1236 708 30 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c66f800 nvvsvc.exe 1580 708 8 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c66b800 nvscpapisvr.ex 1588 708 7 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043637d080 svchost.exe 2000 708 8 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043ca1f800 svchost.exe 1292 708 12 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043ca8b800 spoolsv.exe 2056 708 30 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c042700 sched.exe 2168 708 17 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c036800 avguard.exe 2428 708 120 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c032800 armsvc.exe 2440 708 5 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c034800 Avira.ServiceH 2448 708 28 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c030800 OfficeClickToR 2476 708 23 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c02e800 IPROSetMonitor 2508 708 4 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c02c800 LogiRegistrySe 2516 708 6 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c02a800 svchost.exe 2524 708 12 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c028800 NvNetworkServi 2544 708 5 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c026800 NvStreamServic 2680 708 10 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043c024800 dasHost.exe 2740 1096 26 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be64740 svchost.exe 2760 708 17 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be66800 svchost.exe 2768 708 14 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be75800 vmnetdhcp.exe 2776 708 3 -------- 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be70800 vmware-usbarbi 2792 708 5 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be72800 vmnat.exe 2808 708 6 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be74800 vmware-authd.e 2824 708 7 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be91800 MsMpEng.exe 2836 708 8 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043be97800 ss_conn_servic 2844 708 6 0 0 0 2017-03-07 22:34:38 UTC+0000
0xffff9e043beed040 MemCompression 2952 4 4 0 ------ 0 2017-03-07 22:34:38 UTC+0000

Great. Again, looks like we have a memory image here that we can successfully use with Volatility.

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --profile=Win10x64_14393 hivelist
Volatility Foundation Volatility Framework 2.6
Virtual Physical Name ------------------ ------------------ ----
0xffff87063f057000 0x00000000059f3000 \REGISTRY\MACHINE\HARDWARE

Uh oh (again). It can’t seem to locate many of the registry hives in memory.

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --profile=Win10x64_14393 userassist
Volatility Foundation Volatility Framework 2.6
The requested key could not be found in the hive(s) searched

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --profile=Win10x64_14393 shellbags
Volatility Foundation Volatility Framework 2.6
Scanning for registries....
Gathering shellbag items and building path tree...

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --profile=Win10x64_14393 shimcache
Volatility Foundation Volatility Framework 2.6
WARNING : volatility.debug : No ShimCache data found

Again, can’t extract the info from these plugins because of the lack of registry hives found.

However, it seems that many other plugins complete successfully.

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --profile=Win10x64_14393 filescan
Volatility Foundation Volatility Framework 2.6
Offset(P) #Ptr #Hnd Access Name ------------------ ------ ------ ------ ----
...
0x0000f000000f0820 3 0 RW-rwd \Device\HarddiskVolume2\$Extend\$RmMetadata\$Repair:$Corrupt:$DATA 0x0000f000000fe2d0 8 0 R--r-d \Device\HarddiskVolume6\Windows\System32\coreaudiopolicymanagerext.dll 0x0000f00000100420 12 0 R--r-d \Device\HarddiskVolume6\Windows\System32\Windows.UI.Xaml.Resources.dll 0x0000f00000100720 3 0 R--rwd \Device\HarddiskVolume6\Program Files\Microsoft Office\root\CLIPART\PUB60COR\NA02386_.WMF
0x0000f00000102ef0 15 0 R--r-d \Device\HarddiskVolume6\Windows\System32\dsreg.dll
0x0000f00000113080 32753 1 ------ \Device\DeviceApi\CMNotify
0x0000f000001132a0 15 0 R--rwd \Device\HarddiskVolume6\Windows\System32\vcruntime140.dll 0x0000f00000114cc0 15 0 R--r-d \Device\HarddiskVolume6\Windows\System32\microsoft-windows-kernel-power-events.dll
0x0000f0000011c370 32708 1 RW-r-- \Device\HarddiskVolume6\Windows\System32\winevt\Logs\Microsoft-Windows-AppXDeploymentServer%4Operational.evtx
0x0000f00000127ef0 16 0 R--r-d \Device\HarddiskVolume6\Windows\System32\DriverStore\FileRepository\iaahcic.inf_amd64_6c0fb3e072c6ec98\iaAHCIC.cat
0x0000f0000012e740 32768 1 ------ \Device\DeviceApi\CMNotify
0x0000f00000135a00 16 0 R--r-- \Device\HarddiskVolume6\Windows\INF\msgpiowin32.PNF
0x0000f00000140cd0 2 0 R--r-- \Device\HarddiskVolume6\Windows\WinSxS\Manifests\x86_microsoft.windows.i..utomation.proxystub_6595b64144ccf1df_1.0.14393.0_none_1e9c04c01886b354.manifest
...

I also ran the mbrparser and mftparser plugins against the image to see if that data was resident.

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --profile=Win10x64_14393 mbrparser

Verified works.

$ python ~/volatility/vol.py -f Hibernated/Output/ActiveMemory.bin --kdbg=0xf800a81d0e00 --profile=Win10x64_14393 mftparser

Verified works.

So, we seem to witness the same anomaly in strings, however it appears that the decompressed hibernation file contains more identified Unicode strings as compared to the native file. Given that strings performs a relatively arbitrary function (i.e. identify things that might be strings of an alphabet/namespace), I am simply providing it as a data point. And, again, we see that the decompressed file yields a substantial amount of additional information that was otherwise obfuscated/hidden from discovery in its native form.

Conclusion

As you can see, Arsenal’s tool was able to successfully decompress and reconstruct the provided Hibernation files (sans the zeroed file from the Booted system obviously), thus restoring a substantial amount of otherwise obfuscated/encoded data and ultimately our capability to extract useful artifacts in our investigations! Given how long it’s been since I’ve been able to easily and comprehensively decompress a Windows 8/Server 2012+ Hibernation file, I would have been satisfied with simple decompression of all strings or chunks of data. Not only do we get that, but also the restored ability to use Volatility for more comprehensive analysis of the extracted memory image (sans a few missing memory artifacts as previously noted*).

For me, it looks like I now have a new go-to tool for decompressing Hibernation files from Windows 8/Server 2012+ systems.

*If anyone has any insight, I would love to find out why we can’t seem to locate the registry hives in the reconstructed memory image, along with what else may be missing (as I didn’t test every single plugin) and why.

/JP

Quick(er) Mounting and Dismounting of LVM’s on Forensic Images

I recently came across Int’l Man of Leisure’s blog posts here and here on “Mounting and Imaging Logical Volume Manager (LVM2)”. He does a great job of defining the problem statement (dealing with LVM’s in their various image formats in a DFIR investigation) and how to work through getting a set of logical images back into their intended LVM layout for appropriate mounting and analysis.

IMOL begins by going through the background of LVM, what it is, and how to install it to prepare your system for dealing with LVM’s. Once prepared, IMOL begins by presenting a set of two VMDK images that must be merged or “stitched together” in order to be interpreted and parsed by the Linux LVM. However, VMDK files are not something natively readable/mountable by a linux system. So, before we can even begin stitching these back together, these VMDK files must be converted into something natively readable by the system, such as a raw image/block device. In IMOL’s testing, he found that FTK Imager was one of a few tools that was able to read the VMDK files in order to convert (image) them to raw files. He then used FTK Imager to image the VMDK files into respective Raw DD format files for continued use. However, here is where I would like to branch into my process for mounting LVM’s that completely eliminates the need for converting an image to raw using a tool called “QEMU”, and thus saving you (potentially) hours of time.

We all know when dealing with forensic imaging/conversion that even the slightest hiccup can render an entire image useless and long-spent time wasted. The less time we spend imaging/converting, the faster we can get to analysis and toward our goals for the investigation. Enter QEMU, specifically “qemu-nbd”. I could go into a lot of detail of all the types of images it can convert and how useful it can be in various capacities (in fact, I may do another blog post about it). However, for this post, I will just stick to specifically how you can use it to perform on-the-fly image format translation (in real-time) between various formats – no need to spend time converting to another image file.

QEMU has a utility called “qemu-nbd” (nbd stands for Network Block Device) that essentially performs real-time translation betwixt (I have waited so long to use that word in a serious tone) various image formats. It’s as easy as the following:

Ensure you have an available NBD
# ls -l /dev/nbd*

If no device files (or not enough) exist as /dev/nbd*, create as many as needed
# for i in {0..<z>}; do mknod /dev/nbd$i b 43 $i; done
*Where <z> is the number of devices you need, minus one

Mount the image file
# qemu-nbd -r -c /dev/nbd<x> image.<extension>
* -r: read-only
* -c: connect image file to NBD device
*Where <x> is the appropriate block device number (typically starting at 0) and <extension> is a supported QEMU Image Type (raw, cloop, cow, qcow, qcow2, vmdk, vdi, vhdx, vpc)

Note that this will need to be done for each image that is a part of the LV group. For example, if there are 3 different VMDK files that together comprise one or more LV groups, you would do the following (ensuring the associated /dev/nbd devices have already been created before issuing the below commands):

# qemu-nbd -r -c /dev/nbd0 image_0.vmdk
# qemu-nbd -r -c /dev/nbd1 image_1.vmdk
# qemu-nbd -r -c /dev/nbd2 image_2.vmdk

That’s it. Each /dev/nbd<x> is immediately translated and available as a raw block device to be queried/mounted just as if it were a raw image to begin with. Pretty awesome, huh?

Now, if you were lucky enough to start with raw/DD images, you don’t need to perform any of the above. Instead, you can just skip to the below instructions for mounting and mapping the LVM’s.

At this point, my process to identify and load the LVM(s) mostly mirrors that as described by IMOL, with a few subtle differences. I won’t go into great detail of it all as IMOL gives great descriptions of each step in his walk-through. However, I will lay out my commands below for those who are looking for an easy copy/paste method to stick into their cheat sheets.

Keep in mind that the order of the below commands is critical to successful mounting of LVM’s.

Load the LVM module if not already loaded
# modprobe dm_mod

Ensure you have enough available loopback devices (one for each nbd device)
# ls -l /dev/loop*

If not enough loopback devices exist, create as many as needed
# for i in {0..<z>}; do mknod /dev/loop$i b 7 $i; done
*Where <z> is the number of devices you need, minus one

Set up a loopback device for each image that is part of the LV group
# losetup -r [-o <byte_offset>] -f [/dev/loop<x>] /dev/nbd<x>

Read partition tables from each loopback device to create mappings
# kpartx -a -v /dev/loop<x>

List the available Physical Volume Groups (VG’s)
# pvdisplay

List the available Logical Volume Groups (VG’s)
# lvdisplay

(Optional) If not listed, re-scan the mounted volumes to identify the associated VG’s
# pvscan
# lvscan
# vgscan

Activate the appropriate VG’s
# vgchange -a y <VG>
** The recombined LVM volume(s) will now be available at /dev/mapper/<VolumeGroup>-<VolumeName>

Mount the LVM Volume(s)
# mount [options] /dev/mapper/<VolumeGroup>-<VolumeName> /mnt/point

Congratulations. You (should) now have filesystem access to the given LVM(s)!

== Sidebar ==

Interested in why we use the given numbers of “43” and “7” for the mknod command?

The mknod command is structured like the following: mknod <device> <type> <major_#> <minor_#>

For our uses, we are creating device files of type “b” (block device), with major #’s of “43” (nbd) and “7” (loopback). The major number tells the system what type of device to expect and operate as. For a list of devices that your system is aware of and can dynamically assign when a major number is not specified, check out your /proc/devices file. IBM does a rather good job of explaining it all here. The minor number is more for reference and thus should probably, as best practice, reference/relate to the device number as well. Though, there is nothing requiring it to be that way.

For further information about the mknod command structure, just check out the man page.

== /Sidebar ==

Once you’re done with the images, the next logical step is to dismount the images which can at times become unnecessarily and illogically troublesome. To properly dismount LVM’s, perform the following steps (again, order is critical here!):

Dismount each mounted filesystem
# umount /mnt/point

De-activate each activated Volume Group
# vgchange -a n <VG>

Remove partition mappings for each loop device
# kpartx -d -v /dev/loop<x>

Remove each loopback device
# losetup -d /dev/loop<x>

(Optional) Force remove an LVM
# dmsetup remove -f <VG>

Keep in mind the dmsetup command above is considered deprecated and not suggested for use. However, I provide it as I have had to use it at times in the past when a VG simply would not detach using the appropriate commands. That said, if all else fails, reboot 🙂

Hopefully, this post will help with the often convoluted process of mounting LVM’s, especially when split across multiple images/devices.

/JP

The Importance of Incident Scoping/Assessment

In consulting, all engagements begin with what we refer to as “scoping” in order to, at a very high level, determine if/how we may be able to help a client. Sure, they can sometimes be arduous or monotonous and often involve a comedy of conference call errors, but they are absolutely CRITICAL to the success of the engagement. So critical, in fact, that I wanted to take some purposeful time to address this often-overlooked, admittedly very unsexy, but nonetheless integral piece of performing effective DFIR.

Though I’m approaching this from a consulting point of view, this is in no way unique nor applicable only to consulting. So, don’t tune out now just because, “I don’t have clients.” You do, actually, whether they are external entities or people within your own organization. Each of us has “clients” in various forms that rely (often heavily) on us as DFIR professionals to ask the right questions and respond with the appropriate guidance and/or actions to mitigate threats and protect them from harm. As such, every DFIR professional will find themselves in situations where they must first comprehensively understand the problem(s) and issue(s) at hand before it can be effectively addressed in response and analysis. In fact, this is often where many of us find ourselves at the onset of an alert or suspected compromise that requires a well-formulated and concerted DFIR response. Though it can be very difficult to take step back and perform a high level assessment instead of diving right into action (especially when you may have external entities and/or higher level management wanting answers LIKE YESTERDAY), doing so will pay you back ten-fold.

At a lower level, scoping (or an initial assessment) must be performed as a due diligence effort to acquire all appropriate/pertinent information, addressing a variety of aspects that may affect the success, efficacy, or efficiency of the investigation. From my private sector experience dealing with a wide array of clients from all types of verticals, industries, and organizational sizes, I’ve attempted to unjustly distill the scoping topic and question set down a few (ok, a few more than a few) important bullets provided below.

However, do keep in mind that this is not intended to be all-inclusive set of questions to ask by any means. Rather, the below set of topics and specific questions are intended to serve as a solid baseline in facilitating comprehensive response and should be augmented or modified as needed. In addition, the below questions are from the perspective of me/us asking an external entity (i.e. client). So, feel free to change/replace the pronouns as well as instances of “the client” as needed for your use and application.

  • Purpose: Understand the background of the situation
    • How did we (the client/organization) get here?
    • When and how did they first notice any sort of issue?
    • Can they think of any legitimate (non-malicious) causes for the current issue?
    • Have there been any other anomalies either previous to or during the timeframe of concern that may be related to the current issue?
  • Purpose: Understand what responsive actions have been performed to date
    • Are the systems still running or have they been powered down?
    • Have firewall blocks been put in place?
    • (Tons more questions I ask…)
    • Have any external entities been notified (particularly if under certain regulations)?
  • Purpose: Understand the set of artifacts available to assist us in our response
    • What type(s) of machines are involved (workstations/servers, OS)?
    • Are they virtual or physical?
    • Where are they hosted, and do they span disparate physical locations?
    • What specific logging is enabled (host, network, and appliance)?
    • …and which of the above are actually collected and available?
    • …and do the available logs contain the right (useful) content?
      • Let’s just say it is not uncommon to get “VPN logs” that simply show syslog interface up/down, “Web Logs” that contain only the load balancer as the “client-ip”, or “DNS logs” that aren’t logging query/response and logging only the IP of the DNS server as the “client”.
    • …and do the available logs span the time frame(s) of concern/interest?
  • Purpose: Identify the goals (in priority order) for the engagement
    • Ask the question, “What would a successful engagement look like to you?”
    • Is the client interested in simply getting back to business?
    • Would they like a root cause analysis (how exactly did this get in/past their defenses)?
      • If so, are they looking to specifically prove/disprove something?
    • Are they simply looking to check a box (fulfilling some sort of requirement)?
      • Often this will not be stated, but you can determine it by asking certain questions and gauging certain entities’ investment in the response.
    • What would they like us to do, specifically, in priority order?
      • Now, we don’t take this verbatim and slap it into a SoW, but we do use it to guide our response as best as possible to achieve their priority goals in tandem with an appropriate, comprehensive, and best practice response.

Though it is not uncommon for a situation to merit even further questioning and level of excruciating detail, depending on complexity, the above set of topics are what I would consider the minimum requirements for a comprehensive initial assessment. Suffice to say that misunderstandings or simple lack of required information in any one of these areas can lead to a variety of undesirable consequences, ranging from that of a simply sub-optimal outcome to that of a completely disastrous one for both the company and the client.

The DFIR industry is a “pay now or pay later” industry, and this is no exception. As such, I highly encourage all of us to be purposeful in spending our time performing due diligence on the front end instead of paying the consequences of not doing so throughout our investigations.

/JP

Page 1 of 3

Powered by WordPress & Theme by Anders Norén