CTIMalware

Analyse du malware Buer Loader écrit en Rust

L’analyse de malwares fait partie du quotidien de l’équipe CTI. Cet article présente l’analyse d’une souche Rust de Buer Loader depuis la réception des échantillons jusqu’à l’écriture d’un script d’extraction de stage2*. Malgré plusieurs mécanismes de protection, il a été possible d’extraire tous les échantillons de manière différentes. TEHTRIS fournit le code permettant une telle extraction.

Introduction

Le langage Rust [1] est de plus en plus utilisé [2] chez les développeurs. En effet, la philosophie de ce langage est de maximiser la sécurité tout en offrant des performances proches du C++ dans une syntaxe agréable. De nombreux projets ambitieux comme Kerla [3] visent à offrir un système d’exploitation compatible avec n’importe quel binaire Linux, sans modification ! Ceci offre à Rust beaucoup de crédibilité et explique son adoption chez les développeurs. D’un point de vue bas niveau, ce langage génère un code binaire plus complexe à signer et analyser par rapport à du C en proposant davantage d’instructions et en ajoutant de l’offuscation à peu de frais, ce qui intéresse les auteurs de malware.

Encore peu nombreux à l’adopter, il n’est pourtant pas étonnant de noter que ce langage commence à se faire une place dans le bestiaire des malwares, comme le montre Buer Loader [3] qui comporte des variants développés dans ce langage.

Si ce malware a déjà été analysé [3], il reste intéressant d’en étudier le « stage1* » pour identifier les mécanismes de protection et les évolutions concernant son offuscation au cours du temps. En effet, des techniques de contournement de sandbox [5] et de chargement en mémoire de binaires offusqués [6] par un mécanisme inconnu ont été observés. Cela a suffi à susciter l’intérêt d’une rétro-analyse du loader en question.

L’objectif est de caractériser et d’extraire la charge principale du malware qui a plus particulièrement été repéré dans les environnements informatiques entre juillet et aout 2021.

Une méthode d’extraction par émulation à été présentée à la conférence Hack-It-N de décembre 2021. Une diffusion en différé de la présentation est disponible sur Youtube [16].

Analyse

Les échantillons récupérés fonctionnent sur le même modèle, à savoir une base en Rust chargeant en mémoire un stage2, lui-même écrit en Rust. Voici la liste des échantillons récupérés :

Sha256Compile timeSize
001405ded84e227092bafe165117888d423719d7d75554025ec410d1d65589252021-07-28 17:59:193.4 MB
4421dbc01ddc5ed959419fe2a3a0f1c7b48f92b880273b481eb249cd17d59b912021-08-11 15:35:556.6 MB
52d8316b0765c147558aecbda686d076783f3a08b2741b8c9e3e717cc56e8a922021-07-19 13:57:227.3 MB
580d55f1e51465b697d46e67561f3161d4534a73e8aa47e18b9bae344d46bcf42021-07-19 13:57:227.3 MB
578dc62dfa0203080da262676f28c679114d6b1c90a4ab6c07b736d9ce64e43e2021-07-15 16:28:547.1 MB
5ac6766680c8c06a4b0b4e6a929ec4f5404fca75aa774f3eb986f81b1b30622b2021-08-05 14:47:254.9 MB
64dd547546394e1d431a25a671892c7aca9cf57ed0733a7435028792ad42f4a72021-08-16 18:54:294.1 MB
88689636f4b2287701b63f42c12e7e2387bf4c3ecc45eeb8a61ea707126bad9b2021-08-03 15:46:423.3 MB
afb5cbe324865253c7a9dcadbe66c66746ea360f0cd184a2f4e1bbf104533ccd2021-06-25 17:49:487.1 MB
c425264f34fa8574c7e4321020eb374b9364a094cda9647e557b97d5e2b8c17b2021-08-16 18:54:294.1 MB
d3a486d3b032834b1203adefd25d0bf0b36fae7f9e72071c21ccc266e1e1f8932021-07-19 14:14:544.3 MB

Les dates de compilation semblent cohérentes entre elles. En effet, elles correspondent approximativement aux dates de soumission sur Virustotal [15] (avec un petit délai, naturellement). Les binaires sont strippés (c’est-à-dire que les symboles non nécessaires au fonctionnement du binaire ont été supprimés, et plus particulièrement les noms de fonctions et symboles de débogage) mais contiennent tout de même des informations sur le code source utilisé :

Malheureusement, BinDiff [7] n’est pas en mesure de comparer les binaires. Le flux de contrôle est trop complexe pour lui. Les comparaisons de code ont donc été effectuées manuellement par nos experts.

Figure 1: Erreur BinDiff

Notre analyse a permis de déterminer qu’il ne semble n’y avoir que 2 fichiers sources en dehors des librairies open source (principalement cryptographiques) utilisées. Une première technique anti-sandbox consiste à perdre du temps en effectuant une boucle inutile. L’ordre de grandeur du temps nécessaire (et perdu) est de l’ordre d’une minute avec un CPU occupé à 100%.

Figure 2: Anti sandbox par perte de temps

Après un passage par cette boucle, nous arrivons à la fonction de déchiffrement du stage2. Celle-ci comporte un important nombre d’appels successifs à des fonctions inutiles, parmi lesquelles nous retrouvons :

  • GetCommandLineA;
  • GetCurrentProcess;
  • GetEnvironmentStrings;
  • GetLastError;
  • GetProcessHeap;
  • GetTickCount;

Il s’agit probablement d’un mécanisme de saturation utilisant des calls (appel à une bibliothèque Windows) et ne prenant pas d’argument pour surcharger d’éventuelles sandboxes, ce qui complique l’émulation de code. Il semblerait qu’un script génère tous les appels. Cette technique a également l’avantage d’ajouter un ratio call/instruction crédible par rapport à un programme légitime. Compter les calls permet parfois de retrouver des routines de déchiffrement ou d’unpacking :

Figure 3: Decoys variant 1
Figure 4: Decoys variant 2

Ces appels sont répétés tout au long de la routine de désoffuscation. Les données sont empilées successivement sur la pile, puis dans le tas à l’aide avec des kernel32 !HeapAlloc, sous la forme de DWORD :

Figure 5: Stage2 buffer reconstruction

Le graphique d’appel est très différent suivant les échantillons, ce qui renforce notre hypothèse selon laquelle le code est généré à partir d’un script. Le mix des appels et de la désoffuscation implique un effort particulier pour se fondre dans un comportement semblant être légitime.

Figure 6: Control flow Variant1
Figure 7: Control flow Variant2
Figure 8: Control flow Variant3

Cette technique permet d’inclure des données binaires en limitant l’entropie entre 0.5 et 0.8, très proches de ce que l’on aurait pu attendre d’instructions légitimes.

Figure 9: Entropie du malware

Ces données, une fois reconstituées sont placées dans un buffer qui est déchiffré. On reconnait alors un mécanisme de type Key Schedule, suivi d’un générateur faisant penser à du RC4 :

Figure 10: KSA block
Figure 11: RC4 Random generator

Le paradigme Rust rend ces routines de déchiffrement difficiles à identifier. Les outils tels que findcrypt [8] ne les détectent pas. La vérification suivante permet d’identifier toute la chaîne de désoffuscation :

Figure 12: Decryption in python

La décompression est effectuée par la librairie lzma-rs [9] version 0.1.3, et les clés de déchiffrement sont facilement visibles dans le code. Le script suivant permet son extraction :

Figure 13: Key extraction script

La liste des clés RC4 des échantillons est la suivante :

  • YDVHHCYTCH ;
  • DQOQHMLGYU ;
  • BWSCVQZXOB ;
  • SIMHIDVSCR ;
  • RGZPMAAQRP ;
  • YVMOOVSIOF ;
  • KSQKGUUTXZ ;
  • EUWPUQYDTT ;
  • KHBXNNHKNN ;

Malheureusement, le nombre de clés est insuffisant pour pouvoir évaluer la qualité du PRNG [10]. A noter que le charset est très réduit, ce qui n’a pas d’importance vu que la clé est en clair dans la section « .rdata ». Au vu du graphe d’appel extrêmement variable et très complexe, l’écriture d’un script d’extraction de données générique fonctionnant pour tous les échantillons est fastidieux. Maintenant que l’on connait la clé (cf. script d’extraction précédent), il convient de trouver les données chiffrées. Celles-ci, une fois désoffusquées, comportent un entête avec des données invariantes :

Figure 14: 3 blocks of encrypted buffer header

Le script suivant permet ensuite de trouver les données extraites en mémoire, à condition que le programme ait bien réalisé la phase de déchiffrement en effectuant une recherche de type Egg-hunting :

Figure 15: Script de récupération des données chiffrées

Il ne manque plus, pour automatiser l’extraction, que de retrouver une adresse ou poser un breakpoint, ce qui est fait de manière assez logique en retrouvant les références vers la clé RC4 :

Figure 16: Script de lancement du malware

Et le script fonctionne… Du premier coup pour tous les échantillons ! Le code source de l’outil est accessible sur le github TEHTRIS [19].

Une méthode alternative a été présentée à la conférence Hack it N [16]. Elle consiste à extraire le stage2 par émulation en utilisant la lib unicorn [17]. Le code source est disponible sur le github TEHTRIS [18].

L’extraction des stage2 des échantillons étudiés donne la liste suivante :

Sha256Stage1 compile timeStage 2

 

compile time

edc3b5f8d45d7a1cceee144e57fc5ddfaf8c0c7407a1514d2f3bab4f3c9f18b82021-07-28 17:59:192021-07-28 17:39:31
d7ec38c0e89a749a7727e5644328835b50e19302e9f3a4688809403ebcbd03d22021-08-11 15:35:552021-08-11 15:30:38
6578db32dc78ef7f41213557cf894d03b97ed6974ae7a72bec9b7c7ac08c4ba92021-07-19 13:57:222021-07-19 13:44:11
d48d91451b9594eadc0d1ef6e379bbce9a6033bd337e06d46613a70187c9c5ef2021-07-15 16:28:542021-07-15 16:20:18
54109b12cbbd223f5ad79a9f87bfe50ef05a80e5551a3c1931748c36989004962021-08-05 14:47:252021-08-05 14:38:43
2d8a2bcc45daedd343eadb4222885d12a221bebbf7f1d98f92cb233df0a4c1d42021-08-16 18:54:292021-08-16 18:45:22
16feaed6222ce4a1941ae0c32eabaf0ecf68c33c49544f71d431d1b70c4247fd2021-08-03 15:46:422021-08-03 15:38:18
7af554fb260817350d33b801d9f0b8a638b831992f4b1b31c2bbdab875b211df2021-06-25 17:49:482021-06-25 17:43:23
2d8a2bcc45daedd343eadb4222885d12a221bebbf7f1d98f92cb233df0a4c1d42021-08-16 18:54:292021-08-16 18:45:22
039d63a07372e6e17f9779ccffbafbf9a06a9402ade58fbec3b0b2f8d20381752021-07-19 13:57:222021-07-19 13:46:46

Les horodatages semblent cohérents entre eux et l’on note que les dates de compilation correspondent avec un décalage de 5 à 10 min laissant à penser à une étape de packing manuelle.

On notera que le stage2 vérifie la présence d’une machine virtuelle en testant la présence des exécutables suivants :

> vboxservice.exe ;
> vboxtray.exe ;
> vmtoolsd.exe ;
> vmwaretray.exe ;
> vmwareuser.exe ;
> vmacthlp.exe ;
> vmsrvc.exe ;
> vmusrvc.exe ;
> prl_cc.exe ;
> prl_tools.exe ;
> xenservice.exe ;
> qemu-ga.exe ;
> windanr.exe.

L’étape de désoffuscation faite, le binaire n’est plus offusqué et livre facilement ses secrets. Par exemple les C2 [11] :

Figure 17: List of C2 URLs

Ou encode la liste des fichiers constituant le code source :

Figure 18: Source code metadata

L’analyse du stage2 a cependant déjà été faite, et nous ne la décrirons pas dans le présent article.

Conclusion

L’évasion de système de détection automatisée de malware est un jeu d’équilibriste entre l’entropie du binaire, le camouflage des fonctions de chiffrement, le ralentissement du délai d’analyse, l’obtention d’un ratio calls/instructions cohérent…

L’utilisation du Rust ajoute une surcouche de code machine qui est cependant bien inférieure à ce que ferait un compilateur Go [12], delphi [13], cython [14], etc. rendant une analyse manuelle tout à fait raisonnable.

Cependant, ces techniques ne tiennent pas face à un reverser, ou à une sandbox bien configurée. L’analyse manuelle des menaces les plus connues et laissant le moins de traces constituent un plus dans l’amélioration continue de nos produits sandbox et EDR. Il est très probable qu’à l’avenir, de plus en plus de malwares soient développés en Rust. L’avenir nous le dira.

* L’utilisation de stageN consiste à séparer le malware en plusieurs sous parties spécialisées afin d’échapper à une détection antivirale. Dans ce cas, le stage1 est le malware tel qu’envoyé aux victimes et le stage2 est la charge utile encapsulée dans le stage1.

BIBLIOGRAPHIE

[1] https://www.rust-lang.org/

[2] https://www.tiobe.com/tiobe-index/rust/

[3] https://github.com/nuta/kerla

[4] https://www.proofpoint.com/us/blog/threat-insight/new-variant-buer-loader-written-rust

[5] https://fr.wikipedia.org/wiki/Sandbox_(s%C3%A9curit%C3%A9_informatique)

[6] https://fr.wikipedia.org/wiki/Offuscation

[7] https://www.zynamics.com/bindiff.html

[8] https://github.com/you0708/ida/tree/master/idapython_tools/findcrypt

[9] https://github.com/gendx/lzma-rs

[10] https://fr.wikipedia.org/wiki/G%C3%A9n%C3%A9rateur_de_nombres_pseudo-al%C3%A9atoires

[11] https://www.trendmicro.com/vinfo/us/security/definition/command-and-control-server

[12] https://golang.org/

[13] https://www.embarcadero.com/fr/products/delphi

[14] https://cython.org/

[15] https://www.virustotal.com

[16] https://www.youtube.com/watch?v=4Lux_0IROMY

[17] https://www.unicorn-engine.org/

[18] https://github.com/tehtris-hub/MalwareTool/blob/main/Buer/extract_buer.py

[19] https://github.com/tehtris-hub/MalwareTool/blob/main/Buer/extract_buer_debug.py