Dans le cadre du challenge Zero2Automated maintenant distribué toutes les 3 semaines, l’objectif était de développer un script permettant d’extraire les adresses IP de tout sample Danabot de la même campagne.
Challenge
Malheureusement, après 2 semaines de travail régulier, je ne suis pas parvenu à développer une méthode permettant d’extraire ces IP sur tous les samples. Je me suis concentré pour les récupérer sur un seul d’entre eux, ce qui m’a empêché de prendre le temps pour développer une méthode d’extraction universelle.
Je vais détailler l’analyse du binaire et la méthode d’extraction pour un seul sample.
Accès initial
Le fichier du challenge se présente sous format VBS. Il s’agit d’un script obfusqué qu’il va falloir décoder pour en extraire le payload. Ce script ne fait pas vraiment partie du challenge, mais cela s’avèrerait intéressant de le comprendre.
Ce script est composé de plusieurs arrays contenant des variables. Celles-ci sont définies à la fin du fichier. (Oui, en VBS on peut définir des variables après leur utilisation dans le code..)
Le fichier contient également du code inutile, mais qui ne gêne en rien son analyse.
Après avoir déobfusqué, analysé et renommé les différents fonctions et paramètres, la logique apparaît comme simpliste, et il est très facile de comprendre le code.
Pour résumer, ce script contient les données du fichier à dropper dans les arrays. Le script commence par afficher un faux message d’erreur pour faire croire à l’utilisateur que le programme ne se lancera pas. Ensuite, il crée un fichier dans le dossier Temp, déchiffre et écrit le contenu des arrays dans ce fichier. Enfin, il exécute le fichier nouvellement créé (qui s’avère être une DLL)
Loader?
En analysant les imports de cette DLL, je ne trouve aucune API concernant des connexions vers un serveur externe. En utilisant le merveilleux plugin capa pour IDA, je trouve uniquement des fonctions responsables de résoudre des APIs.
J’ai ainsi lu la documentation MSDN et placé un breakpoint sur chaque fonction permettant soit d’initialiser une connexion, soit d’envoyer une requête, par exemple :
-
WSAStartup
-
WinHttpOpen
-
socket
-
WSASend
-
WinHttpSendRequest
Mais aucun break, et le programme se ferme à chaque fois..
Je me penche ainsi sur une éventuelle fonction d’anti analyse, avec un breakpoint sur IsDebuggerPresent, mais rien. Je me rends compte que des IP sont contactées même après avoir fermé le programme.
Il y a donc un autre exécutable lancé, je mets donc un breakpoint sur l’API CreateProcessW et en effet, la DLL se relance via rundll32.exe.
Binaire principal
En analysant cette DLL on peut voir qu’elle contient beaucoup plus de fonctions, d’imports et d’exports qu’avant d’avoir été lancée. Ce qui me fait penser à une injection de données dans la DLL.
On remarque également des imports de ws2_32.dll (Windows Sockets 2) ce qui nous confirme que ce programme est en capacité de contacter une IP externe.
En débuggant le programme, je remarque que peu avant l’appel à WinHttpSendRequest (envoie une requête vers un serveur), une fonction fait apparaître des adresses IP bit par bit. Je me penche sur son analyse.
Fonction de résolution d’IP
Voici le pseudocode généré par IDA que j’ai modifié après l’analyse de cette fonction avec un débugger. Cette fonction prend un dword en argument et retourne un bit d’IP.
Le DWORD passé en argument est important dans cette fonction car c’est lui qui détermine l’IP déchiffrée. La fonction comprise, je suis parti en quête de ce fameux DWORD pour développer mon script et ainsi déchiffrer les IP automatiquement.
Entre temps, je me suis renseigné et je suis tombé sur un tweet (dont je n’ai pas le lien malheureusement) qui traitait des différents formats d’IP qu’un navigateur comprenait. Dans ces dites formes, on retrouve l’héxadécimal :
Par exemple, on peut accéder à Google avec cette URL :
https://8efb2524
J’ai entré un des DWORD dans mon navigateur pour voir ce que ça donne, et effectivement il s’agissait bien d’une IP. J’ai donc analysé une fonction permettant de retranscrire du hex vers du décimal afin d’être compréhensible par WinHttp..
Shellcode
En cherchant où les IP étaient stockées, je suis remonté jusqu’a cette fonction. Il s’agit d’un switch qui prend en paramètre un chiffre, qui s’avère être un compteur pour décider quelle IP sera choisie, ainsi qu’une page mémoire pour stocker le DWORD représentant l’IP.
Les valeurs sont récupérées sur la stack au prologue de la fonction. A partir de là, je n’ai pas compris d’ou venaient ces IP.
Je remarque ensuite que la DLL “unpackée” contient déjà les IP dans ses données. j’ai donc cherché comment elles étaient écrites.
J’ai ensuite mis un breakpoint en accès à l’endroit ou les IP ont été écrites. C’est alors que je suis tombé sur un Shellcode.
Il est responsable de déchiffrer du contenu pour unpacker la DLL, dans ce contenu, on retrouve les IP nécessaires pour contacter le serveur C2.
En tentant de comprendre ce Shellcode, je ne suis pas parvenu à comprendre quelles données étaient déchiffrées, comment, selon quels paramètres. Analyser celui-ci en détail m’aurait pris beaucoup trop de temps.
Echec
Je ne suis donc pas parvenu à terminer ce challenge. j’ai tout de même trouvé les IP dans le binaire et leur emplacement! En revanche, je n’ai pas trouvé de technique pour récupérer ces IP dans tous les samples Danabot.
Peut-être car j’ai perdu beaucoup de temps en négligeant le fait que la DLL se lançait une deuxième fois, en analysant le script VBS qui ne faisait pas partie du challenge ou encore en analysant la fonction permettant de convertir une IP dans sa forme hexadécimale vers sa forme CIDR..
Néanmoins, j’ai pris un plaisir fou à essayer de comprendre ce challenge qui m’aura donné beaucoup de fil à retordre. Un échec me fait comprendre que je suis encore loin de tout connaître dans le domaine, et me motive encore plus à apprendre davantage.
Pour la rentrée, je planifie une série de 3 lives dans lequel je m’amuserais à résoudre des CTF en lien avec le reverse engineering et l’analyse de malwares sur Twitch!