Overview
Elastic Security Labs continue de surveiller les menaces actives telles que GULOADER, également connu sous le nom de CloudEyE - un téléchargeur de shellcode évasif qui a été très actif pendant des années tout en faisant l'objet d'un développement constant. L'un de ces changements récents est l'ajout d'exceptions à son Vectored Exception Handler (VEH) dans le cadre d'une nouvelle campagne, ce qui ajoute encore plus de complexité à sa liste déjà longue d'astuces anti-analyse.
Bien que la fonctionnalité de base de GULOADER n'ait pas changé radicalement au cours des dernières années, ces mises à jour constantes des techniques d'obscurcissement font de l'analyse de GULOADER un processus long et gourmand en ressources. Dans ce billet, nous aborderons les sujets suivants lors du triage de GULOADER :
- Examen du shellcode initial et du processus de déballage
- Trouver le point d'entrée du shellcode décrypté
- Discutez de la mise à jour du VEH de GULOADER qui obscurcit le flux de contrôle.
- Fournir une méthodologie pour le rafistolage des VEH
Shellcode initial
Dans notre exemple, GULOADER est livré dans un programme d'installation NSIS (Nullsoft Scriptable Install System). Lorsque le programme d'installation est extrait, les principaux composants sont les suivants :
- Script NSIS - Ce fichier script décrit tous les aspects de la configuration et de l'installation.
- System.dll - Situé sous le site
$PLUGINSDir
. Ce fichier est déposé dans un dossier temporaire pour allouer/exécuter le shellcode GULOADER.
- Shellcode - Le shellcode chiffré est enfoui dans un dossier imbriqué.
Une méthodologie rapide pour localiser le fichier hébergeant le shellcode peut être réalisée en surveillant les événements ReadFile
à partir du Process Monitor de SysInternal après l'exécution de GULOADER. Dans ce cas, nous pouvons voir que le shellcode est lu à partir d'un fichier (Fibroms.Hag
).
GULOADER exécute le shellcode par le biais de rappels utilisant différentes fonctions de l'API Windows. Le principal objectif est d'éviter les détections centrées sur les API Windows traditionnelles utilisées pour l'injection de processus, telles que CreateRemoteThread
ou WriteProcessMemory
. Nous avons observé EnumResourceTypesA
et CallWindowProcW
utilisés par GULOADER.
En consultant la documentation MSDN pour EnumResourceTypesA
nous pouvons voir que le deuxième paramètre attend un pointeur vers la fonction de rappel. La capture d'écran ci-dessus montre que le shellcode nouvellement alloué est placé dans cet argument.
Trouver le point d'entrée principal du shellcode
Dans les échantillons récents, GULOADER a augmenté la complexité au début du shellcode initial en incluant un grand nombre d'instructions inutiles et de sauts. La rétro-ingénierie du téléchargeur peut nécessiter un long processus d'obscurcissement du code conçu pour briser le désassemblage et le flux de contrôle dans certains outils, ce qui rend frustrant le fait de trouver le début réel du shellcode GULOADER.
Une méthode pour trouver l'appel initial peut consister à exploiter la vue graphique dans x64dbg et à utiliser une approche de bas en haut pour rechercher l'instruction call eax
.
Une autre technique pour retracer le flux de contrôle initial consiste à tirer parti du cadre d'ingénierie inversée Miasm. Vous trouverez ci-dessous un exemple rapide dans lequel nous pouvons passer le shellcode et désassembler les instructions pour suivre le flux :
from miasm.core.locationdb import LocationDB
from miasm.analysis.binary import Container
from miasm.analysis.machine import Machine
with open("proctoring_06BF0000.bin", "rb") as f:
code = f.read()
loc_db = LocationDB()
c = Container.from_string(code, loc_db)
machine = Machine('x86_32')
mdis = machine.dis_engine(c.bin_stream, loc_db=loc_db)
mdis.follow_call = True
mdis.dontdis_retcall = True
asm_cfg = mdis.dis_multiblock(offset=0x1400)
Miasm coupe les instructions 142 jmp
et navigue à travers les instructions inutiles où nous l'avons configuré pour qu'il s'arrête sur l'instruction d'appel à EAX (adresse : 0x3bde
).
JMP loc_3afd
-> c_to:loc_3afd
loc_3afd
MOV EBX, EAX
FADDP ST(3), ST
PANDN XMM7, XMM2
JMP loc_3b3e
-> c_to:loc_3b3e
loc_3b3e
SHL CL, 0x0
PSRAW MM1, MM0
PSRLD XMM1, 0xF1
JMP loc_3b97
-> c_to:loc_3b97
loc_3b97
CMP DL, 0x3A
PADDW XMM3, XMM5
PXOR MM3, MM3
JMP loc_3bde
-> c_to:loc_3bde
loc_3bde
CALL EAX
La queue du miasme
GULOADER’s VEH Update
L'une des techniques phares de GULOADER est centrée sur sa capacité de traitement des exceptions vectorisées (VEH). Cette fonction permet aux applications Windows d'intercepter et de traiter les exceptions avant qu'elles ne soient acheminées par le processus d'exception standard. Les familles de logiciels malveillants et les applications de protection logicielle utilisent cette technique pour empêcher les analystes et les outils de suivre le code malveillant.
Le GULOADER commence ce processus en ajoutant le VEH à l'aide de RtlAddVectoredExceptionHandler
. Tout au long de l'exécution du shellcode GULOADER, du code est placé à dessein pour déclencher ces différentes exceptions. Lorsque ces exceptions sont déclenchées, le VEH vérifie la présence de points d'arrêt matériels. S'il n'est pas trouvé, GULOADER modifie l'EIP directement via la structure CONTEXT en utilisant une clé XOR d'un octet (change par échantillon) avec un décalage d'un octet à partir de l'endroit où l'exception s'est produite. Nous examinerons un exemple spécifique de cette technique dans la section suivante. Vous trouverez ci-dessous la décompilation de la VEH de notre échantillon :
Bien que cette technique ne soit pas nouvelle, GULOADER continue d'ajouter de nouvelles exceptions au fil du temps ; nous avons récemment observé ces deux exceptions ajoutées au cours des derniers mois :
EXCEPTION_PRIV_INSTRUCTION
EXCEPTION_ILLEGAL_INSTRUCTION
L'ajout de nouvelles exceptions à GULOADER peut entraîner la rupture d'outils utilisés pour accélérer le processus d'analyse des chercheurs.
EXCEPTION_PRIV_INSTRUCTION
Passons en revue les deux exceptions récemment ajoutées pour suivre le flux de travail de la VEH. La première exception (EXCEPTION_PRIV_INSTRUCTION
) se produit lorsqu'on tente d'exécuter une instruction privilégiée dans le jeu d'instructions d'un processeur à un niveau de privilège où elle n'est pas autorisée. Certaines instructions, comme l'exemple ci-dessous avec WRSMR, attendent des privilèges du niveau du noyau, de sorte que lorsque le programme est exécuté en mode utilisateur, il déclenche l'exception en raison de permissions incorrectes.
EXCEPTION_ILLEGAL_INSTRUCTION
Cette exception est invoquée lorsqu'un programme tente d'exécuter une instruction CPU non valide ou non définie. Dans notre exemple, lorsque nous rencontrons des instructions de virtualisation Intel telles que vmclear
ou vmxon
, cela déclenche une exception.
Lorsqu'une exception se produit, le code GULOADER VEH détermine d'abord quel code d'exception est à l'origine de l'exception. Dans notre exemple, si l'exception correspond à l'une des cinq catégories ci-dessous, le code suivra le même chemin.
EXCEPTION_ACCESS_VIOLATION
EXCEPTION_ILLEGAL_INSTRUCTION
EXCEPTION_PRIV_INSTRUCTION
EXCEPTION_SINGLE_STEP
EXCEPTION_BREAKPOINT
GULOADER vérifie ensuite s'il existe des points d'arrêt matériels en parcourant l'enregistrement CONTEXT qui se trouve dans la structure EXCEPTION_POINTERS. Si des points d'arrêt matériels sont trouvés dans les différents registres de débogage, GULOADER renverra une adresse 0
dans l'enregistrement CONTEXT, ce qui finira par faire planter le shellcode.
S'il n'y a pas de point d'arrêt matériel, GULOADER récupère un seul octet qui se trouve à 7 octets de l'adresse qui a causé l'exception. Si vous utilisez le dernier exemple avec vmclear
, vous obtiendrez l'octet (0x8A
).
Puis, à partir de cet octet, il effectue une opération XOR avec un autre octet codé en dur. Dans notre cas (0xB8
), il est unique par échantillon. Maintenant, avec un décalage dérivé 0x32
(0xB8 ^ 0x8A
), GULOADER modifiera l'adresse EIP directement à partir de l'enregistrement CONTEXT en ajoutant 0x32
à l'adresse précédente (0x7697630
) qui a causé l'exception, ce qui entraînera l'exécution du code suivant à partir de l'adresse (0x7697662
).
Avec différentes instructions inutiles entre les deux, et des exceptions répétées (nous avons compté 229 exceptions uniques dans notre échantillon), il n'est pas difficile de comprendre pourquoi cela peut briser différents outils et augmenter le temps des analystes.
Nettoyage des flux de contrôle
Pour faciliter le suivi du flux de contrôle, un analyste peut contourner le VEH en retraçant l'exécution, en enregistrant les exceptions et en corrigeant le shellcode à l'aide de l'algorithme de modification de l'EIP évoqué précédemment. Pour cette procédure, nous avons utilisé TinyTracer, un outil écrit par @hasherezade qui s'appuie sur Pin, un cadre d'instrumentation binaire dynamique. Cela nous permettra d'attraper les différentes adresses qui ont déclenché l'exception. Ainsi, en utilisant l'exemple ci-dessus avec vmclear
, nous pouvons voir que l'adresse était 0x7697630
, ce qui a généré une exception appelant KiUserExceptionDispatcher
, une fonction responsable de la gestion des exceptions en mode utilisateur.
Une fois toutes les exceptions collectées et filtrées, elles peuvent être transmises à un script IDAPython dans lequel nous parcourons chaque adresse, calculons l'offset en utilisant le 7e octet et la clé XOR (0xB8
), puis éliminons toutes les instructions générant des exceptions à l'aide de sauts courts.
L'image suivante est un exemple d'instructions de correction qui déclenchent des exceptions aux adresses 0x07697630
et 0x0769766C
.
Vous trouverez ci-dessous un graphique représentant le flux de contrôle avant l'application globale des correctifs. Notre bloc de base avec l'instruction vmclear
est surligné en orange. En mettant en œuvre le VEH, le GULOADER aplatit le graphique du flux de contrôle, ce qui rend plus difficile le repérage de la logique du programme.
Après avoir patché le VEH avec des instructions jmp
, il transforme les blocs de base en les reliant entre eux, ce qui réduit la complexité du flux du shellcode.
L'utilisation de cette technique peut accélérer le processus de nettoyage, mais il est important de noter qu'il ne s'agit pas d'une méthode infaillible. Dans ce cas, il reste encore une bonne quantité de code/fonctionnalité qui devra être analysée, mais cela permet de simplifier considérablement le code en supprimant le VEH. Le texte complet du POC est disponible ici.
Conclusion
GULOADER possède de nombreuses caractéristiques différentes qui peuvent perturber le désassemblage, entraver le flux de contrôle et rendre l'analyse difficile pour les chercheurs. Malgré cela et l'imperfection du processus, nous pouvons contrer ces caractéristiques par différents processus statiques ou dynamiques afin de réduire le temps d'analyse. Par exemple, nous avons observé qu'avec de nouvelles exceptions dans le VEH, nous pouvons toujours remonter jusqu'à elles et patcher le shellcode. Ce processus mettra l'analyste sur la bonne voie, plus proche de l'accès aux fonctionnalités de base de GULOADER.
En partageant une partie de notre flux de travail, nous espérons vous fournir de nombreuses informations si vous rencontrez GULOADER dans la nature. Sur la base des changements intervenus chez GULOADER, il est très probable que les comportements futurs nécessiteront des stratégies nouvelles et différentes. Pour la détection de GULOADER, la section suivante inclut les règles YARA, et le script IDAPython de ce billet se trouve ici. Pour des mises à jour sur les dernières recherches en matière de menaces, consultez notre section d'analyse des logiciels malveillants par l'équipe d'Elastic Security Labs.
YARA
Elastic Security a créé différentes règles YARA pour identifier cette activité. Vous trouverez ci-dessous un exemple de règle YARA permettant d'identifier GULOADER.
rule Windows_Trojan_Guloader {
meta:
author = "Elastic Security"
creation_date = "2023-10-30"
last_modified = "2023-11-02"
reference_sample = "6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99"
severity = 100
arch = "x86"
threat_name = "Windows.Trojan.Guloader"
license = "Elastic License v2"
os = "windows"
strings:
$djb2_str_compare = { 83 C0 08 83 3C 04 00 0F 84 [4] 39 14 04 75 }
$check_exception = { 8B 45 ?? 8B 00 38 EC 8B 58 ?? 84 FD 81 38 05 00 00 C0 }
$parse_mem = { 18 00 10 00 00 83 C0 18 50 83 E8 04 81 00 00 10 00 00 50 }
$hw_bp = { 39 48 0C 0F 85 [4] 39 48 10 0F 85 [4] 39 48 14 0F 85 [7] 39 48 18 }
$scan_protection = { 39 ?? 14 8B [5] 0F 84 }
condition:
2 of them
}
Observations
Toutes les observables sont également disponibles au téléchargement dans les formats ECS et STIX.
Les observables suivants ont été examinés dans le cadre de cette recherche.
Observable | Type | Nom | Référence |
---|---|---|---|
6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99 | SHA-256 | Windows.Trojan.Guloader | GULOADER downloader |
101.99.75[.]183/MfoGYZkxZIl205.bin | Url | NA | GULOADER C2 URL |
101.99.75[.]183 | ipv4-addr | NA | GULOADER C2 IP |