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
- https://www.ndchost.com/wiki/server-administration/netcat-over-ssh
- https://tunnelix.com/linux-memory-analysis-with-lime-and-volatility/
- https://www.andreafortuna.org/2019/08/22/how-to-generate-a-volatility-profile-for-a-linux-system/
- https://www.aldeid.com/wiki/Volatility/custom-profiles
- https://sansorg.egnyte.com/dl/rcC7AFiUr0/?
- https://www.agix.com.au/use-a-dd-image-as-a-disk-for-a-virtualbox-guest/?utm_source=pocket_mylist
Share this post
Twitter
Facebook
Reddit
LinkedIn