Dans ce billet on va parler d’un ransomware qui a ciblé plusieurs centaines d’entreprises sur ces deux dernières années. Cette analyse sera divisée en plusieurs parties.
Je vais commencer par une rapide présentation du malware en question, puis une première analyse axée sur les techniques de désobscurcissement (ou désobfucation) du malware.
Lockbit 3.0
Lockbit est un logiciel malveillant qui fait partie de la famille des ransomwares. Ce malware est conçu pour voler et chiffrer les fichiers présents sur les machines infectées.
Logo de Lockbit 3
Le malware dépose un fichier texte demandant une rançon à payer en cryptomonnaies pour que les fichiers soient déchiffrés et que les données volées ne soient pas revendues ou publiées.
Sample
Le sample en question est disponible sur Malware Bazaar.
- SHA256 :
80e8defa5377018b093b5b90de0f2957f7062144c83a09a56bba1fe4eda932ce
- MD5 :
38745539b71cf201bb502437f891d799
Si on analyse le ransomware sur Virus Total, celui-ci est détecté comme malveillant par 63 solutions de sécurité.
Analyse de Lockbit sur VT
Un fait intéressant, une version non empaquetée de Lockbit 3.0 disponible sur Malware Bazaar est identifiée comme BlackMatter. Il semble que le groupe ait acheté le code source d’un autre ransomware pour l’utiliser dans la version 3.0, que l’on appelle aussi LockBit Black.
Exécution
Avant de faire une première analyse, je me suis dis qu’il serait intéressant d’exécuter Lockbit pour voir son comportement. Pour ça, le double-clique sur l’exécutable ne suffit pas.
Lockbit nécessite d’être exécuté avec les paramètres suivants via la ligne de commande: -k LocalServiceNetworkRestricted -pass db66023ab2abcb9957fb01ed50cdfa6a
.
Quelques secondes après avoir exécuté, Lockbit on reçoit une notification de Windows nous prévenant que le service Windows Security Center a été désactivé. Dans le gestionnaire de taches, on a un processus BD87.tmp
qui écrit de façon importante sur le disque.
Visualisation des processus dans le task manager
Quelques minutes plus tard, le fond d’écran a changé, des fichiers ont été chiffrés et un nouveau fichier texte est déposé sur le bureau.
Lockbit wallpaper
Le fichier texte en question correspond à la demande de rançon qui annonce que tous les fichiers importants ont été chiffrés et extrais de la machine.
Pour des questions de performance, Lockbit ne pas semble chiffrer entièrement les fichiers, mais seulement une partie. J’ai rempli un fichier de plusieurs méga octets avec des a
. On voit bien qu’à un certain point, les données ne sont plus chiffrées et que les a
s’affichent.
Fichier texte non chiffré après une certaine taille
Unpacking
Lorsqu’on ouvre l’exécutable dans IDA, très peu de fonctions sont reconnues. IDA ne semble pas capable d’analyser les fonctions ni de les décompiler.
IDA Pro face au malware empaqueté
Le malware est en fait empaqueté (packed en anglais) à l’aider d’un packer. L’utilisation du packer sert à rendre plus compliquée l’analyse statique du malware.
Le payload qui correspond au binaire initial est chiffré par le packer. A l’exécution du ransomware, le payload est déchiffré dynamiquement en mémoire.
C’est la première fonction appelée, sub_41B000
, qui semble effectuer les actions de déchiffrement. Contrairement aux autres fonctions, celle-ci est analysée correctement par IDA.
Pour unpack Lockbit, la version 32 bits de x64dbg.
Avant tout il faut changer les paramètres de la ligne de commande Files -> Change Command Line
avec les mêmes paramètres que pour exécuter Lockbit.
Ensuite, à partir du point d’entrée du binaire, on exécute chaque instruction pas à pas jusqu’à arriver après la fonction sub_41B000
. On remarque que les noms des fonctions de Windows apparaissent, mais aussi que le reste du code a été déchiffré.
Execution du malware avec x64dbg
A l’aide du plugin Scylla de x64dbg, on peut ensuite récupérer les imports et dumper la mémoire du processus dans nouveau fichier PE.
Dump du malware avec Scylla
Lorsque l’on charge le PE dans IDA, de nouvelles fonctions sont reconnues et certains symboles sont résolus.
Rendu dans IDA
Lors du dump du processus Scylla a changé le point d’entrée vers la valeur du registre EIP ce qui fait que la fonction qui déchiffre le code n’est pas la première fonction appelée dans start
, mais celle juste au dessus : sub_41B000
.
Résolution des hashes d’API
Après avoir unpack le malware, on a pu récupérer quelques symboles comme on peut le voir sur le screenshot de IDA ci-dessus. La plupart des appels de fonctions des APIs natives de Windows restent obscurcis.
A la compilation du malware, les appels vers les fonctions natives sont hashés. Ils sont résolus dynamiquement à l’exécution comme le montre le schéma ci-dessous.
Schéma de la video de OAlabs
La fonction en charge de résoudre ces hashes est sub_41B47D
. Comment le sait-on ? La fonction appelée juste après (sub_40B804
) s’attend à ce que les APIs soient résolues donc c’est sûrement la fonction ci-dessous qui va créer la table d’import des adresses.
Fonction resolve_iat dans IDA
La fonction get_api_by_hash
prend en paramètre une valeur qui correspond au hash d’une API de Windows. On pourrait analyser le code de la fonction et résoudre ce hash manuellement. Mais pour gagner du temps, on va utiliser un plugin IDA : hashdb-ida.
Hashdb-ida est un plugin qui fonctionne avec HashDB pour recenser les hashes utilisés par les logiciels malveillants. A l’aide de cet outil, on va pouvoir résoudre les imports automatiquement.
Commençons par résoudre les hashes de la fonction get_api_by_hash(0xF80F18E8)
avec hashdb-ida :
- Selectionner
0xF80F18E8
- Clic droit -> HashDB Hunt Algorithm : choisir
ror13_add
- Clic droit -> HashDB Lookup: le plugin detecte la fonction
RtlCreateHeap
et propose d’importer le modulentdll
. - Un nouveau type
Enum
est créé dans les local types que l’on peut appliquer au paramètre de la fonction - On peut refaire un lookup sur le second appel de
get_api_by_hash
qui détecteHeapAlloc_0
et importekernel32
. - Il ne reste qu’à changer le type du paramètre de la fonction
get_api_by_hash
parhashdb_strings_ror13_add
qui correspond à l’enum créé par hashDB-ida pour qu’il soit interprété par le decompilateur.
Si on effectue la même manipulation pour le second appel à cette même fonction, on resoud HeapAlloc_0
.
Intéressons-nous ensuite à la fonction resolve_api_table
.
Cette fonction compare une liste de hashes avec les exports du processus à l’aide de l’algorithme de hachage implémenté dans la fonction en question. Ces hashes de fonctions sont stockées dans des structures globales.
Voici à quoi ressemble la liste de hashes lors du premier appel de resolve_api_table
:
1
2
3
4
5
6
7
8
9
10
11
12
.text:00407DA4 dword_407DA4 dd 410A87Dh, 0BD09C722h, 60F68344h, 0E5EFEE15h, 16F36F30h
.text:00407DA4 dd 0E2215D8Eh, 2B573CD1h, 0CB2F3F91h, 5F02CE05h, 4514EFA9h
.text:00407DA4 dd 0AB6CECE8h, 4B34EBE9h, 0C5249F90h, 2B54E090h, 2B7C9CD0h
.text:00407DA4 dd 0CB449BD0h, 2B449ED0h, 47628058h, 18D6ABA9h, 51B2AF80h
.text:00407DA4 dd 63B2B140h, 0E3A28158h, 42B2A9E9h, 0DD310DDBh, 0BA6BFB4Eh
.text:00407DA4 dd 0C17D2304h, 7BE0BC93h, 56FDB04h, 0ECFB2F4Bh, 671AE144h
.text:00407DA4 dd 0D2DD1E72h, 55B90506h, 1ABD870Eh, 4CBE82B8h, 9F9E00DFh
.text:00407DA4 dd 3EC99E49h, 42CEB74Bh, 3DECE4DDh, 0E4AF3FA5h, 1DCAAF67h
.text:00407DA4 dd 6DD53A2Fh, 0E3C40A3Bh, 54F31BB0h, 0FB5FF02h, 0F82BFDF3h
.text:00407DA4 dd 95FF30A8h, 1B2F96D4h, 498DE81Ah, 2F94EFD3h, 5F3922D0h
.text:00407DA4 dd 0F3E3D5FFh, 0E4F716C5h, 0E465E132h, 26325B53h, 29608ED5h
.text:00407DA4 dd 64769D53h, 981AEF0h, 0DEB32182h, 40BA6DD5h, 0CCCCCCCCh
La fonction parcourt cette liste et utilise comme délimiteur 0xCCCCCCCC
qui est présent à la fin de chaque liste de hashes.
1
2
3
4
5
6
7
8
9
10
11
12
13
[..]
v4 = a2 + 1;
result = sub_407AE0(*a2 ^ 0x4506DFCA);
if ( result )
{
v6 = (int *)(a1 + 4);
while ( 1 )
{
result = *v4++;
if ( result == 0xCCCCCCCC )
break;
api_by_hash = get_api_by_hash((hashdb_strings_ror13_add)(result ^ 0x4506DFCA));
[..]
De plus, on remarque ci-dessus la même valeur 0x4506DFCA
utilisée plusieurs fois. C’est la clé pour le chiffrement XOR.
A l’aide de HashDB, on va pouvoir déchiffrer automatiquement ces hashes.
Il faudra sélectionner la clé de chiffrement dans la fonction resolve_api_table
puis faire un clic droit -> “HashDB Set XOR key”.
Ensuite, on sélectionne les blocs de dwords et on les convertit à l’aide d’Auto-DWORD.
Il ne reste plus qu’à sélectionner à nouveau les valeurs converties et faire un clique droit -> “HashDB Scan IAT” pour que le plugin resolve les fonctions.
Résolution des imports via HashDB
Lors de la sélection ne prenez pas la première valeur qui correspond au nom de la bibliothèque, ni la dernière valeur (0xCCCCCCCC
).
Il ne reste plus qu’à convertir ces valeurs en structures et changer les types des paramètres passés à la fonction resolve_api_table
.
En rafraîchissant le pseudo-code de la fonction resolve_iat
, on retrouve bien les références des pointeurs de fonctions qui appartiennent à la structure shell32.
Visualisation des references de shell32 dans IDA
Pour résumer cette première partie de l’analyse de Lockbit 3, on a vu ce qui se passe quand lockbit infecte une machine.
Ensuite à l’aide de x64dbg on a unpack le malware en l’executant instructions par instructions jusqu’à arriver à la fonction de déchiffrement.
Puis nous avons résolu les imports dynamiques avec IDA et HashDB.
Dans le prochain article on se concentrera sur l’extraction de la configuration utilisée par Lockbit qui définit ce que le logiciel doit infecter.
J’espère que ce post vous a plu, n’hésitez pas à me contacter sur Twitter ou même par mail si vous avez des questions.
Liens et sources :