Phrack 59 06
Phrack 59 06
txt
==Phrack Inc.==
--[ Contents
1 - Introduction
1.1 - Generic Unix File Systems
1.2 - Forensics
2 - Anti-Forensics
3 - Runefs
3.1 - Creating hidden space
3.2 - Using hidden space
3.3 - TCT unclear on ext2fs specifications
5 - Conclusion
6 - Greets
7 - References
8 - Appendix
8.1 - The Ext2fs
8.2 - runefs.tar.gz (uuencoded)
8.3 - tdt.tar.gz (uuencoded)
--[ 1 - Introduction
This section will describe basic Unix file system theory (not focussing
on any specific implementation), discussing the meta-data structures used
to organise the file system internally. Files within the Unix OS are
continuous streams of bytes of arbitrary length and are the main
abstraction used for I/O. This article will focus on files in the more
general sense of data stored on disk and organised by a file system.
Data blocks are clustered and organised into files by inodes. Inodes
are the meta-data structure which represent the user visible files; one for
each unique file. Each inode contains an array of block pointers (that is,
indexes into the data block array) and various other information about the
file. This additional information about the file includes: the UID; GID;
size; permissions; modification/access/creation (MAC) times, and some other
data. The limited amount of space available to inodes means the the block
pointer array can only contain a small number of pointers. To allow file
sizes to be of substantial length, inodes employ "indirect blocks". An
indirect block acts as an extension to the block array, storing additional
pointers. Doubly and trebly indirect blocks contain block pointers to
further indirect blocks, and doubly indirect blocks respectively. Inodes
are stored in an array called the inode table, and are referred to by their
0-based indexes within this table. The state of an inode, i.e. free vs.
allocated, is stored in a bitmap called, imaginitively, the "inode bitmap".
Files, that is, inodes, are associated with file names by special
structures called directory entries stored within directory files. These
structures are stored contigously inside the directory file. Directory
entries have a basic structure of:
struct dirent {
int inode;
short rec_size;
short name_len;
char file_name[NAME_LEN];
};
The 'inode' element of the dirent contains the inode number which is
linked with the file name, stored in 'file_name'. To save space, the actual
length of the file name is recorded in 'name_len' and the remaining space
in the file_name array is used by the next directory entry structure. The
size of a dirent is usually rounded up to the closest power of two, and
this size is stored in 'rec_size'. When a file name/inode link is removed,
the inode value is set to 0 and the rec_size of the preceding dirent is
extended to encompass the deleted dirent. This has the effect of storing
the names of deleted files inside directory files.
Everytime an file name is linked with a file name, and internal counter
within the inode is incremented. Likewise, everytime a link is removed,
this counter is decremented. When this counter reaches 0, there are no
references to the inode from within the directory structure; the file is
deleted. Files which have been deleted can safely have their resources, the
data blocks and the inode itself, freed. This is accomplished by marking
the appropriate bitmaps.
To mount a file system the kernel needs to know the size and locations
of the meta-data. The first piece of meta-data, the super block, is stored
at a known location. The super-block contains information such as the
number of inodes and blocks, the size of a block, and a great deal of
additional information. Based on the data within the super block, the
kernel is able to calculate the locations and sizes of the inode table and
the data portion of the disk.
For performance reasons, no modern file system actually has just one
inode table and one block array. Rather inodes and blocks are clustered
together in groups spread out across the disk. These groups usually contain
private bitmaps for their inodes and blocks, as well as copies of the
superblock to aid recovery in case of catastrophic data loss.
This evidence is both file content (data), and information about the
file(s) (meta-data). Based on the evidence retrieved from the file system
the investigator will attempt to:
--[ 2 - Anti-Forensics
Data hiding, on the other hand, is useful only so long as the analyst
doesn't know where to look. Long term integrity of the data storage area
cannot be garaunteed. For this reason, data hiding should be used in
combination with attacks against the parsing phase (e.g. proprietary file
formats), and against the examination phase (e.g. encryption). Data hiding
is most useful in the case of essential data which must be stored for some
length of time (e.g. photographs of young women in artistic poses).
--[ 3 - Runefs
The most common toolkit for Unix forensic file system analysis is "The
Coronor's Toolkit"[1] (TCT) developed by Dan Farmer and Wietse Venema.
Despite being relied on for years as the mainstay of the Unix digital
forensic analyst, and providing the basis for several enhancements [2][3],
it remains as flawed today as when it was first released. A major file
system implementation bug allows an attacker to store arbitrary amounts of
data in a location which the TCT tools cannot examine.
Historically, the bad blocks inode was used to reference data blocks
occupying bad sectors of the hard disk, preventing these blocks from being
used by live files. The FFS has deprecated the bad blocks inode, preventing
the successful exploitation of this bug, but it is still in use on ext2fs.
Successfully exploiting a file system data hiding attack means, for an
anti-forensics agent, manipulating the file system without altering it
outside of the specifications implemented in the file system checker: fsck.
Although, it is interesting to note that no forensic analysis methodology
uses fsck to ensure that the file system has not been radically altered.
The ext2fs fsck still uses the bad blocks inode for bad block
referencing, and so it allows any number of blocks to be allocated to the
inode. Unfortunately, the TCT file system code does not recognise the bad
blocks inode as within the scope of an investigation. The bad blocks inode
bug is easy to spot, and should be trivial to correct. Scattered throughout
the file system code of the TCT package (and the related toolkit TASK) is
the following errorneous check:
/*
* Sanity check.
*/
if (inum < EXT2_ROOT_INO || inum > ext2fs->fs.s_inodes_count)
error("invalid inode number: %lu", (ULONG) inum);
The first inode that can allocate block resources on a ext2 file system
is in fact the bad blocks inode (inode 1) -- *not* the root inode (inode
2). Because of this mis-implementation of the ext2fs it is possible to
store data on blocks allocated to the bad blocks inode and have it hidden
from an analyst using TCT or TASK. To illustrate the severity of this
attack the following examples demonstrate using the accompanying runefs
toolkit to: create hidden storage space; copy data to and from this area,
and show how this area remains secure from a forensic analyst.
----[ 3.1 - Example: Creating hidden space
# df -k /dev/hda6
Filesystem 1k-blocks Used Available Use% Mounted on
/dev/hda6 1011928 20 960504 1% /mnt
# ./bin/mkrune -v /dev/hda6
+++ bb_blk +++
bb_blk->start = 33275
bb_blk->end = 65535
bb_blk->group = 1
bb_blk->size = 32261
+++
rune size: 126M
# df -k /dev/hda6
Filesystem 1k-blocks Used Available Use% Mounted on
/dev/hda6 1011928 129196 831328 14% /mnt
# e2fsck -f /dev/hda6
e2fsck 1.26 (3-Feb-2002)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/hda6: 11/128768 files (0.0% non-contiguous), 36349/257032 blocks
#
This second example shows how data can be inserted and extracted from
the hidden storage space without any data loss. While this example does not
comprehensively explore the uses of a hidden data storage area, it is
sufficient to demonstrate how data can be introduced to and extracted from
the runefs.
# ./icat /dev/hda6 1
/icat: invalid inode number: 1
#
Interesting as these examples are, there are problems with this runefs.
This implementation of runefs is crude and old (it was written in November
2000), and it does not natively support encryption. The current version of
runefs is a dynamicly resizeable file system which supports a full
directory structure, is fully encrypted, and can grow up to four gigabytes
in size (it is private, and not will be made available to the public).
The final problem with this runefs in particular, and the private
implementation as well, is that the bad blocks data hiding technique is now
public knowledge (quite obviously). This highlights the problem with data
hiding techniques, they become out dated. For this reason data hiding
should always be used in conjunction with at least one other anti-forensics
technology, such as encryption.
There are more ways of securely storing data on the file system far
from the prying eyes of the forensic analyst, and a research paper is due
shortly that will detail many of them. However, this is the last this
article will mention on data hiding, now the focus shifts to data
destruction.
The major vulnerablity with data aquisition is that the evidence being
gathered must be there when the forensic analyst begins his investigation.
Non-existant data, obviously, cannot be gathered, and without this crucial
information the forensic analyst is incapable of progressing the
investigation.
Within a Unix file system all of the following places will contain
traces of the existence of a file -- they contain evidence:
* inodes
* directory entries
* data blocks
# ./ils /dev/hda6
class|host|device|start_time
ils|XXX|/dev/hda6|1026771982
st_ino|st_alloc|st_uid|st_gid|st_mtime|st_atime|st_ctime|st_dtime|st_mode|\
st_nlink|st_size|st_block0|st_block1
12|f|0|0|1026771841|1026771796|1026771958|1026771958|100644|0|86|545|0
13|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|546|0
14|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|547|0
15|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|548|0
16|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|549|0
17|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|550|0
18|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|551|0
19|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|552|0
20|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|553|0
21|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|554|0
22|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|555|0
23|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|556|0
24|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|557|0
25|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|558|0
26|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|559|0
27|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|560|0
28|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|561|0
29|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|562|0
30|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|563|0
31|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|564|0
32|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|565|0
33|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|566|0
34|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|567|0
35|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|568|0
36|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|569|0
37|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|570|0
#
# ./necrofile -v -v -v -v /dev/hda6
Scrubbing device: /dev/hda6
12 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
13 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
14 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
15 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
16 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
17 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
18 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
19 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
20 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
21 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
22 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
23 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
24 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
25 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
26 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
27 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
28 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
29 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
30 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
31 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
32 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
33 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
34 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
35 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
36 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
37 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
# ./ils /dev/hda6
class|host|device|start_time
ils|XXX|/dev/hda6|1026772140
st_ino|st_alloc|st_uid|st_gid|st_mtime|st_atime|st_ctime|st_dtime|st_mode|\
st_nlink|st_size|st_block0|st_block1
#
# ./fls -d /dev/hda6 2
? * 0: a
? * 0: b
? * 0: c
? * 0: d
? * 0: e
? * 0: f
? * 0: g
? * 0: h
? * 0: i
? * 0: j
? * 0: k
? * 0: l
? * 0: m
? * 0: n
? * 0: o
? * 0: p
? * 0: q
? * 0: r
? * 0: s
? * 0: t
? * 0: u
? * 0: v
? * 0: w
? * 0: x
? * 0: y
? * 0: z
#
# ./klismafile -v /mnt
Scrubbing device: /dev/hda6
cleansing /
-> a
-> b
-> c
-> d
-> e
-> f
-> g
-> h
-> i
-> j
-> k
-> l
-> m
-> n
-> o
-> p
-> q
-> r
-> s
-> t
-> u
-> v
-> w
-> x
-> y
-> z
Total files found: 29
Directories checked: 1
Dirents removed : 26
#
# ./fls -d /dev/hda6 2
#
These examples speak for themselves. The 'fls' utility is part of the
TCT-UTILS package, and is intended to examine directory files. In this
case, it is listing all deleted directory entries in the root directory of
the file system. Klismafile is then run in verbose mode, listing and
overwriting each directory entry it encounters. After klismafile, fls is
incapable of noting that anything is amiss within the directory file.
Note: The linux 2.4 kernel caches directories in kernel memory, rather
than immediately updating the file system on disk. Because of this, the
directory file that klismafile examines and attempts to clean might not be
current, or the changes made might get overwritten by the kernel. Usually,
performing disk activity in another directory will flush the cache,
allowing kilsmafile to work optimally.
--[ 5 - Conclusion
Digital forensic tools are buggy, error prone and inherently flawed.
Despite these short comings they are being relied on more and more
frequently to investigate computer break-ins. Given that this
fundamentally broken software plays such a key role in incident response,
it is somewhat surprising that no-one has documented anti-forensic
techniques, nor sort to develop counter-measures (anti-anti-forensics).
Some suggestions regarding anti-anti-forensics methodology are presented
here, to provide the security community a foothold in the struggle against
anti-forensics.
--[ 6 - Greets
--[ 7 - References:
--[ 8 - APPENDIX A
The second extended file system (ext2fs) is the standard file system on
the Linux OS. This paper will provide an introduction to the file system.
Reading this document is no substitute for reading the src, both in the
kernel and in the ext2fs library.
. o O ( B L O C K S ) O o .
The basic component of the file system is the data block, used to store
file content. Typically, the smallest addressable unit on a hard disk is a
sector (512 bytes), but this is too small for decent I/O rates. To increase
performance multiple sectors are clustered together and treated as one
unit: the data block. The typical block size on an ext2fs system is 4096
bytes; however, it can be 2048 bytes or even as small as 1024 (8, 4 and 2
sectors, respectively).
. o O ( I N O D E S ) O o .
The second core part of the file system, the inode, is the heart of
the Unix file system. It contains the meta-data about each file including:
pointers to the data blocks, file permissions, size, owner, group and other
vital peices of information.
---------------------------------------------------------------------------
struct ext2_inode {
__u16 i_mode; /* File mode */
__u16 i_uid; /* Owner Uid */
__u32 i_size; /* Size in bytes */
__u32 i_atime; /* Access time */
__u32 i_ctime; /* Creation time */
__u32 i_mtime; /* Modification time */
__u32 i_dtime; /* Deletion Time */
__u16 i_gid; /* Group Id */
__u16 i_links_count; /* Links count */
__u32 i_blocks; /* Blocks count */
__u32 i_flags; /* File flags */
union {
struct {
__u32 l_i_reserved1;
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
struct {
__u32 m_i_reserved1;
} masix1;
} osd1; /* OS dependent 1 */
__u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__u32 i_version; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
__u32 i_dir_acl; /* Directory ACL */
__u32 i_faddr; /* Fragment address */
union {
struct {
__u8 l_i_frag; /* Fragment number */
__u8 l_i_fsize; /* Fragment size */
__u16 i_pad1;
__u32 l_i_reserved2[2];
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
__u8 h_i_fsize; /* Fragment size */
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
struct {
__u8 m_i_frag; /* Fragment number */
__u8 m_i_fsize; /* Fragment size */
__u16 m_pad1;
__u32 m_i_reserved2[2];
} masix2;
} osd2; /* OS dependent 2 */
};
---------------------------------------------------------------------------
* i_mode The mode of the file, this is the usual octal permissions
that Unix users should be familiar with.
* i_size The size of the file, in bytes. Clearly the maximum size is
4G, as size is an unsigned 32bit integer. Support for 64bit
file sizes had been hacked in with the following define
supplying the high 32bits:
#define i_size_high i_dir_acl
* i_atime The last time the file was accessed. All times are stored
in usual Unix manner: seconds since the epoch.
* i_dtime The deletion time of the file. If the file is still live
then the time will be 0x00000000.
* i_links_count The number of times that the file is referenced in the high
level file system. That is, each hard link to the file
increments this count. When the last link to the file is
removed from the FS, and the links count reaches 0, the
file is deleted. The blocks referenced by the inode are
marked as free in the bitmap.
* i_block[] The block pointers. There are 15 array elements, the first
12 elements are direct blocks pointers; their blocks
contain actual file content. The 13th element points to a
block that acts as an extension of the array. This block is
an indirect block, and the pointers it contains point to
additional direct blocks. The 14th element points to a block
containing an array of block pointers to indirect blocks.
This element is the doubly indirect block. The last element
is the trebly indirect block. This block contains pointers
to doubly indirect blocks.
---------------------------------------------------------------------------
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
---------------------------------------------------------------------------
* i_faddr The fragment address. Fragments are not used on the ext2fs;
therefore, this value is always 0.
---------------------------------------------------------------------------
#define EXT2_BAD_INO 1 /* Bad blocks inode */
#define EXT2_ROOT_INO 2 /* Root inode */
#define EXT2_ACL_IDX_INO 3 /* ACL inode */
#define EXT2_ACL_DATA_INO 4 /* ACL inode */
#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
---------------------------------------------------------------------------
The bad blocks inode contains block pointers to data blocks that occupy
bad sectors of the hard disk. The root inode is the root directory that
contains the head of the file system tree. The other inodes are not
typically used on production systems. The first inode used for user files
is inode 11. This inode is the directory "lost+found", created by the tool
mkfs.
. o O ( S U P E R B L O C K ) O o .
The super block is the most basic means that the kernel has of
determining the status of the file system. It indicates the number of
inodes, blocks, and groups, in addition to various other pieces of
information. The elements within the super block structure change more
rapidly than the inode or group data. This is because libext2fs adds
features to the ext2fs which might not be implemented in the kernel. The
format we examine is from e2fsprogs-1.19.
The super block is 1024 bytes in size, and offset 1024 bytes from the
start of the partition.
* s_mtime The last time the file system was mounted. All time
values are stored as seconds since the epoch.
* s_lastcheck The last time the file system was fsck'd, stored in
typical Unix sec's since epoch format.
* s_block_group_nr The block group that this super block is stored in.
* s_padding1 padding.
. o O ( G R O U P S ) O o .
* bg_pad padding.
* pg_reserved[] padding.
. o O ( D I R E C T O R I E S ) O o .
* rec_len The size of the directory entry. As the length of the name
can be anything up to 255 byte, this allows for more
efficient use of space within the directory file.
* name_len The length of the file's name. This can be up to 255 bytes.
* file_type The type of file, i.e. symlink, device, etc. etc. The
following are valid values:
---------------------------------------------------------------------------
#define EXT2_FT_UNKNOWN 0
#define EXT2_FT_REG_FILE 1
#define EXT2_FT_DIR 2
#define EXT2_FT_CHRDEV 3
#define EXT2_FT_BLKDEV 4
#define EXT2_FT_FIFO 5
#define EXT2_FT_SOCK 6
#define EXT2_FT_SYMLINK 7
---------------------------------------------------------------------------
This concludes the walk through of the physical layout of the ext2 file
system. Further information is available from
http://e2fsprogs.sourceforge.net.