Chris Long

6 minute read

Over the past few years, I’ve occasionally needed to do some quick forensics on Linux hosts. Each time I do, I find myself stitching together 5-10 different pages of content to pull together the information I need to grab the disk and memory collections.

This is a guide that attempts to pull all of that into one place and will likely serve as a future reference for me, but I hope others can derive some value from it as well.

NOTE: This guide should NOT be used for important cases! This is called “quick and dirty” for a reason. There’s always tradeoffs to be made between time savings and evidence preservation and some of the techniques in here optimize for time savings at the expense of potential data modification or loss.


Obtaining a disk image using DD over netcat and writing the result to a file

Receiving host:

nc -l 19000 | bzip2 -d | dd bs=16M of=/path/to/disk_image.dd

Sending host (host being imaged):

dd bs=16M if=/dev/sda[n] | bzip2 -c | nc receiving.host.com 19000

At this point, it’s worth noting you can image the entire disk (e.g. /dev/sda) or just a specific partition (/dev/sda1). Doing the latter will save you some steps down the road. However, if there are multiple partitions you’ll need to analyze, you might as well grab the whole disk.


Memory forensics

This is one of those tradeoff situations. If you have time, do all of this on a VM using the same OS and kernel version as your target system and just transfer over the compiled kernel module. If not, you’ll certainly be stomping all over evidence by installing packages and compiling files, but sometimes that can be a tradeoff worth making depending on the situation.

Install LiME:

sudo apt-get install lime-forensics-dkms
git clone https://github.com/504ensicsLabs/LiME
make ## Take note of where the resulting kernel module ends up
# Install the kernel module and write out the memory dump to a file
sudo insmod /path/to/lime.ko "path=/output_mem.mem format=lime"

Once I’ve transferred over the memory dump, I also like to dump the strings to a file for quick grepping:

strings output_mem.mem > strings.mem

Create a Custom Profile for Volatility

sudo apt install dwarfdump
git clone https://github.com/volatilityfoundation/volatility.git
cd ./volatility/tools/linux/
make
# Zip up the files that comprise of the profile
zip $(lsb_release -i -s)_$(uname -r)_profile.zip ./volatility/tools/linux/module.dwarf /boot/System.map-$(uname -r)

Add the custom profile to Volatility on the analysis system

# Copy the zip file created above to volatility/volatility/plugins/overlays/linux
cp your_profile_name.zip  volatility/volatility/plugins/overlays/linux
# Ensure it worked
volatility/vol.py --info | grep 'your_profile_name'

At this point, we have a memory dump and a DD image of either the entire disk or a specific parition. If you DD’ed the entire disk, follow this step. If not, you can ignore it.

Determining the offset to specify to forensics tools

Many forensic tools expect the start of a data partition as input and will throw “filesystem not recognized” errors and such if you try to pass an entire disk image to them. To workaround this, you can almost always pass an offset to the tool to specify where in the disk image data should start being read.

For example:

# Display details about the filesystem
fsstat disk_img.dd
Cannot determine file system type

In this case, we can use mmls to display the partition layout determine the offset of the data partition in our image:

$ mmls disk_img.dd

GUID Partition Table (EFI)
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Safety Table
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  Meta      0000000001   0000000001   0000000001   GPT Header
003:  Meta      0000000002   0000000033   0000000032   Partition Table
004:  013       0000002048   0000010239   0000008192
005:  014       0000010240   0000227327   0000217088
006:  000       0000227328   0052428766   0052201439
007:  -------   0052428767   0052428799   0000000033   Unallocated

In this case, the longest section with length 0052201439 is the data partition, so the starting offset will be 0000227328 or just 227328

Now when we pass this offset to fsstat using the -o parameter, it’s happy:

$ fsstat -o 227328 disk_image.dd  | head
FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: Ext4
Volume Name: sample-rootfs
Volume ID: 73d0d0d862d4c4a5774020dd74020dd32

Dump the filesystem and create a timeline from it

Next, I like to use fls to dump the file and directory listings of the filesystem (with associated timestamps and metadata) using fls. Remember to pass the offset if you’re using a full disk image.

fls -o 227328 disk_image.dd -r -p -m / > bodyfile.txt

After that, we can use mactime to narrow down the timerange (optional) and create a filesystem timeline:

mactime -d -z UTC -b bodyfile.txt 2021-04-01..2021-06-24  > filesystem_timeline.csv

log2timeline (Plaso)

log2timeline will give us a more detailed timeline, including things like relevant events from system logs and much more: https://plaso.readthedocs.io/en/latest/sources/user/Parsers-and-plugins.html

# Create an output file called disk_image.plaso from our disk image
# log2timeline is smart enough to ask you which partition(s) you want to analyze. 
# No need to pass an offset here!
log2timeline.py disk_image.plaso disk_image.dd

Once again, we may want to narrow down this timeline a bit, so we can use psort for that

psort.py -z "UTC" -o L2tcsv disk_image.plaso "date > '2021-04-01 00:00:00' AND date < '2021-06-24 00:00:00'" -w sorted_supertimeline.csv

Mounting the disk read-only

I like exploring the filesystem while I review the timeline, and the best way to do that is to mount it. We have to pass some special options though:

# Create a mountpoint
mkdir /mnt/forensics

# We actually have to calculate the offset here:
# Instead of 227328, it's going to be (227328 * sector_size). In many (but not all!) cases, this is 512
# Therefore, our offset will be 116391936

# Must be root to mount
sudo mount -o ro,norecovery,noexec,loop,offset=116391936 -t ext4 disk_image.dd /mnt/forensics

You can now explore the filesystem of the host in a read-only capacity at /mnt/forensics.

Extracting unallocated space

Unallocated space can be useful for digging for deleted file data. Generate it using blkls

blkls disk_image.dd > unallocated_space.blkls

Summary

At this point, we now have the following data sources to analyze:

  • A full disk image
  • A mounted disk image
  • A memory dump
  • Strings from a memory dump
  • A filesystem timeline
  • A sorted super timeline
  • Unallocated space dump

This is generally enough data for most investigations, or at least serves as a solid starting point for more intensive searches. Hopefully this post comes in handy for anyone looking for a quick forensics workflow.

Additional Resources

References

comments powered by Disqus