Hiding data in ext2 file system
The ext2 and ext3 filesystems are pretty cool, but they work so well most people probably take them for granted. One thing you can do is hide information, but I am not talking about something as simple as a hidden file like .bash_history.
Let's get the inode number of a file.
[root@terlingua /]# cd /boot/grub
[root@terlingua grub]# ls -i grub.conf
18073 grub.conf
Then let's find out the fundamental file system block size.
[root@terlingua grub]# stat -f grub.conf
File: "grub.conf"
ID: 0 Namelen: 255 Type: ext2/ext3
Block size: 1024 Fundamental block size: 1024
Blocks: Total: 101086 Free: 83558 Available: 78339
Inodes: Total: 26104 Free: 26068
Now we can use debugfs to get the physical data blocks that the file's inodes point to. In this case it only has one physical data block.
[root@terlingua grub]# debugfs -R "stat grub/grub.conf" /dev/sda1
debugfs 1.39 (29-May-2006)
Inode: 18073 Type: regular Mode: 0600 Flags: 0x0 Generation: 425928246
8
User: 0 Group: 0 Size: 739
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 2
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x48f756ba -- Thu Oct 16 09:59:06 2008
atime: 0x491893f9 -- Mon Nov 10 14:05:13 2008
mtime: 0x48f756ba -- Thu Oct 16 09:59:06 2008
BLOCKS:
(0):77313
TOTAL: 1
Now that we know the physical data block number and the file system's block size, we can read the data off of the hard disk.
[root@terlingua grub]# dd skip=77313 bs=1024 count=1 if=/dev/sda1 | head -n 1
1+0 records in
1+0 records out
1024 bytes (1.0 kB) copied, 0.01572 seconds, 65.1 kB/s
# grub.conf generated by anaconda
If we save the block and pipe it to less, we can see that the file only occupies a portion of the block.
[root@terlingua grub]# dd skip=77313 bs=1024 count=1 if=/dev/sda1 of=block; cat block | less
# initrd /initrd-version.img
#boot=/dev/sda
default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.18-92.el5)
root (hd0,0)
kernel /vmlinuz-2.6.18-92.el5 ro root=/dev/VolGroup00/LogVol00 rhgb quie
t
initrd /initrd-2.6.18-92.el5.img
title CentOS (2.6.26.5)
root (hd0,0)
kernel /vmlinuz-2.6.26.5 ro root=/dev/VolGroup00/LogVol00 rhgb quiet
initrd /initrd-2.6.26.5.img
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@
(END)
Now we can modify the block and store some data in there past the end of the file. The resulting file is smaller because it truncated the end of the file.
[root@terlingua boot]# echo "some hidden data" | dd seek=900 bs=1 of=block
17+0 records in
17+0 records out
17 bytes (17 B) copied, 0.000112 seconds, 152 kB/s
[root@terlingua boot]# ls -l block
-rw-r--r-- 1 root root 917 Feb 12 11:54 block
Now we can write the modified block back to the disk... very carefully!
[root@terlingua boot]# dd seek=77313 bs=1024 if=block of=/dev/sda1
0+1 records in
0+1 records out
917 bytes (917 B) copied, 5.4e-05 seconds, 17.0 MB/s
[root@terlingua boot]# dd skip=77313 bs=1024 count=1 if=/dev/sda1 | less
# initrd /initrd-version.img
#boot=/dev/sda
default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.18-92.el5)
root (hd0,0)
kernel /vmlinuz-2.6.18-92.el5 ro root=/dev/VolGroup00/LogVol00 rhgb quie
t
initrd /initrd-2.6.18-92.el5.img
title CentOS (2.6.26.5)
root (hd0,0)
kernel /vmlinuz-2.6.26.5 ro root=/dev/VolGroup00/LogVol00 rhgb quiet
initrd /initrd-2.6.26.5.img
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@some hidden data
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
(END)
Now verify that normal file system based I/O does not show the data.
[root@terlingua boot]# tail grub/grub.conf
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.18-92.el5)
root (hd0,0)
kernel /vmlinuz-2.6.18-92.el5 ro root=/dev/VolGroup00/LogVol00 rhgb quiet
initrd /initrd-2.6.18-92.el5.img
title CentOS (2.6.26.5)
root (hd0,0)
kernel /vmlinuz-2.6.26.5 ro root=/dev/VolGroup00/LogVol00 rhgb quiet
initrd /initrd-2.6.26.5.img
The recorded file size of 739 bytes in the file's primary inode never changed, so the remaining bytes in the last block are not technically part of the file. The hidden data in this example will persist unless the file grows to over 900 bytes.