Recovering deleted files with SleuthKit

      No Comments on Recovering deleted files with SleuthKit

SleuthKit is probably one of the most comprehensive collections of tools for forensic filesystem analysis. One of the most basic use-cases is the recovery of files that have been deleted. However, SleuthKit can do much, much more. Have a look at the case studies wiki page for an impression.

Let’s assume, there is a FAT volume on our disk (maybe a USB stick or a memory card) and we want to recover all deleted file. The safest way is probably to duplicate the entire volume first and perform an offline analysis. Again, there are quite a few tools for creating (forensic) images, the simpliest probably being dd.

To dump all partitions of your disk use

$ dd if=/dev/sdg of=/tmp/disk.img bs=512

Of course, you could also dump just one partition (e.g. /dev/sdg2).

To get the partition table layout, you can the use mmls on the image file

$ mmls /tmp/disk.img
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

     Slot    Start        End          Length       Description
00:  Meta    0000000000   0000000000   0000000001   Primary Table (#0)
01:  -----   0000000000   0000000134   0000000135   Unallocated
02:  00:00   0000000135   0003858623   0003858489   DOS FAT16 (0x06)
03:  -----   0003858624   0003862527   0000003904   Unallocated

fsstat is used to get information about the filesystem itself. In this case, the target partition starts with an offset of 135, so the imgoffset flag -o is mandatory (if you just dump a single partition, there is of course no offset and -o is not needed).

$ fsstat -o 135 /tmp/disk.img
FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: FAT16

OEM Name: MSDOS5.0
Volume ID: 0x34333064
Volume Label (Boot Sector): NO NAME    
Volume Label (Root Directory):
File System Type Label: FAT16   

Sectors before file system: 135
[...]

You can now do all sorts of things with your image file, e.g. recursively list all file and directories (including deleted ones);

$ fls -o 135 -r /tmp/disk.img

To recover a deleted file by inode number, you can use the command line tool icat

icat -o 135 -r /tmp/disk.img 54 > /tmp/DeletedPicture.jpg

For a quick overview and simple examples on other commands have a look at http://rationallyparanoid.com/articles/sleuth-kit.html.

The SleuthKit wiki points to a great script by Dave Henkewick that runs recursively through the image file and uses fls and icat to retrive the inode numbers and restore the files:

#!/usr/bin/perl -w
use strict;

# (C) 2004 dave (at) hoax (dot) ca
# ************* THIS SCRIPT HAS NO WARRANTY! **************
#
# this script works with the output from SleuthKit's fls and icat version: 3.00
# using afflib-3.3.4
#
# dont worry if you do not have the same versions because it should work unless
# the output from the commands have changed
#
# if the script does not work, please email me the debug output and
# the output from manually running fls and icat, thanks!
#
# set the recovery directory 
my $fullpath="/tmp/recover/";

# set the absolute path of fls binary
my $FLS="/usr/bin/fls";

# set the fls options
my @FLS_OPT=("-o","135","-f","fat","-pr","-m $fullpath","-s 0");

# set the path of the device to be recovered
my $FLS_IMG="/tmp/disk.img";

# set the inode of the directory to be recovered
my $FLS_inode="2";

# set the path of the icat STDERR log
my $ICAT_LOG="/tmp/icat.log";

# set the absolute path of the icat binary
my $ICAT="/usr/bin/icat";

# set the icat options
my @ICAT_OPT=("-o","135","-f","fat");

my $ICAT_IMG="$FLS_IMG";

# here we go. hold on tight!
list($FLS_inode);


sub list($) {
	#make the recovery dir
	system("mkdir","-p","$fullpath") && die "Cannot mkdir $fullpath while processing: $_";
	#run a recursive FLS on our chosen inode and regex each line
	foreach $_ (`$FLS @FLS_OPT $FLS_IMG $_[0] 2>&1`) {
		regex($_);
		#print $_;
	}
}

sub regex($) {
	#first, regex for dirs, clean 'em up, and create 'em in recovery dir
	#
	# the following regex will work on output of the format:
	# 0|/directory/file.foo.bar (deleted)|0|r/----------|0|0|0|0|0|0
	# 0|/directory/file.foo.bar|1384462|r/rrw-r--r--|1000|1000|971556|1218136846|1218136846|1225037181|0
	# 0|/directory|1392712|d/drwxr-xr-x|1000|1000|4096|1225309096|1225309096|1226059913|0
	# 0|/directory/file.foo.bar -> /directory2/file2.foo.bar|1384462|l/lrw-r--r--|1000|1000|971556|1218136846|1218136846|1225037181|0
	#
	#
	if (/(\d\|([\S\s]+)\|(\d+)\|\S\/d([\w-]{3})([\w-]{3})([\w-]{3})(\|\d+\|\d+\|\d+\|\d+\|\d+\|\d+\|\d+))/) {
		my $fulldir = $2;
		my $uid = $4; my $gid = $5; my $oid = $6;
		$fulldir =~ s/ (\(deleted(\)|\-realloc\)))$//g;
		$fulldir =~ s/ /_/g;
		$uid =~ s/-//g; $gid =~ s/-//g; $oid =~ s/-//g;
		$uid = lc($uid); $gid = lc($gid); $oid = lc($oid);
		#print "mkdir -p $fulldir\n";
		system("mkdir","-p","$fulldir") && die "Cannot mkdir $fulldir while processing: $_";
		#print "chmod u=$uid,g=$gid,o=$oid $fulldir\n";
		system("chmod","u=$uid,g=$gid,o=$oid","$fulldir") && die "Cannot chmod u=$uid,g=$gid,o=$oid $fulldir while processing: $_";
	#second, regex for files, sockets, fifos then
	#clean and dump them in recovery dir	
	} elsif (/(\d\|([\S\s]+)\|(\d+)\|\S\/(-|s|f|r)([\w-]{3})([\w-]{3})([\w-]{3})((\|\d+\|\d+\|\d+\|\d+\|\d+\|\d+\|\d+)|(\|\d+\|\d+\|\d+\|\d+\|\d+\|\d+)))/) {
		my $inode = $3;
		my $fullfile = $2;
		$fullfile =~ s/ (\(deleted(\)|\-realloc\)))$//g;
		$fullfile =~ s/ /_/g;
		#print "$ICAT @ICAT_OPT $ICAT_IMG $inode > $fullfile\n" if ($inode != 0);
		system("$ICAT @ICAT_OPT $ICAT_IMG $inode > \"$fullfile\" 2>> $ICAT_LOG") if ($inode != 0);
		#cannot use die cuz an invalid inode will kill the script
		#&& die "Cannot icat $inode into \"$fullfile\" while processing: $_"
		
	# thrid, regex for symlink, clean, and create in recovery dir
	} elsif (/(\d\|([\S\s]+)\s\-\>\s([\S\s]+)\|(\d+)\|\S\/(l)([\w-]{3})([\w-]{3})([\w-]{3})(\|\d+\|\d+\|\d+\|\d+\|\d+\|\d+\|\d+))/) {
		#print "$1\n";
		my $fullsym_dst = $2; my $fullsym_src = $3;
		$fullsym_dst =~ s/ /_/g; $fullsym_src =~ s/ /_/g;
		#print "ln -s $fullsym_src $fullsym_dst\n";
		system("ln","-s","$fullsym_src","$fullsym_dst") && die "Cannot ln $fullsym_src $fullsym_dst while processing: $_";
	} else {
		print "Unknown directory listing. File or directory NOT recovered\nDebug:\n$_[0]\n";
	}
} #that's all folks. hope y'all had fun!

Another great tool is the Autopsy Forensic Browser, a graphical frontend to the SleuthKit commands that runs in your browser.

Leave a Reply

Your email address will not be published.