Malwares : étude des codes malveillants
Introduction
Il est illusoire de penser qu’un chapitre suffirait pour couvrir l’étude de codes malveillants.
La multiplicité des langages de programmation, des systèmes d’exploitation, des architectures de processeurs, l’utilisation de programmes légitimes de façon détournée, la très grande variété de vulnérabilités font qu’il existe une quasi-infinité de combinaisons qui rendent une approche systématique peu efficace, voire impossible.
De plus, il est courant de penser, à tort, qu’un antivirus est une solution de protection.
En effet, un antivirus n’est pas un bouclier qui vous empêche toute contamination, il remplit plutôt une fonction de vaccin : il vous empêche d’exécuter des malwares connus.
L’année 2020 fut une brillante démonstration de la limite de la vaccination : aucun vaccin ne protégeait de la Covid-19, malgré l’existence de nombreux vaccins, et être vacciné contre le tétanos ne vous protégeant pas d’une infection par un coronavirus.
Deux questions se posent donc :
« Dans quel cas est-il impossible d’élaborer un vaccin efficace ? » et « À quoi suis-je toujours vulnérable ? »
Pour répondre, il faut comprendre les limites des techniques de détection de malwares et savoir qu’en moyenne un antivirus ne vous protège que d’environ un tiers des codes malveillants.
L’analogie du vaccin fonctionne parfaitement pour les codes malveillants et puisque vous n’êtes jamais protégé contre 100 % des menaces, le plus simple reste de ne pas s’y exposer.
Ainsi, la détection d’un malware dans votre système d’information n’est...
Qu’est-ce qu’un malware ?
Un code malveillant, en anglais malicious software, souvent abrégé en malware, est un programme réalisant des actions malveillantes.
Une définition qui n’aide pas vraiment à avancer.
Le contenu et le comportement d’un malware dépendent grandement du but de son auteur.
L’ensemble des outils, programmes, processus, techniques, tactiques d’un attaquant ou groupe d’attaquants est abrégé TTP (en anglais : Tactics, Techniques, and Procedures).
Il est possible de classer les codes malveillants selon des méthodes différentes. Dans notre langage quotidien, nous nous référons à plusieurs d’entre elles.
Il n’est pas rare d’entendre parler de vers (imageant la propagation d’une machine à une autre), ce qui correspond à l’approche épidémiologique ; de credential stealers (programmes de vol d’identifiants), une approche centrée sur le but de l’attaquant ; ou encore de backdoors (portes dérobées), ce qui correspond à la description technique de la modification apportée au système pour en garder le contrôle.
La meilleure classification
C’est une question complexe et la réponse, à défaut d’être 42, est « celle qui vous convient » ou, pour les plus créatifs, « la vôtre ».
Cependant, les classifications existantes ont bien entendu toutes leur intérêt.
Une approche épidémiologique prend en compte les facteurs favorisant la propagation d’une infection et met en scène l’agent pathogène, l’hôte, le milieu, les vecteurs de transmission, etc.
On peut ainsi réaliser un classement, une taxonomie pour les puristes. On retrouve d’ailleurs dans notre quotidien les appellations virus, vers, vecteurs de propagation, pour parler de la menace informatique.
L’avantage de cette approche, c’est qu’elle permet d’anticiper la façon de contenir la menace en prenant en compte l’éventuelle progression de la menace dans le parc informatique.
Des approches techniques du genre MITRE Att&ck ou la CKC (Cyber Kill Chain) de Lockheed Martin permettent de classer les malwares selon les traces qu’ils laissent sur le système et les combinent avec la phase de l’attaque en cours.
Ces étapes sont les mêmes que celle régissant un pentest : reconnaissance, énumération, exploitation, installation, mouvements latéraux, etc.
La CKC reste à un niveau macro, la matrice Att&ck zoomant jusqu’à l’artefact système.
Les malwares sont donc classés, par exemple, selon l’utilisation de program startup, des liens symboliques ou encore de l’accès au clipboard (presse-papiers en français, ce dernier contenant les informations lors des copier-coller).
Note sur la matrice MITRE Att&ck : cette dernière est très orientée système...
La détection par base de connaissance
Spoiler : cela ne fonctionne pas.
Une des approches les plus simples est de maintenir une liste contenant tous les codes malveillants passés, présents et futurs.
Grâce à une fonction dite de somme de contrôle (ou hash en anglais), il est possible de vérifier l’intégrité d’un fichier. Une telle fonction, par exemple SHA256, respecte un cahier des charges strict en trois points :
-
Deux fichiers différents ne peuvent pas avoir la même somme de contrôle.
-
Un fichier ne peut avoir qu’une unique somme de contrôle.
-
On ne peut pas, à partir de la somme de contrôle, retrouver le contenu du fichier d’origine.
Ce qui est vérifiable empiriquement avec le test ci-dessous :
$ cat fichier1
Le contenu du fichier 1.
# sans majuscule au debut
$ cat fichier1.bis
le contenu du fichier 1.
# avec aussi une majuscule à Fichier ; après tout, pourquoi pas
$cat fichier1.ter
Le contenu du Fichier 1.
# autre chose rien à voir
$ cat fichier2
Le contenu du fichier 2, c'est à peu près la même soupe.
A 2kPi près, dira-t-on.
$ sha256sum fichier*
a464400a766b072fb122c6d950b605442b375a18458b6be23c9b1c89c80f4a27 fichier1
27d978894819ae58cd33d556157766773ab53933c84b161aec5d4486fbb09895 fichier1.bis
6afbbdaacb44d8f6d4eb7cee343d00791498d820e9b6273cb370921046becdaa fichier1.ter
a07b45f1ed19abf55c09206d2cf640b69f044700c830732e3ecc3da8bfc01bef fichier2
On constate que chaque somme de contrôle est complètement différente. La moindre modification du contenu, même l’ajout d’un espace, provoque un changement radical dans le résultat.
La première approche, vous l’aurez compris...
Correspondances partielles
La faiblesse principale des fonctions de hachage dans leur utilisation en base de connaissance réside dans le fait qu’une modification du fichier modifie tout le hash. C’est parfait pour en garantir l’intégrité, mais ce n’est pas pratique pour ce qu’on voudrait faire : savoir si deux fichiers sont partiellement ou totalement similaires.
De tels algorithmes existent et sont notamment utilisés dans les correcteurs orthographiques pour applications de messagerie ou autres traitements de texte, le plus connu étant la distance de Levenshtein : https://en.wikipedia.org/wiki/Levenshtein_distance#Computing_Levenshtein_distance
Les regex (Regular Expressions) peuvent également permettre d’identifier une correspondance partielle.
De tels algorithmes peuvent s’appliquer au calcul des hashs, on parle alors de fuzzy-hashing.
L’algorithme le plus répandu consiste à calculer les context-triggered piecewise hashes, son implémentation la plus connue est SSDeep : https://ssdeep-project.github.io/ssdeep/index.html
Explication complète, présentation et "Paper" en anglais sont disponibles ici : https://dfrws.org/presentation/identifying-almost-identical-files-using-context-triggered-piecewise-hashing/
En simple, ou si vous n’avez pas lu le "Paper", SSDeep calcule des hashs sur des "tranches" du fichier ; vous allez voir, c’est très parlant.
L’affichage du résultat est sous forme blocksize:hash:hash,filename.
Comparons nos fichiers basic_malware_mutated.sh et basic_malware.sh écrits dans la section précédente et constatons que des parties du hash sont similaires.
$ssdeep basic_malware_mutated.sh basic_malware.sh
...
Structure d’un PE et imphash
Pour introduire le prochain concept nommé imphash, il faut étudier le format des binaires exécutables Windows.
Nul besoin d’entrer dans les détails, la version simplifiée du schéma ci-dessous devrait suffire :
Vous trouverez la documentation complète ici : https://docs.microsoft.com/en-us/windows/win32/debug/pe-format?redirectedfrom=MSDN
Un binaire PE commence par l’en-tête PE, à savoir les lettres "MZ". PE (Portable Executable) est le format propriétaire Windows pour les fichiers exécutables, bibliothèques de développement et autres objets exécutables liés. On le reconnaît facilement via l’extension de fichier .exe ou .dll ou par les deux premiers octets du fichier brut "MZ".
Le binaire est découpé en sections où sont rangés les différents contenus. La section .text est la section principale, la section .data contient les variables, .rdata contient les contenus annexes (en readonly), et la section .rsrc les ressources additionnelles (par exemple, les images).
Si vous avez déjà fait du C, du PHP, du Java ou du Python, vous savez certainement que la première chose à faire dans votre code est d’importer les librairies nécessaires au programme, ce afin que le compilateur prépare les dépendances entre votre code et les librairies de votre ordinateur.
Un binaire doit déclarer les fonctions qu’il compte utiliser.
Ces imports sont stockés dans une table d’imports (généralement dans .rdata), un pointeur du data directory y conduit.
La table d’imports permet d’avoir une idée générale du comportement d’un exécutable. Dans notre cas :
"CloseHandle","CopyFileA","CopyFileW","CreateDirectoryW"...
Entropie et packing
Puisqu’il est difficile d’échapper aux méthodes combinées de impfuzzy + fuzzy hash, un attaquant désirant ne pas être détecté devra modifier le binaire dans son intégralité pour atteindre son but.
Une approche est de réaliser un second binaire appelé wrapper. Ce dernier n’a qu’une mission : servir de caisse de transport opaque pour notre binaire. Un binaire dans un binaire ? Binaire-ception ! On peut traduire les verbes « to pack » et « to wrap » par « emballer », on vient recouvrir le binaire de notre « papier cadeau spécial » : un autre binaire.
À ce stade, il nous faut parler d’entropie, un concept mathématique et physique.
Pour la formule qui nous intéresse en informatique, l’entropie de Shannon, il s’agit d’un score entre 0 et 8.
En simplifiant, le score représente le nombre de bits qu’il faut pour pouvoir écrire toutes les valeurs contenues dans un fichier.
Par exemple, la phrase « Portez ce vieux whisky au juge blond qui fume » utilise toutes les lettres de l’alphabet ainsi que le caractère espace.
Il faut donc 27 caractères pour écrire ce message. Si on leur donne à chacun un numéro entre 0 et 26, on peut écrire le message au format binaire.
On n’a pas besoin des 8 bits pour coder les valeurs de 0 à 27, seuls 5 sont nécessaires :
0000 0000 = 0
0001 1011 = 27
Si on utilise 4 bits, on ne peut écrire que 16 valeurs (1111 = 15), 5 bits permettent d’écrire 32 valeurs (11111 = 31).
Le calcul mathématique de l’entropie est log2(27) = 4.754887502163469.
L’entropie ici est donc...
Analyses et outillage
Chaque type de fichier demande un outillage spécifique, sauf chez quelques spécimens qui font tout avec hexdump et leurs scripts C ou Python.
Il existe des distributions Linux dédiées telles que REMNux (la distribution orientée reverse engineering, disponible sur https://remnux.org/) ou Tsurugi (la distribution orientée DFIR - Digital Forensic and Incident Response, https://tsurugi-linux.org/).
Mais une bonne vieille Debian fonctionne tout aussi bien, si vous prenez le temps de la configurer et d’y ajouter quelques packets.
La plupart sont disponibles dans les dépôts Debian (apt) ou dans les dépôts pip, voici un extrait de notre sélection personnelle :
bless |
Éditeur hexadécimal avec interface graphique |
|
ssdeep |
Fuzzy-hashing de fichiers |
|
EVi |
Visualisation graphique d’entropie |
|
impfuzzy |
Fuzzy-hashing des tables d’import PE |
|
Gdb + peda |
Débugueur + extention utile |
|
pdfid |
Étude des PDF |
|
radare2 |
Étude des shellcodes |
|
oletools |
Étude des documents Office |
|
OfficeMalScanner |
Étude auto des documents Office |
|
peframe |
Étude des PE |
|
pefile |
Étude des PE |
|
yara |
Comme un grep en hexa |
|
vt-cli |
Le client pour VirusTotal |
|
CAPE |
Extraction des fichiers de config et des payloads |
|
Cuckoo |
Sandbox, (nous utilisons VirtualBox) |
|
VirtualBox |
apt-get install virtualbox |
Pour... |
Simulations et profilage
Une fois l’analyse de notre malware terminée, la phase de classification commence. Pour déterminer la malveillance d’un code, il est possible de corréler son activité avec les étapes d’une attaque.
La réalisation d’une attaque, que ce soit dans le cadre d’un audit de sécurité ou d’une attaque réelle, se fait par étapes. Par exemple :
-
Reconnaissance
-
Énumération
-
Exploitation
-
Post-exploitation
-
Élévation de Privilège
-
Persistance
-
C2 : Control and Command (Canal de communication pour piloter les machines infectées)
-
Action sur objectif
Ces étapes peuvent varier légèrement selon les modèles, mais l’idée reste généralement la même. Il existe donc des points communs entre le PTES, l’OSSTMM, la CKC et la MITRE Att&ck.
La CKC est plutôt générique et s’applique très bien de façon agnostique et sur tout type de menace : Mac, Linux, Windows, réseau, OT, IoT, mobile…
https://www.lockheedmartin.com/en-us/capabilities/cyber/cyber-kill-chain.html
Le niveau de détail peut cependant varier. Ainsi, pour la matrice MITRE Att&ck, le nombre d’étapes augmente et chacune est détaillée.
Cependant, cette spécialisation se fait à un prix, car pour être exhaustif sur les techniques d’attaques il faut choisir une technologie sur laquelle elles s’appliquent. Ainsi, MITRE Att&ck choisit sans surprise Windows, perdant au passage la possibilité de modéliser avec précision les attaques réseau (ARP, IP spoofing, buffer overflow, memory leaks…).
Pour la persistance sous Windows, il existe des variantes pour arriver à des résultats "similaires" : clés...