Pirates Corporation & Co.


> Hop là ho ! une bouteille de rhum...

#

Emotet v5 Reversing (unpacking, basic analysis and Indiana Jones) [part. I]


Emotet est un cheval de Troie bancaire qui a été identifié pour la première fois en 2014 par des chercheurs en sécurité. Il était conçu à l’origine comme un malware bancaire qui tentait de pénétrer de manière sournoise dans votre ordinateur pour voler des informations sensibles et privées. Les versions suivantes du logiciel ont vu l’ajout de services de diffusion de spam et de malware, y compris d’autres chevaux de Troie bancaires.

Emotet a ciblé des individus, des entreprises et des entités gouvernementales d’un bout à l’autre des États-Unis et de l’Europe, en volant des identifiants bancaires, des données financières et même des portefeuilles Bitcoin.

Une attaque notable d’Emotet sur la ville d’Allentown, en Pennsylvanie, a nécessité une aide directe de l’équipe de réponse aux incidents de Microsoft. Le nettoyage et la maintenance auraient coûté plus de 1 million de dollars.

Maintenant qu’Emotet sert à télécharger et à diffuser d’autres chevaux de Troie bancaires, la liste des cibles est potentiellement encore plus importante. Les premières versions d’Emotet étaient utilisées pour attaquer les clients des institutions bancaires en Allemagne. Les versions suivantes ciblaient des organisations au Canada, au Royaume-Uni et aux États-Unis (source: Malwarebytes).

Ainsi, par un hasard un peu forcé par une soudaine envie d’explorer un maldoc, je me suis rendu sur le site Hybrid Analysis dans l’espoir de trouver un sample de quelque chose … à analyser.

Concernant ce sample, je commence l’analyse en sachant que je dispose déjà d’informations intéressantes fournies dans le rapport d’incident lié à celui-ci.

Hybrid Analysis utilise Falcon Sandbox, une alternative commerciale à Cuckoo Sandbox.

Ci-dessous, une présentation de quelques indicateurs de la section Malicious Indicators.

Dans la section Informative comme son nom l’indique …

Dans la section Screenshots, nous pouvons observer en image, l’exécution du maldoc.

A propos de Cuckoo Sandbox, c’est un outil qui permet l’analyse automatisée de malwares et ainsi la compréhension rapide de ce que peut faire un fichier suspect (binaire, document, email, …) sur différentes plateformes (Windows, Linux, Android).

Vous pouvez lire le magazine e-Forensics du mois de mars 2019 dédié à Cuckoo Sandbox.

Il est toujours intéressant de pouvoir disposer d’un tel outil/environnement d’analyses comportementales dans une entreprise (je dis ça, mais j’dis rien …).

Mais, si nous devons faire tout ce travail à la main, il faut commencer par rechercher la charge utile dans le document Microsoft Word.

Pour faire ceci, nous pouvons utiliser l’outil oledump.py de Didier Stevens pour regarder dans les sections de ce document.

A la recherche de codes VBA, d’une commande PowerShell, WMI, … ou toutes autres choses paraissant suspectes.

Ici, avec l’habitude, nous reconnaissons l’encodage base64 et les chaînes de caractères PowE et rShell. Nous supposons l’exécution d’une commande PowerShell.

Puis on regarde le payload (ce qu’il y a d’encoder).

Nous découvrons la liste des liens qui cachent le malware Emotet :

Nous téléchargeons les 5 échantillons (les binaires ne sont peut-être pas identiques). Puis on vérifie le condensat SHA256 pour chaque échantillon.

Nous remarquons que c’est le même hash, mais aussi que la plupart des sites hébergeant cette variante d’Emotet utilisent le CMS WordPress.

Sans doute ont-ils une vulnérabilité commune ?

J’ai pu observer quelques CVEs sur Shodan à propos des serveurs ci-dessus et des références à WooComerce avec Wappalyzer.

Aussi, je suppose que ce sont des serveurs légitimes qui se sont fait poutrer via un upload de fichiers arbitraires, de l’exécution de codes ou autres …


Binary reversing / Unpacking

Si nous regardons de plus près le binaire récupéré, nous observons qu’il contient seulement 2 sections.

Avec IDA (Interactive DisAssembler) le constat n’est pas glorieux.

Il faudra sans doute user de l’analyse dynamique pour faire sortir Emotet de son tombeau.

Ainsi avec l’aide de x32dbg, nous pouvons observer l’initialisation du binaire. Par la suite, à l’adresse 0x0040257B, la fonction VirtualAllocEx est appelée pour allouer de la place dans la mémoire.

C’est dans cet espace tampon que sera stocké le code qui servira à unpacker Emotet.

Dans l’exemple ci-dessous, le registre EAX contient le point d’entrée de la plage mémoire allouée à l’adresse 0x001B0000 (dans mon cas).

L’exécution continue donc à l’adresse 0x001B0000. C’est ici que le processus d’unpacking commence.

Plus loin, le chargement des sections .data, .rdata et .reloc.

Puis il quitte l’espace mémoire avec l’instruction JMP ESI qui n’est autre qu’un saut vers l’OEP (Original Entry Point).

A ce stade, il est possible de reconstruire le PE non packé (avec le plugin Scylla) pour mieux l’étudier.

File > Dump Memory > Dump PE

Une fois le dump effectué, on peut quitter le debugger et vérifier le nouveau binaire avec (PEiD ou Detect It Easy) et PeStudio.

Avec IDA, nous obtenons une représentation plus cohérente de cette souche d’Emotet.

Du coté des antivirus, les taux de détection sont encore bien bas …


Loading Emotet

Première partie

Une fois unpacké, ce n’est pas gagné pour autant. En effet, le code est obfuscé (les chaînes de caractères sont cryptées, les noms des APIs sont stockés sous forme de valeurs de hachage dans le binaire).

Par exemple, le code ci-dessous est utilisé pour obtenir certaines fonctions de ntdll.dll.

En observant avec x32dbg, nous constatons qu’Emotet énumère via le PEB (Process Environment Block), en comparant les hashs sdbm respectifs de ntdll.dll et de kernel32.dll, pour obtenir un handle vers ces librairies.

Plus tard, nous observerons que les autres DLL sont chargées tout simplement en utilisant la fonction LoadLibraryW (en déchiffrant préalablement le nom de la DLL).

#!/usr/bin/env python2.7
 
string = "ntdll.dll"
 
hash = int(0)
for i in range(0, len(string)) :
  hash = (ord(string[i]) + hash) & 0xffffffff
  if i != (len(string) - 1) : hash = (hash * 65599) & 0xffffffff
 
print hex(hash)

Pour résoudre les APIs, c’est la même fonction de hashage qui est utilisée, avec un XOR en plus. Notons que la clé XOR est propre à chaque librairie.

#!/usr/bin/python

def sdbm_hash(string, key = 0) :
  hash = int(0)
  for i in range(0, len(string)) :
    hash = (ord(string[i]) + hash) & 0xffffffff
    if i != (len(string) - 1) : hash = (hash * 65599) & 0xffffffff
  return(hex((hash ^ key) & 0xffffffff)[2:])

def hash_comp(path_to_file, hash, key) :
  with open(path_to_file) as fp :
    line = fp.readline()
    while line :
      if sdbm_hash(line.strip(), key).upper() == hash :
        print("[%s] -> %s" % (hash, line.strip()))
      line = fp.readline()

def resolve_api(path_to_hash, path_to_modules, key) :
  with open(path_to_hash) as fp :
    line = fp.readline()
    while line :
      hash_comp(path_to_modules, line.strip().replace('h', '').upper(), key)
      line = fp.readline()

print("-= ntdll.dll =-")
resolve_api("modules.hash/ntdll.txt", "modules.list/ntdll.txt", 0x571BDDC1)

print("-= kernel32.dll =-")
resolve_api("modules.hash/kernel32.txt", "modules.list/kernel32.txt", 0x2068f5EB)

Ainsi, lors de son exécution, Emotet charge les fonctions dont il a besoin de cette façon.

Nous remarquons au passage que certaines valeurs de hash sont fausses. Peut-être pour tromper l’analyste ?

Ensuite, il fait un GetModuleFileNameW pour obtenir le chemin complet de son emplacement sur le disque, puis il utilise sa fonction de hashage sur le path obtenu.

Nous remarquons le call à l’adresse 0x001ACA10 (attention tenir compte de ASLR) qui appelle une fonction qui sert à déchiffrer des données. Dans le cas ci-dessous, elle déchiffre la chaine --%x qui sera utilisée en tant qu’argument pour le formatage dans la fonction _snwprintf.

Donc, vient l’appel à la fonction _snwprintf qui permet d’écrire des données formatées dans une chaîne de caractères.

Par la suite, il fait un appel à GetCommandLineW pour obtenir la ligne de commande et ses arguments afin d’établir si le binaire est exécuté avec le commutateur --1e458dc6 (dans notre cas).

Car vous l’aurez compris, le commutateur correspond au hash du résultat obtenu par la fonction GetModuleFileNameW (chemin du binaire sur le disque).

Si l’argument n’existe pas, Emotet restart sur un appel à CreateProcessW et ajoute le commutateur désiré à la commande, puis ExitProcess.

De retour vers l’analyse statique, nous avons précédemment trouvé une fonction cryptographique (en fait, un simple XOR) qui est utilisée pour chiffrer des données (chaînes de caractères, valeurs de hash, …). Simple mais efficace pour faire de l’obfuscation.

La clé XOR est différente pour chaque chaîne de caractères (l’objectif remplit bien son rôle … ralentir le RE).

Comme expliqué plus haut, lors du chargement de ntdll.dll et kernel32.dll, Emotet énumère le PEB.

Pour les autres librairies, c’est un appel à LoadLibraryW qui fait le travail. Le nom de la librairie est déchiffré préalablement.

Nous savons comment reverser la fonction de crypto, nous pouvons nous servir de ces informations pour décrypter les chaînes de caractères dans le programme.

L’objectif est de trouver le(s) adresse(s) IP de communication vers le (C2C ou Relay).

Notre attention est retenue par la chaîne de caractères %u.%u.%u.%u qui est compatible avec le format d’une adresse IP.

Nous localisons la fonction qui fait un traitement sur cette valeur.

Mettons cette information de côté et poursuivons…


Seconde partie

Nous relançons le binaire en prenant soin d’ajouter le bon commutateur --5c0f3203 à la ligne de commande.

A l’adresse 0x012FCA79 (w/ ASLR), le commutateur --5c0f3203 dans la ligne de commande est trouvé. C’est la fonction lstrcmpiW qui se charge d’effectuer cette comparaison.

Grosso merdo, lors de cette seconde exécution, Emotet met en place sa persistance en se recopiant dans son home directory. S’il est exécuté avec des privilèges d’administration, le malware s’installe dans le répertoire %windir%\SysWOW64. Sinon il se positionne dans %localappdata%.

En se recopiant, il génère un nouveau nom d’exécutable. Je n’ai pas analysé ce passage en détail mais en gros, il semblerait qu’il utilise deux listes de noms qu’il concatène en utilisant comme vecteur, une valeur de taille fixe (par exemple, le hostname mais ce n’est qu’une supposition) pour choisir le nom de l’exécutable dans son répertoire de destination (son home directory).

Juste avant d’effectuer ce travail, il charge les librairies advapi32.dll et shell32.dll pour pouvoir faire appel à des fonctions supplémentaires.

Dans mon cas, le malware se nomme sonicredist.exe et son emplacement potentiel sur le disque est :

Emotet vérifie s’il est déjà installé dans son home directory. Si ce n’est pas le cas, il déclenche ses procédures de persistance et restart au bon emplacement.

Le nom du service est choisi de la même façon mais avec quelques différences, en utilisant une seule et unique liste de noms (différente de celle utilisée précédemment), et donc ici, pas de concaténation avec une autre liste.


Troisième partie

S’il est localisé dans son home directory avec le nom d’exécutable attendu (dans mon cas %windir%\SysWOW64\sonicredist.exe) et s’il est exécuté à cet emplacement en utilisant le commutateur --15fc565b, Emotet exécute ses routines cryptographiques, de communications et autres…

Il utilise les fonctions cryptographiques de Microsoft, à ce stade, les librairies ntdll.dll, kernel32.dll, advapi32.dll, shell32.dll, crypt32.dll, urlmon.dll, userenv.dll, wininet.dll et wtsapi32.dll sont chargées.

Mais notre objectif, dans le cadre de cet article, est de localiser l(es) adresse(s) IP qu’utilise Emotet pour communiquer.

Nous positionnons un break point à l’adresse 0x00AE68B0 (w/ ASLR), dans cette fonction, nous avons précédemment trouvé un appel à _snwprintf et la chaîne de formatage %u.%u.%u.%u qui fait fortement penser au format IPv4.

Donc, on positionne un autre break point plus bas à l’adresse 0x00AE6962 (w/ ASLR) pour découvrir la première adresse IP.

Puis une seconde adresse IP.

Et ainsi de suite…

Du coté de l’analyse statique avec IDA, nous obtenons la liste des adresses IP comme définie dans la représentation ci-dessous :

Cependant, il est difficile de savoir pour le moment si ces adresses IP correspondent à :


Conclusions

Bien que notre analyse nous permette de connaître le schéma d’exécution de cette version d’Emotet, il reste beaucoup à apprendre. En effet, certaines fonctions peuvent être revues plus en détail :

L’analyse manuelle est en mon sens, bien plus précise qu’une analyse automatisée avec une Sandbox. L’interaction entre le malware et l’analyste est beaucoup plus … intime.

Cependant, vous pouvez vous appuyer sur un outil comme CAPE (Malware Configuration And Payload Extraction) qui offre également la possibilité de soumettre sur leur plateforme en ligne.

Le lien du rapport CAPE de notre version d’Emotet se trouve ICI.