Written by aaSSfxxx -
Comme promis dans mon précédent billet, j'ai poursuivi (motivé à coup de chocapicz et de techno bourrine, enfin bref passons) ma quête du Graal, c'est-à-dire la poursuite de l'analyse de Andromeda Bot.
Je pense que cette analyse ennuyeuse à mourrir pour le commun des mortels ravira les reverseurs de malwares. Bref, j'arrête le bullshit du chapô (je suis pas doué en remplissage d'introductions, malheureusement), et passons aux choses sérieuses.
Une fois avoir step into dans le "call eax" du loader (cf avant pour voir comment faire), nous pouvons observer ceci:
On a un appel à SetErrorMode (useless) puis un appel à une autre fonction avant d'avoir un leave puis ret. Donc, on F7 sur le 2ème call, où l'on tombe sur une routine qui parse le PEB à la recherche du "atoi" perdu, puis le process initialise le tas, récupère diverses infos telles que des infos sur le disque dur, la version installée, avant d'allouer une plage de mémoire.
Vient ensuite une vérification "amusante": le bot vérifie que la variable d'environnement "src" existe (cette variable a été crée par le loader), et le bot refuse de se lancer si cette variable n'existe pas. Il nous suffit de nopper le jmp derrière le call à "GetEnvironmentVariableW" pour poursuivre le déroulement du bot. Le bot call GetShortPathNameW, puis une fonction "mystérieuse" dans laquelle on va step into, ce qui nous permet d'obtenir ceci:
Le bot vérifie si l'utilisateur a bien le token admin, et place le chemin d'installation ainsi que la clé de registre à créer dans une variable pour être réutilisée plus tard. Une fois ceci fait, le bot récupère aléatoirement une extension définie dans une liste, avant de revenir dans la fonction parente. Il récupère ensuite les informations du disque dur, puis construit une chaîne numérique à partir de ces infos, qui servira de nom au mutex que créera le bot.
On voit ensuite un appel à RtlGetLastWin32Error, puis un appel à 00341A21, dans lequel on va step into (F7) avec ollydbg. On s'aperçoit que le bot va se copier à l'emplacement sélectionné précédemment, puis va créer la clé à l'emplacement choisi précédemment (encore :>), clé qui se voit attribuer des privilèges différents, pour complexifier sa suppression pour un néophyte. On retourne ensuite dans la fonction principale, qui détruit le fichier de lancement, avant d'initialiser winsocks et de créer plusieurs threads. Une analyse avec IDA nous montre que le bot va charger les modules qu'il a stockés au préalable, ainsi que charger une liste de DLLs (qui sont sûrement malveillantes).
Le dernier thread, quant à lui, est le "coeur" du bot, est est placé dans une boucle mise en pause par un ZwDelayExecution. On va donc poser un bp sur le ThreadProc du bot principal et lancer l'exécution via F9.
Une fois le breakpoint posé et l'exécution lancée, on obtient quelque chose comme ceci:
Une fois passé la création du "bot string" (je ne ferai pas de dessin, vous avez le screenshot en haut), on a deux boucles différentes.
Premièrement, esi pointe vers un tableau d'adresses (vide dans le sample que j'analyse), qui contient l'adresse de l'image base d'une DLL. Le bot va essayer de trouver une fonction (inconnue, étant donné qu'on a juste un hash) et de l'appeler. Cette fonction renvoit un pointeur vers une chaîne de caractères, qui va être concaténé au "bot string" qui sera renvoyé au C&C.
Une fois le tableau d'adresses parcoururu, un coup de chiffrement RC4 est appliqué au "bot string", puis le tout est encodé en base64. On charge ensuite dans esi un tableau de pointeurs, qui pointent vers une liste d'URLs de C&C (qui est parcouru dans la 2ème boucle).
Les données en base64 sont envoyées à l'URL spécifiée, et s'il n'y a pas eu de problème, le retour subit un décodage base64 puis RC4 avant de passer dans une fonction de "parsing", située juste avant le call de RtlFreeHeap (nommée loopData dans mon IDB).
Cette fonction prend pour argument les données décodées précédemment,que la fonction va traiter. Cette fonction est composée d'une boucle principale, qui récupère un octet de la zone, sort de la boucle si cet octet est nul. Si l'octet n'est pas nul, on le stocke dans ecx, et on récupère un dword. Ensuite, on push esi, eax et ecx à une fonction mystérieuse (qui va certainement effectuer les actions demandées par le C&C). On a enfin une boucle de "nettoyage" qui va incrémenter esi jusqu'à trouver un null byte. On peut donc en déduire que le C&C renvoie au bot un truc de la forme:
<padding de 4 octets><trame1>\0<trame 2>\0...<trame n>\0\0
où les trames sont constituées comme ceci:
<octet indiquant l'action à effectuer><id de la tâche><données>
Il ne nous reste désormais plus qu'à comprendre comment sont interprétées ces trames.
Le bot comprend 7 commandes différentes de la part du C&C. Toutes les commandes non reconnues ne font rien du tout (ce qui paraît logique :>). Bref, voici un aperçu des commandes (screenshot d'IDA):
La plupart des commandes envoyées par le C&C sont des ordres pour télécharger des plugins (modules compressés/chiffrés dans un format "propriétaires"), des exécutables et DLL en clair ou compressés dans le même format de fichier que les plugins (qui sont des binaires "plats"). On remarque aussi des fonctions de mise à jour du bot (qui va rechercher le nom du bot stocké dans le registre). On a malgré tout une fonction qui supprime les DLLs installées par le bot, ainsi qu'une fonction qui désinstalle le bot et ses modules.
Bref pour ceux qui sont intéressés, l'IDB du payload est disponible ici, et un script d'extraction est dispo ici aussi.