Analyse de Andromeda Bot (analyse du loader)

Written by aaSSfxxx -

Dans cet article, je vais détailler l'analyse d'un sample d'Andromeda, étant donné qu'il existe peu de littérature à ce sujet. Ce bot a été vraisemblablement écrit en assembleur, et est capable de détecter les machines virtuelles, s'injecter dans d'autres processus comme nous le verrons dans cet article. Je me baserai ici sur le sample que m'a gentiment donné Horgh, en passant rapidement sur l'unpacking du sample.

Vue générale et analyse du "dummy payload"

Lorsqu'on unpack le sample, on s'aperçoit qu'il existe très peu d'informations "utilisables" pour le reverser: quasiment aucune "String Data Reference" disponible, aucun import... Notre malware va donc directement rechercher les fonctions en parsant le PEB pour retrouver kernel32, et toutes les fonctions utiles, comme on peut le voir ci-dessous:

On voit ici que le handle de ntdll est recherché, pour ensuite trouver la fonction "LdrGetDllHandle", qui va être utilisée pour récupérer le handle de kernel32.dll. On a ensuite une boucle qui charge les fonctions depuis un tableau de hashes, pour ensuite stocker les adresses dans un tableau. Le process crée ensuite un mutex du doux nom de "lol".
Ensuite vient une partie intéressante du code: le bot énumère tous les process en cours, calcule un "hash" à partir du nom du process, et nous renvoie à la fin de la fonction principale si le hash correspond à un des hashs "blacklistés".

Pour ma part, il reconnaît le process "VBoxService.exe", et donc je me fais renvoyer à la fin du programme, où l'on apperçoit un call vers une fonction, qui prends 2 arguments: la valeur contenue dans eax (dans mon cas le hash correspondant à vboxservice.exe), puis une adresse dans [ebp-188], valant dans mon cas 00403FBF. Du coup, on peut step into (F7), voir ce qui se passe dans cette fonction.

Lorsqu'on step dans la fonction, on retrouve encore le code qui récupère le handle de ntdll, pour y charger des fonctions utiles pour la suite (ZwTerminateProcess, ZwAllocateVirtualMemory), puis va ensuite allouer une page mémoire avec cette dernière fonction. Au passage, la valeur du premier argument (l'adresse mémoire mystérieuse) est récupérée dans ebx, et la page mémoire a pour taille le premier DWORD pointé par ebx.

On appelle ensuite une 2ème fonction qui prend pour paramètres le dword pointé par ebx+4, ainsi que les adresses ebx+1c et ebx+3c ainsi que 0x20. Du coup on step-into dans la fonction, et on voit une fonction qui génère un tableau de nombres de 0 à 255 dans l'ordre croissant, puis un algorithme de "key scheduling": nous sommes donc face à une fonction de chiffrement RC4, qui prend pour clé le premier argument, c'est-à-dire ebp+1c, et fait son key sheduling jusqu'à ce qu'on atteigne la fin de la clé, c'est-à-dire le 0x20 passé en paramètre. On en déduit aisément que le pointeur ebp+3c sont les données à déchiffrer et que le [ebx+4] est la taille des données. On sort donc de la fonction, puis on aperçoit encore un appel vers une autre fonction qui prend pour paramètres la taille des données, un pointeur vers ebx+3c ainsi qu'un pointeur vers la zone allouée précédemment.

Lors de l'analyse de la 2ème fonction, on s'aperçoit que celle-ci a un comportement assez "chaotique", qui pourrait ressembler à un algorithme de décompression. Or, en ayant aperçu un "pushad" au début de la fonction, on peut en déduire que la sortie de la fonction sera signalée par un "popad", qu'il nous suffit juste de chercher (comme lors d'un unpack manuel de UPX). On va donc poser un breakpoint sur le "popad" et F9. Une fois le bp sur le "popad" posé, il nous faut F7 jusqu'à retomber sur la fonction principale (à "MOV EDI,DWORD PTR SS:[EBP-1C]") à cause de quelques tricks anti-debug. La fonction va ensuite continuer quelques initialisations (chargement de DLL et autres) avant d'arriver à la partie intéressante: le "call eax" sur lequel on va step into. On en conclut donc que la fonction appelée par l'entry point va charger et exécuter un payload, qui sera passé en paramètre. Vu qu'on a déclenché une détection anti-VM, on peut se douter qu'on est tombé sur un "dummy payload" conçu pour tromper le reverser peu attentif.

Une analyse rapide du payload montre que le malware se copie dans "%ALLUSERPROFILES%\svchost.exe", crée une clé de démarrage sous le nom de SunJavaUpdateShed, puis lance un simple remote shell.

Analyse du payload réel

Une fois l'analyse du "dummy payload" terminée, nous allons pouvoir analyser le payload réel. Pour cela, il nous faut soit contourner tous les sauts, soit sauter direct au bon endroit c'est-à-dire ici (ie en-dessous du dernier saut qui nous THE GAME):

On s'aperçoit que un payload différent est chargé dans [ebp-188] et qu'une adresse mystérieuse (004013B9 chez moi) est placée dans eax avant le call comme précédent. Etant donné qu'on a déjà analysé la fonction appelée, il nous suffit juste de bp sur le "call eax" et de step into. Une fois le step into réalisé, on se retrouve devant ceci:

En regardant rapidement le code, on s'aperçoit qu'il y a plein d'appels à ZwCreateSection, ZwMapViewOfSection et autres: le malware utilise ici la méthode d'injection décrite sur le blog de w4kfu, pour aller s'injecter dans wuauclt.exe si on est sur un système 32bits, ou svchost.exe si on est sur un système 64 bits. Le malware va ensuite copier sa section .text dans la section qu'il a créé, qu'il va ensuite ouvrir dans wuauclt.exe. Enfin, le payload récupère l'argument qui lui a été pushed (c'est-à-dire le 2ème argument pushed par l'EP à la fonction de déchiffrage/load), et modifie l'entry point de wuauclt.exe par un call vers la fonction du malware récupérée précédemment. On peut donc relancer le programme, et changer l'entry point vers l'adresse de la fonction mystérieuse avant de lancer un F9 (et s'assurer auparavant que notre bp sur "call eax" est toujours en place).

Enfin pour les curieux qui se demandent quel algorithme de compression est utilisé par andromeda, il s'agit vraisemblablement de l'algo aPLib légèrement modifié (il suffit juste de stfw quelques instructions "exotiques" utilisées par l'algo pour s'en rendre compte)

Edit du 16/12/2012: J'ai finalement ajouté l'IDB du "loader" disponible à cette adresse afin de rendre les choses plus "visuelles".