Analyse LockBit

Analyse statique du code

Yohan, notre expert en reverse engineering est tombé récemment sur une nouvelle souche du programme LOCKBIT. Une annonce de LOCKBIT 3.0 ayant été faite récemment, serions-nous face à cette mise à jour ou avons-nous simplement à faire à un groupe imitateur ? Nous sommes en cours d’analyse sur le sujet, voici nos premiers éléments. Qu’en pensez-vous ?

Vérification de l’Anti-Debug

Au début du programme, celui-ci vérifie que le flag `NtGlobalFlag` (`0x70`) est set. Si c’est le cas, alors le binaire rentre dans une boucle infinie. Sinon, le binaire continue son exécution.

Figure 1: Vérification de l’Anti-Debug.

Chargement des DLL

Le binaire charge très peu de DLL et de fonctions au démarrage.

Figure 2: Liste des DLL chargés par le programme.

Si l’on continue dans le code, on peut voir des modifications de chaînes de caractères depuis la stack.

Figure 3: Ajout d’une string sur la stack.

On s’aperçoit ensuite que la chaîne est XORée avec une clé statique mise en place juste avant la chaîne. Cette clé, pour la première chaîne, est `0x20`, chose que l’on peut vérifier avec Python.

Figure 4: XOR de la chaîne avec la clé 0x20.
Figure 5: Code du binaire permettant de XOR la chaîne.

Chaque nom de DLL est encodé avec du code différent. La liste des DLL encodées est la suivante :

- `gdiplus.dll`
- `ws2_32.dll`
- `shell32.dll`
- `advapi32.dll`
- `user32.dll`
- `ole32.dll`
- `netapi32.dll`
- `gpredit.dll`
- `oleaut32.dll`
- `shlwapi.dll`
- `msvcrt.dll`
- `activeds.dll`
- `gdiplus.dll`
- `mpr.dll`
- `bcrypt.dll`
- `crypt32.dll`
- `iphlpapi.dll`
- `wtsapi32.dll`
- `win32u.dll`
- `Comdlg32.dll`
- `cryptbase.dll` 
- `ombase.dll`
- `winspool.drv`

Résolution des DLL Dynamique

Une fois le nom des DLL résolu, le binaire doit résoudre dynamiquement les adresses des DLL afin de pouvoir utiliser les fonctions. Pour cela, il résout dans un premier temps `Kernel32.dll` puis `LoadLibraryA` afin de résoudre chacune des fonctions.

Figure 6: Résolution des fonctions dynamiquement.

Kernel32.dll

Pour la résolution de cette DLL, le binaire réalise un hash FNV du nom de la DLL en minuscule et la vérifie avec une chaîne harcodé. Si cette valeur est juste alors l’adresse de base de la DLL est retourné.

Figure 7: Vérification du nom de la DLL et renvoie de l’adresse de base.
Figure 8: Résultat d’un hash FNV sur la chaîne Kernel32.dll.

LoadLibraryA

Pour la résolution de cette fonction, le binaire réalise les mêmes opérations. Il boucle dans les fonctions présents dans Kernel32.dll puis pour chaque fonction réalise son hash FNV et le compare à une valeur hardcodé. Si la valeur est la même alors il retourne l’adresse de base de la fonction.

Figure 9: Vérification du nom de la fonction et renvoie l’adresse de base.
Figure 10: Résultat d’un hash FNV sur la chaîne LoadLibraryA.

Pas touche à ma langue

Le binaire va ensuite récupérer la fonction GetSystemDefaultUILanguage afin de vérifier la langue par défaut sur l’ordinateur pour ne pas chiffrer les systèmes ayant une certaine langue par défaut.

Figure 11: Récupération de l’adresse de base de la fonction GetSystemDefaultUILanguage.
Figure 12: Résultat d’un hash FNV sur la chaîne GetSystemDefaultUILanguage.

La valeur renvoyé par cette fonction est ensuite comparé avec des entiers qui correspondent à la langue du système.

Figure 13: Vérification de la langue du système.

En se basant sur la documentation officiel de microsoft, il est possible de récupérer la liste des langues comparés avec la variable.

- Azeri (Cyrillic)
- Azeri (Latin)
- Armenian - Armenia
- Belarusian
- Georgian
- Kazakh
- Kyrgyz (Cyrillic)
- Russian - Moldava
- Russian
- Tajik
- Turkmen
- Uzbek (Cyrillic)
- Uzbek (Latin)
- Ukrainian

Si la langue est dans la liste alors, le binaire résout la fonction ExitProcess pour terminer l’exécution.

Figure 14: Résolution de la fonction ExitProcess.
Figure 15: Résultat du hash FNV pour ExitProcess.

Réduction des droits sur le process

Le binaire va réduire ses droits d’accès au process en modifiant sa propre liste d’accès.

Pour cela, il résout la fonction NtOpenProcess pour avoir un handle sur son process en cours et récupère les droits via GetSecurityInfo.

Suite à cela, il appelle RtlAllocateAndInitializeSid pour créer une nouvelle structure SID afin d’appeler RtlAddAccessDeniedAce et mettre les droits du groupe EVERYONE à ACCESS_DENIED.

Figure 16: Récupération des droits sur le process.

Enfin, le binaire va appeler RtlGetAce afin de passer sur chaque ACE et ajouter une ACL via RtlAddAce. Le binaire appelle ensuite SetSecurityInfo pour appliquer toutes les ACL et bloquer l’accès à tout le monde sur le process.

Figure 17: Blocage du process pour tout le monde.

Error Stack Trace

Le binaire utilise la fonction NtSetInformationProcess afin de bloquer le moindre message d’erreur. Il utilise trois flags qui sont les suivants :

- SEM_FAILCRITICALERRORS
- SEM_NOGPFAULTERRORBOX
- SEM_NOALIGNMENTFAULTEXCEPT
Figure 18: Blocage des messages d’erreurs.

Décodage de la configuration

La fonction suivante permet de décoder la configuration dans le binaire.

Un simple XOR avec la clé 0x5F est réalisé, il est donc facile de récupérer les chaînes en réalisant un XOR sur tout le binaire.

Après avoir effectué le XOR, il est possible de récupérer une image via binwalk. L’image affiche le logo de LockBit.

Figure 19: Récupération de l’image dans la configuration du binaire.

Une seule image ressort, pourtant il existe 9 fichiers.

- Fichier EMF: Contient le vecteur graphique du texte "ALL YOUR IMPORTANT FILES ARE STOLEN AND ENCRYPTED"
- Fichier EMF: Contient le vecteur graphique pour "LOCKBIT 2.0"
- Un fichier Blender Pro Medium TTF
- Un fichier Proxima Nova TTF
- Le texte du logo LockBit en PNG
- Le logo LockBit en PNG
- Un logo grand logo LockBit en PNG
- Une liste de processus
- Une liste de services

Le binaire va ensuite compter le nombre de processus dans la liste afin de les mettre dans une liste.

La configuration elle, est stocké dans une liste de bytes.

Si le byte contient 0xFF alors l’option est activé dans le cas contraire, la valeur du byte est 0xAA.

Figure 20: Configuration du binaire.

Les index de la liste correspondent à :

- Index 0: Désactivation du bypass de l'UAC.
- index 1: Activation de l'auto suppression.
- Index 2: Activation de la page de log.
- Index 3: Activation du chiffrement par le réseau.
- Index 4, 5, 6: Si un de ces 3 flags est à `0xFF` alors le binaire se réplique via les GPO.
- Index 7: Mise en place de clé de registre pour les fichiers .lockbit.
- Index 8: Impression de la page de ransom sur les imprimantes.

On remarque que contrairement aux malwares dans la nature, celui-ci à une configuration modifié.

Il n’active pas le chiffrement par le réseau et ne met pas en place de clé de registre pour les fichiers .lockbit.

Escalade de privilège

Pour avoir tous les droits sur la machine courante, le binaire va utiliser une technique connu nommé `Juicy Potato` pour élever ses droits. Cette technique abuse du `Golden Privileges` afin d’élever ses droits en local.

Log

Le binaire va ensuite vérifier si le flag de Log est activé dans la configuration. Etant donné que ce n’est pas le cas dans notre configuration, je vais passer cette partie.

Bypass UAC

Pour bypass l'UAC, le binaire va dans un premier temps vérifier qu'il est admin puis, va bypass l'UAC via l'interface COM `ColorDataProxy/CCMLuaUtil`. Cette technique connu peut être trouvé sur [Github](https://github.com/hfiref0x/UACME/blob/92e84a734c4719a9067f4e9c8cb0e263ae4e06af/Source/Akagi/methods/hybrids.c#L877).

Réplication via GPO

Si le binaire est admin et qu’un des flags pour la réplication GPO est activé alors, celui-ci va ensuite essayer de se répliquer via les GPO.

Figure 21: Vérification des droits et des flags pour lancer la réplication via GPO.

Dans un premier temps, le binaire vérifie s’il est exécuté sur l’Active Directory en récupérant le nom de l’ordinateur courant et celui de l’Active Directory afin de comparer les deux.

Figure 22: Check si l’ordinateur actuel est un AD.

Si l’ordinateur actuel n’est pas un AD, alors cette partie s’arrête et le binaire n’essaye de pas de créer de GPO.

Le binaire, va ensuite récupérer le nom DNS de l’AD ainsi que le nom du compte Administrateur afin de se connecter au domain.

Suite à cela, il va préparer la chaîne pour la création de la GPO. Pour cela, il s’aide de la format string `%02X%02X%02X%02X%02X%02X%02X` utilisé sur la clé publique hardcodé dans le programme.

Cette clé publique aussi est différente des autres samples rencontré dans la nature.

A l’aide de cette chaîne, le binaire va ensuite se connecter au domaine.

Figure 23: Création de la chaîne à l’aide de la format string et connexion au domaine.

Dans cette continuité, en s’aidant de la chaîne LDAP formaté juste avant, le binaire va créer la GPO dans le domaine.

Figure 24: Création de la GPO.

Une fois cette GPO créé, le binaire va créer et mettre à jour le fichier GPT.INI.

Ce fichier va contenir la chaîne suivante formaté:

```
[General]
Version=%s
displayName=%s
```

La version est contenu sur la stack est XORé ce qui permet de la récupérer. La valeur pour la version est `2621892`.

Figure 25: Contenu du fichier GPT.INI.

Maintenant, le binaire va mettre à jour le dossier de la GPO pour y ajouter les fichiers suivants :

– \MACHINE\Preferences\NetworkShares\NetworkShares.xml: **Permet de mettre en partage réseau tous les disques du serveur.**
– \MACHINE\Preferences\Services\Services.xml: **Arrête une liste de service trouvé précédemment.**
– \MACHINE\Preferences\Files\Files.xml: **Place le binaire sur le Bureau de tous les partages.**
– \MACHINE\Preferences\ScheduledTasks\ScheduledTasks.xml: **Kill tous les process de la liste trouvé précédemment.**
– \MACHINE\Registry.pol: **Contient des clés de registre.**
– \MACHINE\comment.cmtx

Les chaînes de caractères de chaque fichier sont situés sur la stack et encodé différemment à chaque fois. Pour le cas de `ScheduledTasks.xml` le contenu du fichier est encodé de la manière suivante :

Figure 26: Décodage de la chaîne pour le fichier ScheduledTasks.xml.

Une fois le contenu des fichiers mise en place, le programme attend une minute avant de lancer une réplication.

Pour la réplication, le programme passe par PowerShell, cette chaîne est encodé dans la stack puis décodé avec une simple soustraction.

Figure 27: Commande de réplication PowerShell.

Le binaire vérifie évidemment si la commande s’est bien déroulé. En cas de problème celui-ci passe par le LDAP pour envoyer un gpudpate sur tous les postes.

Figure 28: Fin de la fonction de réplication.

Suppression des Shadows Copies

Une fois le malware implanté, celui-ci va supprimer les backups pour que le système ne soit plus récupérables. Pour cela, il va passer par des chaînes encodés sur la stack et va exécuter les commandes suivantes :

- cmd.exe /c vssadmin Delete Shadows /All /Quiet
- cmd.exe /c bcdedit /set {default} recoveryenabled No
- cmd.exe /c bcdedit /set {default} bootstatuspolicy ignoreallfailures
- cmd.exe /c wmic SHADOWCOPY /nointeractive
- cmd.exe /c wevtutil cl security
- cmd.exe /c wevtutil cl system
- cmd.exe /c wevtutil cl application
Figure 29: Exécute chacune des commandes pour supprimer les shadows copies.

Impression des notes du ransomware

Figure 30: Lancement de la fonction pour imprimer la ransomnote.

Pour ce qui est de l’impression, dans un premier temps le binaire récupère toutes les imprimantes sur le système à l’aide de la fonction `EnumPrintersW`.

Une fois la liste de toutes les imprimantes récupérés, il va s’assurer de ne pas imprimer dans des fichiers en comparant les imprimantes avec les chaînes `Microsoft Print to PDF` et `Microsoft XPS Document Writer`.

Une fois ce filtrage effectué, le binaire va ouvrir l’imprimante, écrire le contenu de la ransomnote et fermer l’imprimante pour chacune des imprimantes présentes.

Figure 31: Fermeture de l’imprimante.

Processus de chiffrement

Le binaire appelle la fonction `FindFirstVolumeW` afin de trouver le premier volume de disponible sur la machine pour commencer son chiffrement. Il itère les volumes suivants avec `FindNextVolumeW` pour trouver ceux disponibles.

Figure 32: Itération pour récupérer tous les volumes.

Pour initialiser son contexte cryptographique, le binaire essaye d’importer la DLL `bcrypt`, s’il échoue il récupère alors la fonction `CryptAcquireContextW` présente dans la DLL `advapi32`.

Figure 33: Initialisation de la fonction Cryptographique.

Le binaire créé ensuite une paire de clé publique et privée, à l’aide de l’algorithme `Libsodium`. Il chiffre la clé publique et supprime la clé privée de la mémoire.

Figure 34: Génération, Chiffrement, Suppression.

Pour sa dernière partie de process, le binaire va lancer la fonction `NtCreateIoCompletion` pour lui permettre d’accélérer son process de chiffrement et va lancer autant de Thread de chiffrement qu’en contient le CPU.

Durant son process, le binaire va éviter de chiffrer les dossiers, fichiers et extensions suivantes:

Dossiers:

- $Windows.~bt
- intel
- msocache
- $recycle.bin
- $windows.~ws
- tor browser
- boot
- windows nt
- msbuild
- microsoft
- all users
- system volume information
- perflog
- google
- application data
- windows
- windows.old
- appdata
- mozilla
- microsoft.net
- microsoft shared
- internet explorer
- common files
- opera
- windows journal
- windows defender
- windowsapp
- windowspowershell
- usoshared
- windows security
- windows photo viewer

Fichiers:

- ntldr
- ntuser.dat.log
- bootsect.bak
- autorun.inf
- thumbs.db
- iconcache.db
- restore-my-files.txt

Extensions:

- .386
- .cmd
- .ani
- .adv
- .msi
- .msp
- .com
- .nls
- .ocx
- .mpa
- .cpl
- .mod
- .hta
- .prf
- .rtp
- .rpd
- .bin
- .hlp
- .shs
- .drv
- .wpx
- .bat
- .rom
- .msc
- .spl
- .msu
- .ics
- .key
- .exe
- .dll
- .lnk
- .ico
- .hlp
- .sys
- .drv
- .cur
- .idx
- .ini
- .reg
- .mp3
- .mp4
- .apk
- .ttf
- .otf
- .fon
- .fnt
- .dmp
- .tmp
- .pif
- .wav
- .wma
- .dmg
- .iso
- .app
- .ipa
- .xex
- .wad
- .msu
- .icns
- .lock
- .lockbit
- .theme
- .diagcfg
- .diagcab
- .diagpkg
- .msstyles
- .gadget
- .woff
- .part
- .sfcache
- .winmd

Auto Suppression

Une fois le processus de chiffrement terminé, le programme va procéder à sa suppression.

Pour cela, il va lancer une commande shell toujours encodé sur la stack.

Figure 35: Décodage de la chaîne d’auto suppression.

La commande `fsutil` est utilisé pour que le binaire ne soit plus sur le disque une fois l’exécution terminé. C’est bien plus efficace qu’une simple suppression avec la commande `rm` car le fichier est totalement remplacé sur le disque, il n’y a donc pas de moyen de le récupéré avec des outils une fois la suppression effectué.

De plus, il utilise la fonction `MoveFileExW` pour dire au système de supprimer le fichier après un reboot.

Fin d’exécution

Le binaire résout la fonction `ExitProcess` afin de se fermer.

Figure 36: Arrêt du binaire.

Conclusion

La génération de clé de 32 bits suivis du chiffrement de la clé publique avec la clé publique de Lockbit et la suppression de la clé privée en mémoire rend le processus de décryptage impossible.

De plus, le binaire ne communique pas avec l’extérieur. Il n’envoie pas de message et ne se connecte à aucun C2. Ils n’ont pas besoin de récupérer d’information spécifique sur le chiffrement car toutes les informations sont stockés à la fin des fichiers. Ces informations sont bien évidemment aussi chiffrés.

Pour ce qui est de la propagation, sachez que si la configuration GPO est activé dans le binaire et que vous détectez une GPO pour déployer le malware. Celui-ci à de grande chance d’avoir été lancé dans un premier temps sur l’Active Directory.