Nicolas SURRIBAS

Développement / Réseau / Sécurité Informatique

inforensique

Le système de fichiers Ext3

Rédigé par devloop - -

Après m'être attaqué à Ext2, je continue mes études des systèmes de fichiers en vous parlant cette fois de Ext3, un système de fichiers dont l'objectif était de combler les lacunes de la version précédente.

On dit souvent qu'Ext3 n'est rien de plus qu'un Ext2 avec un système de journalisation.
Pourtant Ext3 apporte des améliorations en terme de vitesse d'accès aux données et permet le redimensionnement du système de fichier. Quelques changements sur la gestion des métadonnées ont aussi leur importance comme nous allons le voir.

Mais avant de continuer il est important de comprendre ce qu'est un journal pour un système de fichiers.
Quand on étite un fichier sur notre disque dur, le système d'exploitation effectue tout un tas d'opérations pour mettre à jour le système de fichiers. Par exemple si on ajoute des données à un fichier, le système va devoir allouer des blocks (rechercher des blocks libres disponibles), les marquer comme utilisés, y inscrire les données puis enfin mettre à jour les métadonnées (taille du fichier, pointeurs sur les blocks utilisés, dates de modification, accès etc).
Imaginons qu'une panne électrique arrive en plein milieu de ces opérations : on pourrait très bien se retrouver avec des blocks marqués comme utilisés dans le système de fichiers mais qui ne sont en réalité utilisés par aucun fichiers... c'est donc de l'espace perdu.

Heureusement le programme fsck se charge de chercher ce type d'erreurs pour nous... seulement vérifier l'intégralité du système de fichiers c'est long ! C'est pour cela que l'on a créé la journalisation : à chaque modification du système de fichiers les opérations à effectuer sont enregistrées avant d'être effectuées.
Après une panne électrique le système n'a qu'à regarder dans le journal quelles opérations il doit reprendre (ou annuler) pour s'assurer que tout fonctionne.

Comme pour la dernière fois, j'ai effectué des tests en boîte noire, à l'aide du SleuthKit.
Au lieu de créer une partition sur un disque dur, j'ai opté pour la création d'un fichier conteneur :
dd if=/dev/zero of=ext3.img count=50000
mkfs.ext3 ext3.img

On obtiens ainsi un système de fichier ext3 d'environ 25Mo. Regardons ce que le formatage nous a donné :
# ils -a ext3.img
class|host|device|start_time
ils|shirley||1151770485
st_ino|st_alloc|st_uid|st_gid|st_mtime|st_atime|st_ctime|st_mode|st_nlink|st_size|st_block0|st_block1
1|a|0|0|1151770448|1151770448|1151770448|0|0|0|0|0
2|a|0|0|1151770448|1151770448|1151770448|40755|3|1024|201|0
3|a|0|0|0|0|0|0|0|0|0|0
4|a|0|0|0|0|0|0|0|0|0|0
5|a|0|0|0|0|0|0|0|0|0|0
6|a|0|0|0|0|0|0|0|0|0|0
7|a|0|0|0|0|0|0|0|0|0|0
8|a|0|0|1151770448|0|1151770448|100600|1|1048576|214|215
9|a|0|0|0|0|0|0|0|0|0|0
10|a|0|0|0|0|0|0|0|0|0|0
11|a|0|0|1151770448|1151770448|1151770448|40700|2|12288|202|203

Cette commande nous permet d'afficher les inodes actuellement allouées (utilisées). Comment expliquer que 11 inodes soient déjà utilisées avant que l'on a créé aucun fichier ?
La réponse est trouvable dans le fichier /usr/include/linux/ext3_fs.h :
/*
 * Special inodes numbers
 */
#define EXT3_BAD_INO             1      /* Bad blocks inode (blocks inutilisables) */
#define EXT3_ROOT_INO            2      /* Root inode (la racine / ) */
#define EXT3_BOOT_LOADER_INO     5      /* Boot loader inode */
#define EXT3_UNDEL_DIR_INO       6      /* Undelete directory inode */
#define EXT3_RESIZE_INO          7      /* Reserved group descriptors inode */
#define EXT3_JOURNAL_INO         8      /* Journal inode (le fameux journal de ext3) */

Les inodes 6, 7, 9 et 10 sont réservées pour un usage ultérieur (en prévision pour une prochaine version). Les inodes 3 et 4 sont utilisées pour stocker les ACL (un système de permissions plus évolué que le traditionnel user-group-others)
L'inode 11 est utilisé par le répertoire lost+found caractéristique des systèmes Ext :
# fls -rm / ext3.img
0|/lost+found|0|11|16832|d/drwx------|2|0|0|0|12288|1151770448|1151770448|1151770448|1024|0

Montons maintenant le système de fichier et créons un fichier :
mount -o loop ext3.img /mnt/
echo test > /mnt/fichier
umount /mnt

Observons le résultat avec fls (travaille au niveau des noms de fichiers) :
# fls -rm / ext3.img
0|/lost+found|0|11|16832|d/drwx------|2|0|0|0|12288|1151770448|1151770448|1151770448|1024|0
0|/fichier|0|12|33188|-/-rw-r--r--|1|0|0|0|5|1151770676|1151770676|1151770676|1024|0

'ils' nous rensigne sur les inodes :
# ils -a ext3.img
class|host|device|start_time
ils|shirley||1151770757
st_ino|st_alloc|st_uid|st_gid|st_mtime|st_atime|st_ctime|st_mode|st_nlink|st_size|st_block0|st_block1
1|a|0|0|1151770448|1151770448|1151770448|0|0|0|0|0
2|a|0|0|1151770676|1151770673|1151770676|40755|3|1024|201|0
3|a|0|0|0|0|0|0|0|0|0|0
4|a|0|0|0|0|0|0|0|0|0|0
5|a|0|0|0|0|0|0|0|0|0|0
6|a|0|0|0|0|0|0|0|0|0|0
7|a|0|0|0|0|0|0|0|0|0|0
8|a|0|0|1151770448|0|1151770448|100600|1|1048576|214|215
9|a|0|0|0|0|0|0|0|0|0|0
10|a|0|0|0|0|0|0|0|0|0|0
11|a|0|0|1151770448|1151770448|1151770448|40700|2|12288|202|203
12|a|0|0|1151770676|1151770676|1151770676|100644|1|5|5121|0


La dernière ligne concerne notre nouveau fichier :
  • son inode est 12
  • l'inode est allouée (a)
  • l'utilisateur propriétaire est root (uid 0)
  • le groupe propriétaire est root (gid 0)
  • les dates de modification, dernier accès, changement des métadonnées ont la même valeur
  • l'inode est pointée par un seul lien ("fichier")
  • le fichier fait 5 octets
  • Les deux dernières entrées permettent au système de retrouver les blocks où ont été enregistré le contenu du fichier.

Effacons le fichier et observons le résultat :
# rm /mnt/fichier
# fls -rm / ext3.img
0|/lost+found|0|11|16832|d/drwx------|2|0|0|0|12288|1151770448|1151770448|1151770448|1024|0
0|/fichier (deleted)|0|12|33188|-/-rw-r--r--|0|0|0|0|0|1151770676|1151770861|1151770861|1024|0

Par rapport au fls précédent on a perdu... la taille du fichier !

# ils ext3.img 12
class|host|device|start_time
ils|shirley||1151770996
st_ino|st_alloc|st_uid|st_gid|st_mtime|st_atime|st_ctime|st_mode|st_nlink|st_size|st_block0|st_block1
12|f|0|0|1151770861|1151770676|1151770861|100644|0|0|0|0

Au niveau de l'inode les changements sont radicaux : les adresses des blocks sont maintenant à 0.

Contrairement à Ext2, nous ne pouvons pas récupérer le contenu du fichier à l'aide de la commande icat.
Nos observations peuvent se retrouver sur la page Wikipédia consacrée à Ext3 :

Unlike ext2, ext3 zeroes out the block pointers in the inodes of deleted files. Because this removes all metadata for the affected files, the files cannot be recovered directly. The user's only recourse is to grep the hard drive for data known to signal the start and end of the file. This provides slightly more secure deletion than ext2, which can be either an advantage or a disadvantage.


Tout est dit ! Les données sont plus difficiles à extraire toutefois elles sont toujours présentes sur le disque. Différents outils existent comme foremost qui se charge d'extraire les fichiers d'une partition en se fiant à leurs entête.
Mais tout n'est pas finis !! Après tout Ext3 journalise les changements alors voyons ce qu'il y a dans ce fameux journal :
icat ext3.img 8 > journal
hexdump journal

Malheureusement c'est loin d'être la gloire... on retrouve tout de même quelques octets correpondants aux métadonnées du fichier :
0001580 81a4 0000 0000 0000 a034 44a6 a0ed 44a6
0001590 a0ed 44a6 a0ed 44a6 0000 0000 0000 0000
00015a0 0000 0000 0000 0000 0000 0000 0000 0000

0x44A6A034 est la valeur hexadécimale de 1151770676 (la date de dernière modification) et 0x81A4 devient 100644 en octal (les permissions sur le fichier). Seulement... ces informations concernent le fichier effacé et non le fichier avant sa suppression.
Impossible donc de récupérer l'ancienne inode.

L'explication est lisible ici : Ext3 utilise une journalisation dite physique et ne conserve que les modifications sur les blocks, contrairement à une journalisation logique qui enregistre les modifications sur les fichiers.

Ne baissons pas les bras : nous allons récupérer le groupe de blocks sur lequel se trouve notre fichier afin de réduire le champ d'action pour la récupération des données.
Nous utilisons la commande imap de debugfs qui nous renseigne sur l'inode qui nous intéresse (12) :
# debugfs ext3.img
debugfs 1.38 (30-Jun-2005)
debugfs:  imap <12>
Inode 12 is part of block group 0
        located at block 6, offset 0x0180

Avec fsstat du SleuthKit récupérons les informations sur le groupe de block 0 :
# fsstat ext3.img
Group: 0:
  Inode Range: 1 - 1568
  Block Range: 1 - 8192
  Layout:
    Super Block: 1 - 1
    Group Descriptor Table: 2 - 2
    Data bitmap: 3 - 3
    Inode bitmap: 4 - 4
    Inode Table: 5 - 200
    Data Blocks: 201 - 8192
  Free Inodes: 1557 (99%)
  Free Blocks: 6949 (84%)
  Total Directories: 2
...

On extrait l'ensemble de ces 8192 blocks dans un fichier :
# dls ext3.img 1-8192 < out
# ls -hl out
-rw-r--r--  1 root root 6,8M 2006-07-01 18:49 out
# strings out
test

On obtient un fichier de 6,8Mo mais le contenu est dedans.

La journalisation fait de Ext3 un système de fichiers plus stable en cas d'arrêt brutal de l'ordinateur mais, contrairement à ce que l'on pourrait croire, le journal (au niveau physique) rends la récupération de données effacées bien plus difficile.

Le noyau Linux permet de choisir le niveau de journalisation lors du montage du système de fichier :
"mount -o data=journal"
Journals all data and metadata, so data is written twice. This
is the mode which all prior versions of ext3 used.

"mount -o data=ordered"
Only journals metadata changes, but data updates are flushed to
disk before any transactions commit. Data writes are not atomic
but this mode still guarantees that after a crash, files will
never contain stale data blocks from old files.

"mount -o data=writeback"
Only journals metadata changes, and data updates are entirely
left to the normal "sync" process. After a crash, files will
may contain stale data blocks from old files: this mode is
exactly equivalent to running ext2 with a very fast fsck on reboot.

Le mode utilisé par défaut est "ordered". En conclusion l'effacement sécurisé sur un système Ext3 consiste simplement à reécrire par dessus les blocks alloués au fichier. Par défaut il n'y a pas de risques de retrouver le contenu d'un fichier dans le journal.

Références :
Ext3 - Wikipedia
Journaling File System - Wikipedia
Brian Carrier - Why Recovering a Deleted Ext3 File Is Difficult
Linux Ext3 FAQ
Récupération de photos numériques sur une partition Ext3

Ext2 et les effacements sécurisés

Rédigé par devloop - -

Dans ma solution de l'épreuve forensics du challenge Securitech 2006, vous avez peut-être été étonné de savoir à quel point il était aisé d'étudier une image d'un disque et d'en extraire des données, ce même si les fichiers ont été effacés.

La question que l'on peut se poser est "Pourquoi des mesures n'ont pas été prises au niveau des systèmes d'exploitation pour garantir une suppression efficace des données ?"
Personnellement je vois deux réponses. La première est tout simplement que si l'effacement sécurisé peut-être considéré comme une caractéristique utile (voire nécessaire), la possibilité de pouvoir récupérer des données malencontreusement effacés est toute aussi importante (et à surrement sauvé un bon nombre de personnes).
La seconde réponse pourrait être l'existence d'un lobby puissant qui a tout intérêt à ce que les particuliers n'aient pas de moyens d'effacer leurs données les plus sensibles (organisations gouvernementales etc).
Vous avez peut-être entendu parler de cette rumeur de backdoor dans Windows Vista demandée par le gouvernement britanique et qui lui aurait permis de passer à travers les systèmes de cryptage inclus dans le prochain Windows. Cette rumeur lancée par le site de la BBC a très vite été démentie mais à tout de même eu le temps de faire parler d'elle et de faire réfléchir.

L'hypothèse que des pressions soient exercées sur Microsoft pour empécher l'effacement sécurisé ne me parrait si extraordinaire que ça...
Linux propose bien une méthode d'effacement sécurisée par le biais de l'attribut 's' que l'on peut fixer avec la commande chattr (voir la page de manuel).

Afin de comprendre le fonctionnement du système de fichiers ext2, j'ai crée une petite partition sur un vieux disque et ai effectué mes tests en "boîte noire" à l'aide du live cd grml dont j'ai déjà parlé et qui contient différents outils d'analyse forensics ou de récupération de données.

Après un cfdisk obligatoire, j'ai écrasé le contenu de la partition puis l'ai formaté en ext2 :
dd if=/dev/zero of=/dev/hda1
mke2fs /dev/hda1
Cette opération me donne un système ext2 tout ce qu'il y a de plus standard (sans journalisation en plus).
A l'aide de la commande strings qui extrait les chaines de caractères d'un fichier, on peut déjà connaître le contenu du disque :
# strings /dev/hda1
lost+found
Créons maintenant un répertoire et un fichier. Ces données nous servirons de base pour la suite :
# mount /dev/hda1 /mnt
# ls /mnt/
.  ..  lost+found
# mkdir /mnt/mondir
# umount /mnt
# strings /dev/hda1
lost+found
mondir

# echo "ceci est un test" > /mnt/mondir/fichier.txt
# strings /dev/hda1
lost+found
mondir
ceci est un test
fichier.txt
Lors de mes tests j'ai passé beaucoup de temps à démonter puis remonter le système de fichier... pour gagner de la place je ne marque pas les commandes mount et umount qui seraient trop nombreuses.
Utilisons les commandes du SleuthKit pou avoir un apperçu des informations présentes sur le disque :
# fls -r /dev/hda1
d/d 11: lost+found
d/d 1673:       mondir
+ r/r 1674:     fichier.txt
# fls -rm / /dev/hda1
0|/lost+found|0|11|16832|d/drwx------|2|0|0|0|12288|1149019267|1149019267|1149019267|1024|0
0|/mondir|0|1673|16877|d/drwxr-xr-x|2|0|0|0|1024|1149019487|1149019583|1149019583|1024|0
0|/mondir/fichier.txt|0|1674|33188|-/-rw-r--r--|1|0|0|0|17|1149019583|1149019583|1149019583|1024|0
# icat /dev/hda1 1674
ceci est un test
Notre fichier.txt possède le numéro d'inode 1674 et sa taille est de 17 octets.

Note : En analyse forensic on peut classer les données contenues sur le disque en plusieurs catégories.
  • Les données concernant le système de fichier lui-même : sa structure, son type, les caractéristiques qu'il propose
  • Le contenu des fichiers, organisées sous forme de blocks de taille fixe. Un block possède un état qui permet de savoir si un fichier l'utilise (allocated) ou s'il est libre pour créer un éventuel fichier (free ou unallocated)
  • Les métadonnées (metadata) : ce sont les données qui décrivent un fichier. Sous Linux on parle d'"inode". On y trouve les permissions, les dates de modification, dernier accès et changement d'état ainsi que les pointeurs vers les blocks de données que l'on a vu précédemment.
  • La catégorie des noms de fichiers. Il s'agit principalement de structures décrivant les répertoires. On peut décrire un répertoire comme un tableau dont chaque entrée contient un nom de fichier, sa longueur (la longueur du nom, pas du fichier lui-même) et le numéro d'inode correspondant au fichier
  • La catégorie applicative qui est la plupart du temps un fichier de journalisation. Cette catégorie ne nous intéresse pas puisque par défaut ext2 n'offre pas de système de journalisation
Les trois catégories ne nous retiendrons ici sont les noms de fichiers, les contenus des fichiers et enfin les métadonnées qui font le lien entre ces deux catégories.

Passons maintenant à l'action et effaçons notre fichier.txt :
# rm /mnt/mondir/fichier.txt
# strings /dev/hda1
lost+found
mondir
ceci est un test
fichier.txt
A première vue le résultat est plutôt navrant puisque l'on retrouve le nom du fichier effacé ainsi que son contenu.
Voyons ça de plus près :
# fls -rm / /dev/hda1
0|/lost+found|0|11|16832|d/drwx------|2|0|0|0|12288|1149019267|1149019267|1149019267|1024|0
0|/mondir|0|1673|16877|d/drwxr-xr-x|2|0|0|0|1024|1149019777|1149019780|1149019780|1024|0
0|/mondir/fichier.txt (deleted)|0|0|0|-/----------|0|0|0|0|0|0|0|0|1024|0
# ils /dev/hda1
class|host|device|start_time
ils|shirley||1149020073
st_ino|st_alloc|st_uid|st_gid|st_mtime|st_atime|st_ctime|st_mode|st_nlink|st_size|st_block0|st_block1
1|a|0|0|1149019267|1149019267|1149019267|0|0|0|0|0
1674|f|0|0|1149019583|1149019583|1149019780|100644|0|17|9217|0
# icat /dev/hda1 1674
ceci est un test
La commande fls s'intéresse aux noms de fichiers, elle explore les tableaux de répertoires et est en mesure de détecter les fichiers qui ont été effacés.
Comme écrit dans Forensic Discovery de Wietse Venema et Dan Farmer, la suppression d'un fichier a les conséquences suivantes sur la catégorie noms de fichiers :
When a file is deleted, the directory entry with the file name and inode number is marked as unused. Typically, the inode number is set to zero, so that the file name becomes disconnected from any file information. (...)
Names of deleted files can still be found by reading the directory with the strings command.
Nous avons donc perdu le lien entre le nom de fichier et ses métadonnées... heureusement la commande ils nous permet de récupérer la liste des inodes correspondant à des fichiers effacés.
La commande icat quand à elle nous affiche le contenu du fichier correspondant à l'inode qui lui passe en paramêtre.
Le livre de Venema et Farmer nous informe aussi que :
With 2.2 Linux kernels, the Linux Ext2fs (second extended) file system marks the directory entry as unused, but preserves the connections between directory entry, file attributes and file data blocks.
Autant dire que l'analyse forensics doit être très facile avec les noyaux 2.2... Heureusement pour nos données la situation a changée.

Si l'on est parvenu à récupérer le contenu de notre fichier effacé avec icat, c'est parce que la taille du fichier est resté stockée sur le disque...
Voyons ce qu'il se passe si on tronque le fichier avant de l'effacer. Pour cela j'ai écris quelques lignes de code (trunc.c) :
#include 
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc,char *argv[])
{
  int fd;
  if(argc!=2)exit(1);
  fd=open(argv[1],O_WRONLY);
  ftruncate(fd,0);
  close(fd);
  unlink(argv[1]);
  return 0;
}
On compile et on lance :
# gcc -o trunc trunc.c -Wall -W -pedantic
# ./trunc /mnt/mondir/fichier.txt
# ils /dev/hda1
class|host|device|start_time
ils|shirley||1149021092
st_ino|st_alloc|st_uid|st_gid|st_mtime|st_atime|st_ctime|st_mode|st_nlink|st_size|st_block0|st_block1
1|a|0|0|1149020576|1149020576|1149020576|0|0|0|0|0
1674|f|0|0|1149021076|1149020953|1149021076|100644|0|0|0|0
# icat /dev/hda1 1674
#
# strings /dev/hda1
lost+found
mondir
Ceci est un test
fichier.txt
Cette fois la commande icat n'a pas pu récupérer le contenu du fichier. Mais le contenu du fichier est toujours lisible sur le disque. Un outil comme foremost est tout à fait capable d'extraire des fichiers en se fiant uniquement à leur entête.

Intéressons-nous maintenant au nom du fichier effacé toujours présent sur le disque. En repartant de notre base et en renommant deux fois le fichier avec des noms de même taille on obtient un résultat satisfaisant :
# mv /mnt/mondir /mnt/xxxxxx
# strings /dev/hda1
lost+found
mondir
xxxxxx
fichier.txt
Ceci est un test
# mv /mnt/xxxxxx /mnt/zzzzzz
# strings /dev/hda1
lost+found
zzzzzz
xxxxxx
Ceci est un test
fichier.txt
Nous sommes sur la bonne voie. On retrouve en effet ces principes dans le code source de la commande d'effacement sécurisée shred (shred.c).
Le ftruncate est présent dans la fonction principale do_wipefd La technique utilisée pour les noms de fichiers est en revanche différente (faites un shred -u -v fichier pour comprendre vite fait le mécanisme) :
/*
* Repeatedly rename a file with shorter and shorter names,
* to obliterate all traces of the file name on any system that
* adds a trailing delimiter to on-disk file names and reuses
* the same directory slot.
*/
Attaquons-nous maintenant au contenu du fichier... une seule solution : écraser les données par d'autres données.
# echo blahblah > /mnt/zzzzzz/fichier.txt
# strings /dev/hda1
lost+found
zzzzzz
xxxxxx
blahblah
fichier.txt
# perl -e "print 'A'x400" > /mnt/zzzzzz/fichier.txt
# strings /dev/hda1
lost+found
zzzzzz
AAAAA[...]AAAAA
fichier.txt
# echo blahblah > /mnt/zzzzzz/fichier.txt
# strings /dev/hda1
lost+found
zzzzzz
xxxxxx
blahblah
fichier.txt
# perl -e "print 'B'x4100" > /mnt/zzzzzz/fichier.txt
# strings /dev/hda1
lost+found
zzzzzz
BBBB[...]BBBBBB
fichier.txt
# echo blahblah > /mnt/zzzzzz/fichier.txt
# strings /dev/hda1
lost+found
zzzzzz
blahblah
BBBB[...]BBBBBB
fichier.txt
Les résultats semblaient satisfaisants au début... mais on s'apperçoit à la fin qu'il faut plus de données pour écraser la slack space et par conséquent l'ancien contenu du fichier.
Tout à l'heure je vous ai dis que le système gérait les données par blocks. Il ne peut pas manipuler chaque fichier à l'octet près, ce serait une énorme perte de temps.
A la création d'un fichier le système va allouer plusieurs blocks de données. D'après mes tests Linux en réserve au minimum deux. Le code suivant permet de connaître l'espace mémoire réservé pour un fichier (getstat.c) :
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

int main(int argc,char *argv[])
{
  struct stat mystat;
  if(argc!=2)exit(1);
  if(stat(argv[1],&mystat) < 0)exit(1);
  printf("Taille totale en octets: %d\nTaille de bloc pour E/S: %d\nNombre de blocs alloués: %d\n",
        mystat.st_size, mystat.st_blksize, mystat.st_blocks);
  return 0;
}
Voyons ce que ça donne :
# echo blahblah > /mnt/zzzzzz/fichier.txt
# ./getstat /mnt/zzzzzz/fichier.txt
Taille totale en octets: 9
Taille de bloc pour E/S: 4096
Nombre de blocs alloués: 2
Conlusion : pour un fichier de seulement 9 octets, le système en a réservé 8192 (2 x 4096). Il est possible de fixer cette taille lors de la création de la partition ext2. 4096 semble être un bon compromis entre vitesse d'accès et gaspillage de place.
Pour effacer de façon sûre notre fichier il faut donc écraser ces deux blocks de 4096 octets, le renommer plusieurs fois et rammener sa taille à zéro avant de l'effacer...
J'ai écris le code suivant pour réaliser cette opération (secrm.c) :
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc,char *argv[])
{
  int fd;
  char *buff;
  char *path1;
  char *path2;
  int i;
  struct stat mystat;
  if(argc!=2)exit(1);
  if(stat(argv[1],&mystat)<0)exit(1);
  if(!S_ISREG(mystat.st_mode))exit(1);
  printf("Taille totale en octets: %d\nTaille de bloc pour E/S: %d\nNombre de blocs alloués: %d\n",
        mystat.st_size, mystat.st_blksize, mystat.st_blocks);
  buff=(char*)malloc(strlen(basename(argv[1])));
  path1=strdup(argv[1]);
  path2=strdup(argv[1]);
  memset(buff,'x',strlen(basename(path1)));
  sprintf(path1,"%s/%s",dirname(path1),buff);
  printf("filename -> %s\n",path1);
  link(argv[1],path1);
  unlink(argv[1]);
  memset(buff,'z',strlen(basename(path2)));
  sprintf(path2,"%s/%s",dirname(path2),buff);
  link(path1,path2);
  unlink(path1);
  printf("filename -> %s\n",path2);
  free(path1);
  free(buff);
  buff=(char*)malloc(mystat.st_blksize);
  fd=open(path2,O_WRONLY);
  printf("Overwriting...\n");
  for(i=0;i<mystat.st_blocks;i++)
  {
    memset(buff,i,mystat.st_blksize);
    write(fd,buff,mystat.st_blksize);
  }
  free(buff);
  printf("Truncating...\n");
  fsync(fd);
  ftruncate(fd,0);
  close(fd);
  printf("Unlinking...\n");
  unlink(path2);
  free(path2);
  printf("Done !\n");
  return 0;
}
On reprends notre base et on teste :
# strings /dev/hda1
lost+found
mondir
Ceci est un test
fichier.txt
# ./secrm /mnt/mondir/fichier.txt
Taille totale en octets: 17
Taille de bloc pour E/S: 4096
Nombre de blocs alloués: 2
filename -> /mnt/mondir/xxxxxxxxxxx
filename -> /mnt/mondir/zzzzzzzzzzz
Overwriting...
Truncating...
Unlinking...
Done !
# strings /dev/hda1
lost+found
mondir
zzzzzzzzzzz
xxxxxxxxxxx
# fls -r /dev/hda1
d/d 11: lost+found
d/d 1673:       mondir
+ r/- * 0:      zzzzzzzzzzz
+ r/- * 0:      xxxxxxxxxxx
# ils /dev/hda1
class|host|device|start_time
ils|shirley||1149027322
st_ino|st_alloc|st_uid|st_gid|st_mtime|st_atime|st_ctime|st_mode|st_nlink|st_size|st_block0|st_block1
1|a|0|0|1149027164|1149027164|1149027164|0|0|0|0|0
1674|f|0|0|1149027269|1149027250|1149027269|100644|0|0|0|0
# icat /dev/hda1 1674
#
Mission accomplie !

Evidemment toute implémentation en kernel land serait bien plus efficace...
Sur le sujet des anti-forensics et de l'effacement sécurisé, la référence reste les travaux d'un certain The Grugq.
Je vous conseille vivement The Defiler's Toolkit de sa création qui contient un code pour effacer les noms des fichiers effacés sur votre disque (klismafile) ainsi qu'un code qui va s'occuper des métadonnées (necrofile).

Bibliographie :
File System Forensic Analysis de Brian Carrier
Forensic Discovery de Wietse Venema et Dan Farmer
shred.c par Colin Plumb
Secure Data Deletion for Linux File Systems
The Ext2 File System sur The Linux Tutorial
Secure Deletion of Data from Magnetic and Solid-State Memory de Peter Gutmann
Can Intelligence Agencies Read Overwritten Data? A repsonse to Gutmann. de Daniel Feenberg
the grugq - secure deletion patch, kernel 2.4.24
Defeating Forensic Analysis on Unix par the grugq

Solution de l'épreuve forensics du challenge Securitech 2006

Rédigé par devloop - -

Dans le jargon informatique, les analyses post-mortem ou plus simplement "analyses après intrusions" (en anglais Computer forensics) sont l'ensemble des techniques visant à récolter les preuves d'une activité illégale sur un système informatique.
Ce domaine est en pleine croissance et les sites dédiés à ce sujet commencent à fleurir sur la toile, preuve que l'efficacité des techniques de sécurisation reste très relative et que le nombre d'attaques va croissant.
Le forensics n'est pas seulement utilisé pour étudier les intrusions indésirables mais aussi pour les intrusions désirables (honeypots), afin de se tenir au courant des dernières techniques ou exploits en vogue.
C'est aussi un sujet épineux puisque techniquement délicat (essayez d'analyser l'intérieur d'un objet sans y mettre les doigts) et en rapport direct avec la connaissances des systèmes de fichiers (sujet à troll).
Mais ce qui est sûr c'est que c'est un sujet passionnant et que sa pratique nous apprend tout un tas de choses intéressantes.

Pour cette épreuve du Securitech de cette année, on nous donnait une image vmware d'un système linux récemment piraté.
On nous donnait quelques axes de recherches par le biais des 3 questions suivantes :
  • Quel est le chemin abolu du l'exécutable ayant permis à l'intrus d'entrer sur le système ?
  • Quel sont les coordonnées du pirate où sont envoyées des informations concernant le système ?
  • Quelle est la commande détournée (backdoor) permettant au pirate de reprendre possession du système ?

Lire la suite de Solution de l'épreuve forensics du challenge Securitech 2006