L'étude d'un malveillant

image

Je suis tombé sur un fichier doc récemment malveillant qui a été envoyé avec des e-mails de phishing. J'ai décidé que c'était une bonne raison de pratiquer l'ingénierie inverse et d'écrire quelque chose de nouveau sur Habr. Under the cat - un exemple d'analyse d'un compte-gouttes de macro malveillant et non moins de DLL malveillantes.

Macro


Sha256 du fichier - abb052d9b0660f90bcf5fc394db0e16d6edd29f41224f8053ed90a4b8ef1b519 . Dans le fichier doc lui-même, sur la première page, il y a une image informant que ce fichier est protégé et expliquant comment activer les macros; il y a deux grands tableaux avec des nombres dans le fichier. Les nombres sont écrits sous forme décimale, les plus longs sont à dix chiffres, ils sont à la fois positifs et négatifs.

Lorsque la victime autorise l'exécution de macros (il y a ceux qui l'ont activé par défaut?), Une chaîne d'actions est lancée, qui exécute finalement la fonction updatedb_network, qui change le répertoire actuel en un répertoire temporaire et y crée le fichier "icutils.dll", dans lequel il écrit les nombres de la première table consécutive sous la forme d'un entier 32 bits avec un signe. Le résultat est la bonne DLL. La fonction clone est importée de cette dll :

Declare PtrSafe Function clone Lib "icutils.dll" _
(ByVal Saved As String, ByVal Time As Integer) As Boolean

Et cela commence par deux paramètres:

R1 = Module1.clone("Cream", 0)

Si l'appel de clone renvoie False, le fichier icutils.dll est remplacé par les données de la deuxième table et le clone avec les mêmes paramètres est appelé à nouveau.

Pour l'avenir, je dirai que la première dll est en 64 bits et ne fonctionnera pas sur les systèmes 32 bits. Ainsi, la macro sélectionne l'architecture de code binaire correcte.

Fait intéressant, la fonction updatedb_network a un morceau de code qui n'a aucun objectif fonctionnel:

Sub updatedb_network()
...
Dim query_to_change As Variant
    Set query_to_change = CurrentDb.QueryDefs("query_name")
    
    query_to_change.SQL = "SELECT * FROM Table ORDER BY ID Asc"
    query_to_change.SQL = "SELECT Field1, Field2 FROM Table ORDER BY ID Asc"
    query_to_change.SQL = "SELECT Field1, Field2 FROM Table WHERE Field LIKE Fashion"
    query_to_change.SQL = "SELECT Field1, Field2 FROM Table WHERE Field LIKE '" & something & "'"
...
End Sub

Peut-être qu'il est là pour donner l'apparence d'un travail utile à ceux qui feuilletent rapidement le code, voient quelques lignes en SQL et pensent que tout va bien? Je ne sais pas. De plus, la plupart des fonctions et des variables ont des noms aléatoires ou non réels (tels que updatedb_network , qui n'interagit pas avec la base de données ou le réseau). Bien qu'il existe, par exemple, la fonction dump_payload , qui stocke 4 octets dans icutil.dll. Mais dans tous les cas, la présence de la fonction Document_Open devrait immédiatement alerter , les auteurs du malware ne peuvent pas le renommer arbitrairement (bien qu'ils puissent utiliser à la place une autre fonction lancée automatiquement).

Ainsi, la fonctionnalité de macro est plus ou moins claire, il est temps de décharger la DLL et de procéder à leur analyse.

Première dll


La première dll (sha256 7427cc4b6b659b89552bf727aed332043f4551ca2ee2126cca75fbe1ab8bf114 ) 64 bits.

La liste des fonctions importées contient les fonctions CreateProcessW (démarrage du programme), CreateRemoteThread (création d'un thread dans un autre processus), VirtualAllocEx (allocation d'un bloc de mémoire dans un autre processus), WriteProcessMemory (écriture dans la mémoire d' un autre processus), ce qui suggère immédiatement l'injection de code dans un autre processus. Voyons maintenant ce qu'elle fait exactement avec IDA Free et Ghidra.
Le point d'entrée principal renvoie simplement 1, ne fait rien d'autre. La deuxième fonction exportée est clone, qui est appelée par la macro et contient du code malveillant.
Les paramètres avec lesquels il est appelé ne semblent rien affecter. L'application déchiffre deux blocs de données. Le premier bloc de données 0x78 avec le contenu suivant (déjà déchiffré):

https://pastebin.com/raw/Jyujxy7z\x00\x00\x00\x00\x00\x00\x00\xf2i\xe0\x1d\x95h\xbc\x03\xe4#\xe0\x1d<\x04\xe0\x1d\xe6\x00\xde\x01\xa4\x17\xbc\x03x\x01\xe0\x1d\xe2\x16x\x07Qy\xbc\x03@Fx\x07Df\xbc\x03\x89a\xde\x01q\x11\xe0\x1d|Ix\x07D@\xbc\x03\x8a\x01\xde\x01^9\xde\x01\xf2i\xe0\x1d\x95h\xbc\x03\xe4#\xe0\x1d\xab

Le deuxième bloc de données a une longueur de 0x1D4C et contient du code exécutable.

De plus, un pointeur vers le module kernel32, le résultat de la fonction GetTickCount () et l'adresse de la fonction ZwDelayExecution () de kernel32 sont écrits dans la structure d'octets 0x90 .

Ensuite, le processus ( CreateProcessW ) «cmd.exe» est créé. En utilisant VirtualAllocEx , deux tampons y sont alloués: avec des autorisations RW d'une longueur de 0x108 et avec des autorisations RWX d'une longueur de 0x1D4C. Le tampon RW copie le bloc de données ci-dessus et la structure susmentionnée avec une longueur de 0x90. La structure écrit également un pointeur sur le bloc de données déchiffré (dans l'espace d'adressage du processus enfant (cmd.exe)). Dans RWX, le tampon est copié ( WriteProcessMemory) bloc de données déchiffré avec code.
Ensuite, dans le processus cmd.exe, un flux ( CreateRemoteThread ) est créé avec un point d'entrée au début du tampon RWX, un pointeur vers le tampon RW est passé en argument. Ceci termine la fonction de clonage, l'action se poursuit dans le processus cmd.exe.

Fait intéressant, la fonction de clonage semble être comme un morceau de code inaccessible qui importe ( LoadLibraryW ) la bibliothèque " WorkPolyhistor ".

Le code injecté dans cmd.exe


Il effectue les actions suivantes:

  • trouve les adresses des fonctions nécessaires à partir de kernel32.dll (son adresse est obtenue à partir du processus parent)
  • charge les bibliothèques ntdll , Ole32 , User32
  • Recherche les adresses des fonctions nécessaires dans ces bibliothèques.

Il est intéressant de noter que pour la plupart des fonctions du code, il n'y a pas de nom de fonction, mais uniquement CRC32 au nom de (tous les noms de fonction de la bibliothèque chargée sont recherchés jusqu'à ce qu'il y ait une fonction avec le CRC32 souhaité au nom de). Il s'agit peut-être d'une protection contre l'obtention de la liste des fonctions importées par l'utilitaire de chaînes, bien qu'il soit étrange que le code stocké sous forme cryptée dispose d'une telle protection, tandis que la DLL elle-même importe les fonctions simplement par nom. Au total, les fonctions suivantes sont détectées:

kernel32:

  • GetProcAddress
  • Bibliothèque de chargement
  • Globalalloc
  • GetTempPath
  • GetFileAttributesW
  • CreateProcessW
  • Globalfree
  • GlobalRealloc
  • Fichier d'écriture
  • CreateFileW (trouvé par son nom)
  • Fichier d'écriture
  • Closehandle
  • Gettickcount
  • ReadFile
  • GetfileSize

ntdll:

  • RtlCreateUnicodeStringFromAsciiz
  • ZwDelayExecution
  • ZwTerminateProcess
  • swprintf

ole32:

  • CoInitialize (trouvé par son nom)
  • CoCreateInstance (trouvé par son nom)

msvcrt:

  • rand
  • srand

Ensuite, le processus utilisant GetTempPath obtient le chemin d'accès au répertoire temporaire, y crée un fichier avec un nom de la forme 26342235.dat, où le nom de fichier est l'enregistrement décimal de TickCount reçu du processus parent et itère à chaque nouvelle tentative (c'est-à-dire s'il n'était pas possible de télécharger la charge utile à la première tentative, puis à la deuxième tentative, un fichier sera créé avec le nom 26342236.dat). Après cela, la bibliothèque wininet est chargée et il existe des pointeurs vers les fonctions suivantes:

  • InternetCloseHandle (crc32: 0xe5191d24)
  • InternetGetConnectedState (crc32: 0xf2e5fc0c)
  • InternetOpenA (crc32: 0xda16a83d)
  • InternetOpenUrlA (crc32: 0x16505e0)
  • InternetReadFile (crc32: 0x6cc098f5)

En utilisant InternetGetConnectedState, il vérifie s'il y a un réseau, sinon, l'application appelle la fonction à une adresse inexistante et se bloque (une telle protection contre la détermination de l'adresse d'où la charge utile est obtenue en utilisant une machine isolée du réseau. C'est le seul cas où l'application se termine anormalement, dans le reste 3 tentatives sont faites , après quoi cmd.exe se termine à l'aide de ZwTerminateProcess ). S'il existe un réseau, à l'aide des fonctions trouvées, la charge utile est téléchargée à partir de l'URL transmise par le processus parent (https://pastebin.com/raw/Jyujxy7z) et enregistrée dans le fichier créé précédemment avec l'extension .dat.

Ensuite, la charge utile est lue à partir du fichier .dat, décodée (base64), décryptée à l'aide de XOR avec CRC32 à partir de l'URL, vérifiez que les 2 premiers octets des données décryptées sont 'MZ', si c'est le cas, le résultat est enregistré dans un fichier avec le même nom mais l'extension. EXE

Code de déchiffrement Python
from binascii import crc32
from base64 import b64decode


def decrypt_payload(payload_b64: bytes, url: str):
    payload_bin = b64decode(payload_b64.decode())
    key = str(crc32(url.encode())).encode()
    decrypted = bytearray()
    for i, b in enumerate(payload_bin):
        decrypted.append(b ^ key[i % len(key)])
    return bytes(decrypted)

À l'aide de la fonction CreateProcessW , le fichier enregistré est lancé. Si tout se passe bien, le processus cmd.exe se ferme à l' aide de ZwTerminateProcess . Si quelque chose ne va pas (sauf pour l'absence de réseau), alors tout est répété à nouveau, un maximum de 3 tentatives sont faites, les noms des fichiers dat et exe sont augmentés de 1 à chaque fois.

Deuxième dll


La deuxième dll (sha256 006200fcd7cd1e71be6c2ee029c767e3a28f480613e077bf15fadb60a88fdfca ) 32 bits.

Dans ce document, la principale fonctionnalité malveillante est implémentée dans la fonction de clonage . Il déchiffre également 2 tampons. Le premier tampon a une taille de 0x78 (120) octets, ces données sont déchiffrées et écrites dessus (sous forme déchiffrée):

https://pastebin.com/raw/Jyujxy7z\x00\x00\x00\x00\x00\x00\x00\x1e(\xf0\x0e\xc5r\xc0;\x12)\xc0;Jr\xc0;Y4\xbc\x03/Mx\x07\x038\xde\x01\x9e\x05\xe0\x1d#\x08\xbc\x03\xeeU\xf0\x0e\x18{x\x078\x1a\xf0\x0e\xccg\xf0\x0eze\xde\x01\x89&\xe0\x1d\xf6\x1f\xe0\x1d

On peut voir qu'au début c'est la même URL que dans la version x64.

Un deuxième tampon de taille 0x4678 octets est alloué avec des autorisations RWX. Le code y est décrypté, après quoi une fonction est appelée à partir de celui-ci avec un décalage de 0x4639 depuis le début du tampon.

Je n'ai pas analysé ce code en détail. Il trouve également des fonctions sur CRC32, lance notepad.exe, y injecte du code qui télécharge la charge utile à partir de la même URL sur pastebin.

Charge utile avec pastebin


La charge utile décryptée avec pastebin est un fichier exe 32 bits (sha256 9509111de52db3d9a6c06aa2068e14e0128b31e9e45ada7a8936a35ddfaf155f ) Je ne l'ai pas encore démonté en détail par manque de temps.

Conclusion


En général, le malware m'a impressionné assez mal écrit, avec beaucoup d'erreurs (je ne les appelle pas ici intentionnellement). Comme s'ils fouettaient.
dll trouve dans la mémoire des fonctions qui ne seront ensuite utilisées nulle part. Peut-être que ce code, comme une macro, est régulièrement réécrit par des cybercriminels chaque fois qu'il commence à être détecté par des antivirus, à la suite de quoi de tels artefacts restent dans le code.

Il est également intéressant de noter que dans la version «dll» de la dll «déposée» sur le disque x64, les noms des fonctions importées «dangereuses» ne sont pas masqués (vous pouvez au moins les voir sous forme de chaînes), et dans le code qui est déchiffré en mémoire et ne tient pas sur le disque, ils sont situés sur CRC32 à partir de nom, pas seulement par son nom.

La charge utile pastebin a été supprimée quelques jours plus tard.

PS KDPV pris d'ici

All Articles