Démarrage BIOS : la traque à grande échelle des codes malveillants

Conçu dans les années 80, le BIOS ne cesse d’être exploité par les malwares. En effet, malgré un champ des menaces en rapide et perpétuelle évolution, le processus classique de démarrage BIOS représente encore un vecteur d’attaque majeur dans les entreprises du monde entier. Baptisés bootkits, les malwares qui visent les processus de démarrage s’exécutent avant le lancement du système d’exploitation. Du coup, ce type de compromission persiste souvent même après une supposée remédiation de l’incident.

Cet article revient sur les problématiques rencontrées par FireEye lors de l’analyse à grande échelle de secteurs de démarrage. Il présente ensuite la solution que nous avons adoptée pour démasquer les secteurs infectés.

Problématiques rencontrées

Dans les vastes réseaux d’entreprise, les spécialistes de la sécurité et de la réponse à incident se heurtent à deux problèmes.

Premièrement, le manque de fiabilité des enregistrements des secteurs de démarrage – MBR (Master Boot Record) et VBR (Volume Boot Record) – collectés sur les systèmes ciblés. Les bootkits sont tristement célèbres pour leur capacité à modifier les appels d’API légitimes pour dissimuler les octets qu’ils ont écrasés dans le code de démarrage. Cette capacité leur permet d’intercepter des lectures et renvoyer du code légitime en apparence, ce qui compromet la fiabilité des collectes d’octets effectuées via lecture du disque depuis l’espace utilisateur.

Bootkit : forme de malware avancé qui s’exécute au tout début du processus de démarrage d’un système pour établir sa persistance et compliquer son identification. Pour en savoir plus sur le processus de démarrage et découvrir un exemple de bootkit VBR, lisez notre article consacré à ROCKBOOT.

Deuxièmement, l’impossibilité pratique d’effectuer une rétroingénierie de tous les secteurs de démarrage. Pour confirmer l’infection d’un système par un bootkit, un analyste peut opérer en deux étapes : d’abord, il effectue une image du disque, puis il procède à la rétroingénierie des octets de démarrage pour déterminer la présence ou non d’un élément malveillant dans la chaîne de démarrage. Toutefois, ce processus prend du temps. Et même une armée d’analystes chevronnés ne pourrait l’exploiter efficacement compte tenu de la taille des réseaux actuels. Prenons le cas du réseau mentionné dans notre article consacré à ROCKBOOT. Ce dernier comprenait près de 10 000 hôtes. Dans l’hypothèse où chaque hôte contient au moins deux secteurs de démarrage (un MBR et un VBR), cela représente une moyenne de 20 000 enregistrements à analyser ! À première vue, on pourrait se dire qu’il suffit d’effectuer un hachage des secteurs de démarrage afin de n’analyser que les enregistrements uniques. Une telle démarche suppose que les réseaux d’entreprise exécutent quasiment tous le même code de démarrage, ce qui, bien sûr n’est pas le cas. En reprenant l’exemple du réseau compromis par ROCKBOOT, un hachage MD5 des 20 000 secteurs de démarrage réduit le nombre des enregistrements uniques à tout juste 6 000. Le tableau 1 illustre ce scénario avec les données que nous avons collectées au fil de nos missions au sein d’entreprises de tailles variées.

Taille de l’entreprise (nb d’hôtes)

Nb moyen d’enregistrements uniques (MD5)

100 – 1 000

428

1 000 – 10 000

4 738

Plus de 10 000

8 717

Tableau 1 – Enregistrements uniques de secteurs de démarrage après hachage MD5

On pourrait ensuite se dire qu’au lieu de "hacher" tout un secteur, il suffit de ne traiter que des sous-sections du code de démarrage, pour ainsi éviter les portions de données dynamiques. C’est ce que nous avons fait. Par exemple, pour calculer un hash dans le cas d’un secteur MBR, nous avons utilisé les octets des deux offsets suivants :

md5( offset[0:218] + offset[224:440] )

Pour un réseau donné, nous avons ainsi identifié quelque 90 hash MBR uniques parmi environ 185 000 systèmes. Toutefois, cette technique présentait des inconvénients, à commencer par le fait qu’elle exigeait de prendre en compte plusieurs cas spécifiques pour des applications comme Altiris, SafeBoot et PGPGuard. Résultat : il fallait ajuster légèrement l’algorithme pour chaque environnement, ce qui nécessitait la rétroingénierie de nombreux secteurs pour trouver les offsets à "hacher".

Au final, nous en avons déduit qu’il nous fallait une solution capable de fournir les trois éléments suivants :

  • Un ensemble fiable d’enregistrements de secteurs de démarrage des systèmes
  • Une analyse comportementale des secteurs de démarrage (au lieu d’une simple analyse statique)
  • Des analyses rapides de dizaines de milliers d’enregistrements

La suite de cet article explique comment nous nous y sommes pris pour relever chacun de ces défis.

Collecte des octets

Les pilotes malveillants s’immiscent dans la pile de pilotes d’un disque pour intercepter incognito les E/S disque qui traversent l’environnement. Pour contrer ce vecteur d’attaque, nous avons développé un pilote du noyau ("Raw Read") capable de cibler différents niveaux de la pile de pilotes. Ce pilote nous permet d’identifier le niveau le plus bas de la pile et de lire les octets qui s’y trouvent (cf. Figure 1).


Figure 1 : Le pilote malveillant agit comme un filtre dans l’environnement, tandis que le pilote Raw Read lit les octets du niveau le plus bas.

Nous pouvons ainsi contourner le reste de la pile et tout hook d’espace utilisateur. (Attention : un attaquant pourra intercepter les requêtes en lecture si le pilote du niveau le plus bas dans l’environnement E/S comporte un hook de code inline). Raw Read nous permet également de comparer les octets du plus bas niveau de l’environnement à ceux de l’espace utilisateur. Ce sont les différences entre ces deux groupes d’octets qui constituent notre premier indicateur de compromission d’un système de démarrage.

Analyse des octets

Comme nous l’avons déjà souligné, la rétroingénierie et l’analyse statique conviennent mal au traitement de centaines de milliers de secteurs de démarrage. L’analyse dynamique automatique constitue une approche beaucoup plus pratique. Cet avantage se révèle notamment lorsque l’on émule l’exécution d’un secteur de démarrage ou, pour utiliser des termes plus techniques, les instructions en mode réel d’un tel secteur.

Pour cela, nous exploitons le moteur Unicorn. Basé sur l’émulateur QEMU, Unicorn prend en charge l’émulation 16 bits en mode réel. Les échantillons de démarrage sont collectés au fur et à mesure depuis les terminaux et transmis au moteur d’émulation pour une capture des fonctionnalités de haut niveau (accès à la mémoire, lectures et écritures sur disque, interruptions qui s’exécutent pendant l’émulation, etc.)

Hash d’exécution

L’empilage (ou encore la compilation) des échantillons dupliqués est essentiel pour réduire la durée des analyses de suivi effectuées par l’humain. Toutefois, si les échantillons de démarrage collectés à grande échelle sont souvent identiques d’un point de vue fonctionnel, les données qu’ils utilisent (chaînes, offsets, etc.) sont, quant à elles, très différentes. D’où la difficulté de générer un hash pour identifier les doublons, comme démontré au tableau 1. Comment l’émulation peut-elle donc résoudre ce problème ? Réponse : grâce au "hash d’exécution". Le principe est simple : pendant l’émulation, il suffit d’appliquer une fonction de hachage au mnémonique de chaque instruction en langage d’assemblage qui s’exécute (par ex., "md5(‘and’ + ‘mov’ + ‘shl’ + ‘or’)"). La figure 2 illustre ce concept.


Figure 2 : Hash d’exécution

Cette méthode permet de réduire les 650 000 échantillons de démarrage uniques collectés à ce jour à un peu plus de 300 hash d’exécution uniques. Cet ensemble réduit de données facilite grandement l’identification des échantillons à analyser. Un hash d’exécution présent uniquement sur quelques systèmes d’une entreprise constitue donc notre deuxième indicateur de compromission d’un système de démarrage.

Analyse comportementale

Comme tous les malwares, les activités suspectes des bootkits varient énormément d’un environnement à un autre. Plutôt que de créer des signatures de détection pour chaque échantillon de malware, nous nous concentrons sur l’identification des comportements anormaux lors du lancement des OS. Pour cela, nous alimentons un moteur d’analytique avec les séries d’instructions exécutées pendant l’émulation. Prenons le cas d’une fonctionnalité malveillante présente dans plusieurs bootkits que nous avons découverts en analysant les résultats d’émulations.'

Plusieurs de ces bootkits avaient été conçus pour modifier le fonctionnement de la table des vecteurs d’interruption (IVT) et de la zone de données du BIOS (BDA) afin d’intercepter les interruptions et les données du système pendant le processus de démarrage. Cela permettait à un attaquant d’intercepter les lectures du disque et d’altérer la taille de mémoire maximale indiquée par le système. Quant aux bootkits, ils pouvaient se dissimuler sur le disque, voire la mémoire.

Notre solution a été d’analyser les écritures sur les plages mémoire réservées à l’IVT (0000:0000h à 0000:03FCh) et à la BDA (à partir de 0040:0000h) pendant le processus de démarrage. Nous avons ainsi pu détecter 1) les malwares qui "personnalisaient" la routine d’interruption 13h pour inspecter et modifier les écritures du disque effectuées pendant le démarrage et 2) les bootkits qui modifiaient la taille de la mémoire indiquée par la zone de données du BIOS pour dissimuler leur présence.

Hooking de tables IVT, décodage et exécution de données du disque, sorties d’écran suspectes du code de démarrage, modification de fichiers ou de données sur le disque… tous ces comportements constituent notre troisième et dernier indicateur de compromission d’un système de démarrage.

Intervention à grande échelle

Certes, l’analyse dynamique améliore considérablement la visibilité sur les comportements des secteurs de démarrage. Mais cette méthode a également un coût. Par ailleurs, elle se révèle extrêmement lente comparé au hachage et aux analyses statiques. Dans notre environnement d’analyse cloud, l’émulation d’un seul secteur prend en moyenne 4,83 secondes. Si nous reprenons le cas des quelques 20 000 secteurs de démarrage du réseau compromis par ROCKBOOT, une analyse dynamique (émulation) des enregistrements en série prendrait alors plus de 26 heures ! Pour accélérer les résultats, nous devions donc être en mesure d’adapter facilement nos débits d’analyse aux volumes de données entrants issus des terminaux. Mais un autre facteur vient compliquer la donne : l’analyse des secteurs de démarrage tend à s’effectuer par lots. Par exemple, lorsque nous déployons nos technologies pour la première fois sur les terminaux d’un nouveau client.

L’arrivée du cloud computing sans serveur nous a permis de créer un service d’analyse des émulations scalable et économique. Comparées aux instances cloud traditionnelles, les technologies sans serveur ont l’avantage de n’induire aucun coût pendant les périodes d’inactivité – à l’exception des coûts de stockage. Même lorsque notre solution cloud reçoit des dizaines de milliers d’enregistrements d’un nouveau client, elle s’adapte rapidement à la demande et parvient ainsi à maintenir une détection quasi-temps réel des octets malveillants.

Notre application repose sur l’infrastructure cloud d’Amazon Web Services (AWS). Une vue d’ensemble de l’architecture est fournie dans la figure 3.


Figure 3 : Workflow d’analyse de secteurs de démarrage

Composants exploités :

  • API Gateway pour fournir une interface RESTful
  • Fonctions Lambda pour la validation, l’émulation, l’analyse, ainsi que le stockage et la récupération des résultats
  • DynamoDB pour le suivi des secteurs de démarrage traités à travers le système
  • S3 pour le stockage des secteurs de démarrage et des rapports d’émulation

L’architecture que nous avons créée expose une API RESTful qui fournit une poignée de terminaux. Globalement, le workflow suit les étapes suivantes :

  1. Les agents des terminaux des réseaux clients collectent automatiquement les enregistrements des secteurs de démarrage à l’aide du pilote Raw Read de FireEye (voir la section « Collecte des octets » ci-dessus). Ils renvoient ensuite ces enregistrements au serveur de réponse à incident (IR) de FireEye.
  2. Le serveur IR soumet des lots d’enregistrements à l’interface REST hébergée sur AWS et l’interroge pour obtenir des résultats par lots.
  3. Le serveur IR fournit une interface utilisateur pour permettre aux analystes de consulter les résultats agrégés de toute l’entreprise et les notifications automatiques en cas de détection d’un secteur malveillant.

L’architecture expose les terminaux de l’API REST via AWS API Gateway, qui transmet ensuite les requêtes entrantes à une fonction Lambda de "soumission". Cette fonction valide les données entrantes, stocke l’enregistrement (à savoir le code de démarrage) sur S3, puis déploie les requêtes entrantes à travers les fonctions Lambda "d’analyse".

C’est à ce moment que l’émulation des secteurs de démarrage se produit. Étant donné que les fonctions Lambda sont lancées à la demande, ce modèle affiche de très hauts niveaux de parallélisme. AWS intègre également divers paramètres qui permettent de contrôler le nombre maximum d’opérations simultanées pour une fonction Lambda, les allocations mémoire/CPU, etc. Une fois l’analyse terminée, un rapport sur le secteur de démarrage est généré puis stocké sur S3. Ce rapport comprend les résultats de l’émulation et d’autres métadonnées extraites du secteur en question (ex. des chaînes ASCII).

Comme mentionné plus haut, le serveur IR interroge régulièrement le terminal REST AWS jusqu’à la fin du traitement. Le rapport est téléchargé à l’issu de ce traitement.

Le cas du Big Data

Notre workflow d’identification des secteurs de démarrage infectés n’est efficace que si nous savons quels indicateurs de compromission rechercher ou quels hash d’exécution blacklister. Pour détecter un code malveillant (avec hash unique) qui aurait échappé à nos signatures existantes,

nous exploitons notre moteur interne de plateforme de Big Data que nous avons intégré à FireEye Helix suite à l’acquisition de X15 Software. Le chargement des résultats de centaines de milliers d’émulations dans le moteur X15 permet à nos analystes de traquer les comportements anormaux à grande échelle (impressions d’écran uniques, offsets initiaux inhabituels, patterns de lecture ou écriture du disque, etc.).

Cette analyse grandeur nature nous aide à identifier de nouveaux échantillons pertinents pour la rétroingénierie et, au final, à créer de nouvelles signatures de détection à intégrer à notre moteur d’analytique.

Conclusion

Quelques semaines seulement après le déploiement de notre solution, nous avions déjà détecté plusieurs systèmes compromis dans plusieurs environnements de nos clients. De ROCKBOOT à HDRoot!, en passant par JackTheRipper (un bootkit qui se propage via les disquettes, sans blague !), rien n’a échappé à nos radars. À ce jour, notre système a collecté et traité près de 650 000 enregistrements uniques. Et il continue de trouver des aiguilles empoisonnées (secteurs de démarrage suspects ou infectés) dans de très grosses bottes de foin.

Bref, en associant des fonctionnalités d’extraction avancée des secteurs de démarrage des terminaux à une technologie sans serveur scalable et à un moteur d’émulation automatique, il est possible de dénicher rapidement du code malveillant parmi des milliers d’enregistrements. Aujourd’hui, FireEye intègre cette solution à son offre Managed Defense et ses services de Réponse à incident.

Remerciements

Merci à Dimiter Andonov, Jamin Becker, Fred House et Seth Summersett pour leur contribution à cet article.