Analyse statique des exécutables Windows, un aperçu

Magazine
Marque
MISC
Numéro
120
Mois de parution
mars 2022
Spécialité(s)


Résumé

Il existe deux méthodes principales pour trier rapidement des exécutables suspects : les lancer dans une sandbox (analyse dynamique) et l'analyse... statique. Nous proposons un article en deux parties pour décrire cette dernière appliquée aux fichiers Windows Portable Executable (PE). Dans ce numéro, nous verrons comment utiliser certaines propriétés de ce format, déceler des anomalies, dont certaines peuvent révéler le caractère malveillant du fichier. Dans un prochain numéro, nous étudierons en détail le format PE lui-même, comment le loader Windows l'utilise, et comment certains malwares le contournent pour mieux passer sous le radar.


Body

1. Simple et souvent efficace

1.1 Identifier et rechercher

Un premier réflexe est de calculer les valeurs de hachés cryptographiques usuelles (MD5, SHA1 et SHA256), ce qui permet d'identifier de manière unique le fichier par son contenu, puis de chercher dans une base de Threat Intelligence interne ou une base publique, comme [hashlookup] ou [virustotal]. Nous savons ainsi si notre fichier est déjà connu et éventuellement analysé, s'il correspond à un fichier légitime ou à une menace, et le cas échéant le contexte de cette dernière : marqueurs associés pour pivoter ou les rechercher, attaquants, modes opératoires et secteurs économiques visés. Même si seul l'algorithme SHA256 est encore considéré comme fiable, les autres valeurs de hachés sont encore largement utilisées.

De plus, connaître le haché d'un fichier est parfois équivalent à en connaître le contenu, car de nombreuses bases permettent de les télécharger en connaissant simplement cette valeur [bazaar]. Enfin, révéler une valeur de haché publiquement (comme une simple recherche sur Virus Total) peut aussi faire comprendre à l'adversaire que vous avez identifié son vecteur d'attaque, il est donc préférable d'effectuer d'abord ses recherches en interne.

L'attaquant peut aussi s'arranger pour modifier très légèrement le fichier à chaque campagne d'attaque, afin d'obtenir un haché totalement différent et brouiller les pistes. L'algorithme de haché [ssdeep] est justement conçu pour être tolérant à des différences locales : deux fichiers similaires auront des valeurs ssdeep proches.

1.2 Des entrailles visibles ?

Autre méthode simple, on peut extraire les chaînes de caractères ASCII ou UNICODE, c'est-à-dire une suite d'un moins 4 caractères « imprimables ». Voici ci-dessous les premières et les dernières chaînes extraites d'un exemple, grâce à l'outil de Sysinternals. L'équivalent existe sous GNU/Linux.

Strings v2.41
Copyright (C) 1999-2009 Mark Russinovich
Sysinternals - www.sysinternals.com
 
!This program cannot be run in DOS mode.
Rich
1d6
UPX0
UPX1
UPX2
3.09
UPX!
...
KERNEL32.DLL
LoadLibraryA
GetProcAddress
VirtualProtect
VirtualAlloc
VirtualFree
ExitProcess

Tout d'abord, il faut noter que peu de chaînes sont visibles, ce qui est inhabituel. On note aussi les 4 valeurs UPX au début et les 7 dernières lignes. KERNEL32.DLL est une librairie dynamique, et les 6 lignes suivantes sont les fonctions appartenant à cette DLL (Dynamically Linked Library). Ces 6 noms suggèrent : le chargement de bibliothèque, l'obtention de l'adresse d'un processus et des fonctions de gestion de la mémoire dynamique (à l'exécution), la terminaison d'un processus. La documentation de l'API Windows donne plus de détails si besoin, et nous y reviendrons. [malAPI] décrit l'usage par les malwares de l'API Windows pour l'évasion ou l'espionnage, par exemple.

Mais l'essentiel est que, dans cet exemple, rien qu'en cherchant les chaînes imprimables, on peut formuler 2 hypothèses : 1 - la rareté des chaînes indique que le fichier est en majorité chiffré ou compressé, ou alors les chaînes sont encodées, et 2 - cet exécutable charge lui-même des DLL et alloue, libère et modifie les propriétés de zones mémoires.

Avec de l'entraînement, on peut reconnaître des messages d'erreurs de la bibliothèque standard C++ ou C, par exemple. Avec un peu de chance (ou un malware bas de gamme), on peut reconnaître une adresse IP, une URL ou les commandes d'un RAT.

En cherchant UPX sur Internet, on trouve l'outil [UPX], un compresseur d'exécutable, ce qui valide plusieurs de nos hypothèses : la compression, le chargement dynamique de DLL et les gestions dynamiques de zones mémoires.

1.3 Recherche de signatures

Une approche moins naïve est de rechercher des motifs, par exemple des signatures [yara] ou avec l'outil [binwalk], qui permet également de calculer les variations d'entropie sur un fichier.

Les 3 méthodes que nous avons vues jusqu'ici fonctionnent d'ailleurs avec n'importe quel type de fichier, y compris les exécutables sous d'autres systèmes.

Mais revenons à la définition d'un malware : il s'agit d’un logiciel comportant des capacités malveillantes (malicious software) et doit donc être exécuté par la machine. Nous n'avons pas utilisé cette caractéristique jusqu'ici.

2. Exécutable ?

Nous verrons dans une seconde partie en détail la structure Portable Executable (PE), aujourd'hui nous retiendrons simplement qu'un exécutable est découpé en plusieurs sections, chacune d'entre elles ayant un rôle et des propriétés (caractéristiques) particulières liées à son usage. Ces différentes sections vont être chargées en mémoire par le loader Windows : la structure PE a été spécialement conçue pour mettre en œuvre cette correspondance (mapping) d'un fichier vers un processus prêt à fonctionner, d'où le nom « d'exécutable ».

Par exemple, la section contenant le code machine est souvent nommée .text, et en mémoire, cette portion possède la propriété « exécutable », mais non modifiable (absence de la caractéristique write). Le point d'entrée, c'est-à-dire l'adresse mémoire de l'instruction exécutée en premier se situe en principe dans cette section. Autres exemples de sections, celle nommée souvent .rdata contient les constantes (seulement read) et .data des variables (aussi write). Les sections de données ne sont pas en principe « exécutable ».

Les noms de sections sont indicatifs et ne sont pas utilisés lors du chargement du fichier PE par le loader Windows, par contre les caractéristiques en mémoire execute, read, write, etc. le sont. Adam « hexacorn » collectionne d'ailleurs les noms de sections exotiques [pe_section_names] et leurs origines.

2.1 Des sections classiques

Avant notre premier exemple, voici comment lire la table ci-dessous : pour chaque section, la colonne vsize est la taille en mémoire (processus) et psize est la taille dans le fichier PE. V pour Virtuelle et P pour Physique. De même, pour les colonnes vaddr et paddr qui sont les adresses. Les adresses mémoires (virtuelles) sont relatives à l'adresse de base du process (ImageBase), souvent 0x400000 pour un PE avec du code x86.

Ci-dessous, il s'agit d'un exécutable très classique : la section .text est la seule avec la caractéristique mem_execute, sans propriété write et le point d'entrée s'y trouve (0x1000 <= 0x4342 <= 0x9000). De même pour les 2 autres sections : les caractéristiques sont cohérentes avec les noms des sections connus.

ImageBase 0x400000, AddressOfEntryPoint 0x4342
sections at 0x1c8
    name       vsize    vaddr    psize    paddr charact
00 .text    00007c38 00001000 00008000 00001000 MEM_READ MEM_EXECUTE CNT_CODE
01 .rdata   00000d08 00009000 00001000 00009000 MEM_READ CNT_INITIALIZED_DATA
02 .data    000029b8 0000a000 00002000 0000a000 MEM_WRITE MEM_READ CNT_INITIALIZED_DATA

2.2 Plus exotiques

Revenons ci-dessous sur l'exemple du début, celui duquel nous avons extrait les chaînes de caractères. Nous avons ajouté à notre table des sections une colonne supplémentaire (entr), la valeur d'entropie.

ImageBase 0x400000, AddressOfEntryPoint 0x5ce0
sections at 0x1c8
   name     vsize    vaddr    psize    paddr  entr charact                              
00 UPX0  00008000 00001000 00000000 00000400 0.000 MEM_WRITE MEM_READ MEM_EXEC CNT_UNINIT_ DATA
01 UPX1  00006000 00009000 00005c00 00000400 7.863 MEM_WRITE MEM_READ MEM_EXEC CNT_INIT_DATA
02 UPX2  00001000 0000f000 00000200 00006000 1.659 MEM_WRITE MEM_READ CNT_INITIALIZED_DATA

Tiens, on retrouve nos chaînes commençant par UPX, il s'agit donc des noms des sections. On note 2 anomalies : deux sections execute dont la première (UPX0) est vide sur le disque (psize = 0), mais à 0x8000 en mémoire (colonne vsize) ! Le point d'entrée est dans la section UPX1. On note également la très forte entropie de UPX1 à 7,8 qui est très proche de 8, le maximum.

Rappelons-nous la fonction d'UPX : il compresse des fichiers PE (et ELF sous GNU/Linux, d'ailleurs) qui sont décompressés « à la volée » lors de l'exécution, vers leur forme originale. On a donc un fichier PE original et un fichier PE « compressé », qui dans notre exemple possède son point d'entrée à l'adresse virtuelle 0x5ce0, dans la section UPX1 à forte entropie. Cette dernière contient donc sans doute la section .text / exécutable originale sous forme compressée et le code de décompression. Quelle était la taille originale sur disque de la section .text d'origine ? Il s'agit de psize = 0x8000, valeur que nous retrouvons comme taille virtuelle de UPX0 (dans le processus) : vsize = 0x8000. Le code dans UPX1 décompresse donc le code original .text vers la section UPX0, d'où l'usage des fonctions VirtualAlloc et VirtualProtect, puis « saute » vers le point d'entrée original (à 0x4342), ce qui explique aussi pourquoi UPX0 est write et execute à la fois !

Vous l'avez deviné : le fichier compressé avec les sections nommées UPX... est la version compressée et fonctionnelle du fichier PE juste au-dessus.

À retenir : une section mémoire à la fois write et execute est typique des packers et du parasitage de processus, c'est-à-dire lorsqu'un malware injecte du code malveillant dans un processus légitime, comme dans celui d'un navigateur pour espionner les mots de passe.

Voyons un dernier exemple de sections ci-dessous :

ImageBase 0x400000, AddressOfEntryPoint 0x223aa
    name       vsize    vaddr    psize    paddr              
00 .text    00023cc1 00001000 00023e00 00000400 MEM_READ MEM_EXECUTE CNT_CODE
01 .rdata   00005456 00025000 00005600 00024200 MEM_READ CNT_INITIALIZED_DATA
02 .data    00004efc 0002b000 00000c00 00029800 MEM_WRITE MEM_READ CNT_INITIALIZED_DATA
03 .sxdata  00000004 00030000 00000200 0002a400 MEM_WRITE MEM_READ CNT_INITIALIZED_DATA
04 .rsrc    00001f08 00031000 00002000 0002a600 MEM_READ CNT_INITIALIZED_DATA
overlay: offset 0x2c600, size 0x5500, entr 7.990, md5:e8bd29878de5a9ebcf950a685c337636, b"7z\xbc"

Cet exécutable comporte une section nommée .sxdata, et également des données après la fin de la dernière section, « après » la fin de la structure PE officielle, à l'offset 0x2c600 (paddr=0x2a600 + psize=0x2000). Cette partie que l'on appelle overlay possède une forte entropie de 7,99 et commence par les 2 octets 7z. Enfin, en extrayant les chaînes de caractères, on trouve 7z.sfx et 7z.sfx.exe : fin du suspense, il s'agit donc d'un exécutable 7-zip auto-extractible (self extractible, SFX). La structure PE est cependant différente de celle d'UPX : les données compressées sont dans l'overlay et non dans une section, comme UPX1.

2.3 Automatiser la recherche de caractéristiques de fichiers PE

Même si les propriétés d'un PE 7-zip SFX que nous venons d'identifier ne sont pas forcément signes d'un comportement malveillant, c'est une belle occasion pour écrire une signature Yara (fichier 7z_sfx.yara) :

import "pe"
import "math"
 
rule _7z_SFX {
   meta:
      description = "Detects 7z SFX"
      date = "2021-12-26"
   condition:
      pe.overlay.size > 0 and pe.sections[3].name == ".sxdata"
      and math.entropy( pe.overlay.offset, pe.overlay.size ) >= 7.5
      and pe.version_info["CompanyName"] == "Igor Pavlov"
      and pe.version_info["FileDescription"] == "7z SFX"
      and pe.imports("KERNEL32.dll", "SetFileAttributesW")    
}

Grâce aux modules pe et math, la signature ci-dessus détecte : la présence de l'overlay et son entropie >=7,5, la section nommée .sxdata, les valeurs des champs CompanyName et FileDescription (situées dans la section .rsrc) et l'import de la fonction SetFileAttributesW de la librairie partagée KERNEL32.dll.

Elle s'utilise ainsi :

>yara64.exe 7z_sfx.yara c:\bin\md5sumsfx.exe
_7z_SFX c:\bin\md5sumsfx.exe

Sans être caractéristiques des malwares, les packers offrent deux propriétés importantes aux attaquants pour protéger leurs exécutables : ils cachent une partie des données internes (code et données) avec la compression, ce qui force le plus souvent à une analyse dynamique, et ainsi retarde le diagnostic. Une autre transformation similaire est le chiffrement, détectable également avec une entropie importante, mais l'analyste doit alors déduire la clé et l'algorithme utilisé. Certaines signatures Yara comme [findcrypt-yara] détectent les constantes utilisées par la plupart des algorithmes cryptographiques. La présence d'un overlay est plutôt inhabituelle.

Il est important de vérifier aussi si le fichier PE est signé numériquement, par quelle organisation, et si le certificat utilisé est valide, de confiance ou non.

2.4 Tout en un ?

Existe-t-il un outil avec une bibliothèque de signatures des packers et autres PE exotiques ? Oui, l'outil de référence est Detect It Easy [DIE] et possède des centaines de signatures, malheureusement dans son propre langage.

Voici les résultats sur un script AutoIt compilé (langage pour les malwares ou les équipes IT), par la version texte de DIE :

>diec C:\Users\laurent\Downloads\autoit-v3\install\helloworld.exe
filetype: PE64
arch: AMD64
mode: 64-bit
endianess: LE
type: GUI
  library: AutoIt(3.XX)[-]
  compiler: Microsoft Visual C/C++(2013)[-]
  linker: Microsoft Linker(12.0*)[GUI64]

Et notre 7-zip SFX :

>diec c:\bin\md5sumsfx.exe
filetype: PE32
arch: I386
mode: 32-bit
endianess: LE
type: GUI
  sfx: 7-Zip(9.36 beta)[-]
  compiler: Microsoft Visual C/C++(2010 SP1)[msvcrt]
  archive: 7-Zip(0.4)[-]
  linker: Microsoft Linker(6.0*)[GUI32]

Mais, comment fait cet outil pour connaître également la version du compilateur et linker utilisés ? Vous souvenez-vous de la chaîne de caractères Rich extraite au début de l'article ? Ces infos y sont cachées et cela sera détaillé dans la deuxième partie. Nous verrons également comment sont indiqués l'architecture et le type (console ou GUI).

Conclusion

Aujourd'hui, nous avons vu comment identifier un fichier grâce à ses valeurs de hachés cryptographiques, comment extraire de l'information des chaînes de caractères ou par leur absence, déceler des anomalies. Enfin, nous avons entrevu l'organisation du format PE en sections et leurs rôles, leurs caractéristiques en mémoire et le principe des packers, et enfin l'importance de la mesure de l'entropie.

Dans cette première partie, nous avons vu quelques approches statiques (sans observation de l'exécution) pour déceler les caractéristiques typiques des malwares Windows. Nous reviendrons en détail dans la 2e partie sur la structure PE et continuerons avec d'autres outils et méthodes.

Remerciements

Merci aux patient(e)s relecteurs(trices) pour leurs suggestions bienveillantes :-)

Références

[hashlookup] CIRCL hash lookup is a public API to lookup hash values against known database of files https://hashlookup.circl.lu

[virustotal] Analyze suspicious files and URLs to detect types of malware, automatically share them with the security community, https://www.virustotal.com

[bazaar] https://bazaar.abuse.ch/browse

[malAPI] https://malapi.io/

[ssdeep] Fuzzy hashing program, https://ssdeep-project.github.io/ssdeep/index.html

[UPX] The Ultimate Packer for eXecutables, https://upx.github.io/

[Yara] https://github.com/InQuest/awesome-yara

[binwalk] https://github.com/ReFirmLabs/binwalk

[pe_section_names] PE Section names – re-visited,
https://www.hexacorn.com/blog/2016/12/15/pe-section-names-re-visited/

[findcrypt-yara] https://github.com/polymorf/findcrypt-yara

[DIE] https://github.com/horsicq/Detect-It-Easy



Article rédigé par

Par le(s) même(s) auteur(s)

Description du système de fichiers ExFAT

Magazine
Marque
MISC
Numéro
118
Mois de parution
novembre 2021
Spécialité(s)
Résumé

Le système de fichiers ExFAT se rencontre dans l’embarqué, par exemple sur les cartes mémoire des appareils photo numériques ou celles des smartphones Android. Nous allons voir dans cet article comment fonctionne ce système de stockage très simple, et les améliorations par rapport à FAT32. Nous irons jusqu’à comprendre comment localiser les métadonnées, lire le contenu d’un fichier et d’un répertoire.

Description du format de stockage forensique Encase/EWF

Magazine
Marque
MISC
Numéro
117
Mois de parution
septembre 2021
Spécialité(s)
Résumé

Dans le cadre de réponses sur incidents, les CERT/CSIRT peuvent être amenés à réaliser des analyses post-mortem à partir des disques ou plus généralement de supports de stockage. Ainsi, il est proposé dans cet article d’énumérer les requis pour collecter, stocker, utiliser efficacement une copie de disque en vue d’une analyse forensique. Nous détaillerons comment le format EWF met en œuvre ses besoins, et en décrirons suffisamment les principes techniques pour savoir accéder à un secteur précis dans l’image du disque.

Les derniers articles Premiums

Les derniers articles Premium

Bénéficiez de statistiques de fréquentations web légères et respectueuses avec Plausible Analytics

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Pour être visible sur le Web, un site est indispensable, cela va de soi. Mais il est impossible d’en évaluer le succès, ni celui de ses améliorations, sans établir de statistiques de fréquentation : combien de visiteurs ? Combien de pages consultées ? Quel temps passé ? Comment savoir si le nouveau design plaît réellement ? Autant de questions auxquelles Plausible se propose de répondre.

Quarkus : applications Java pour conteneurs

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Initié par Red Hat, il y a quelques années le projet Quarkus a pris son envol et en est désormais à sa troisième version majeure. Il propose un cadre d’exécution pour une application de Java radicalement différente, où son exécution ultra optimisée en fait un parfait candidat pour le déploiement sur des conteneurs tels que ceux de Docker ou Podman. Quarkus va même encore plus loin, en permettant de transformer l’application Java en un exécutable natif ! Voici une rapide introduction, par la pratique, à cet incroyable framework, qui nous offrira l’opportunité d’illustrer également sa facilité de prise en main.

Les listes de lecture

11 article(s) - ajoutée le 01/07/2020
Clé de voûte d'une infrastructure Windows, Active Directory est l'une des cibles les plus appréciées des attaquants. Les articles regroupés dans cette liste vous permettront de découvrir l'état de la menace, les attaques et, bien sûr, les contre-mesures.
8 article(s) - ajoutée le 13/10/2020
Découvrez les méthodologies d'analyse de la sécurité des terminaux mobiles au travers d'exemples concrets sur Android et iOS.
10 article(s) - ajoutée le 13/10/2020
Vous retrouverez ici un ensemble d'articles sur les usages contemporains de la cryptographie (whitebox, courbes elliptiques, embarqué, post-quantique), qu'il s'agisse de rechercher des vulnérabilités ou simplement comprendre les fondamentaux du domaine.
Voir les 66 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous