Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
ShellGhost — Une technique d'évasion basée sur la mémoire qui rend le shellcode invisible du début à la fin du processus. | Kitploit
Outils/GitHubGitHub/lem0nsec/shellghost
Outils de Chiffrement/DéchiffrementCriminalistique MémoireExploitationShellcodeAnalyse de BinairesRed TeamingDéveloppement de Charges Utiles
GitHublem0nsec/shellghost

ShellGhost

Une technique d'évasion basée sur la mémoire qui rend le shellcode invisible du début à la fin du processus.

Voir le dépôt
1.2k140il y a 2 ansVérifié par Kitploit

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

ShellGhost

Une technique d'évasion basée sur la mémoire qui rend le shellcode invisible du début à la fin du processus.


Motivation

Je souhaitais partager cette POC d'auto-injection de shellcode pour présenter certains concepts d'évasion AV/EDR qui pourraient s'avérer utiles pour le Red Teaming. Il y a quelques semaines, j'ai mis au point une technique d'évasion mémoire personnalisée que j'ai nommée ShellGhost. Cette technique découle du besoin d'avoir un code qui exécute un shellcode 'invisible' du début à la fin du processus.


Gestion du flux d'exécution du thread

ShellGhost repose sur la gestion des exceptions vectorisées (Vectored Exception Handling) combinée à des points d'arrêt logiciels pour arrêter cycliquement l'exécution du thread, remplacer le point d'arrêt exécuté par une instruction de shellcode chiffrée en RC4, déchiffrer l'instruction et reprendre l'exécution après avoir restauré la protection mémoire en RX. Lorsque l'EXCEPTION_BREAKPOINT suivant est déclenché, le gestionnaire d'exceptions remplace l'instruction du shellcode précédent par un nouveau point d'arrêt afin que l'allocation ne révèle jamais le shellcode complet dans un état non chiffré. Cela se produit à l'intérieur d'une page mémoire privée initialement marquée en LECTURE/ÉCRITURE. Avoir une allocation PRV RW ne sera pas considéré comme un 'Indicateur de Compromission' par les scanners mémoire tels que PE-Sieve et Moneta. Lorsque l'allocation devient RX et que la page est analysée, seuls des points d'arrêt seront trouvés. Cela se produit alors que le shellcode est réellement en cours d'exécution. L'image suivante montre qu'un reverse shell est en cours d'exécution, mais qu'aucun IOC n'est trouvé par Moneta (autre que le binaire non signé).

Tenter d'analyser le processus avec Pe-Sieve donne un résultat encore meilleur :


Mappage du shellcode

Le mappage du shellcode est la fonctionnalité centrale de ShellGhost. Cette tactique permet au thread d'exécuter des instructions par intermittence sans jamais exposer l'intégralité du shellcode en mémoire. Cela est possible car la position de chaque instruction individuelle du shellcode que le thread exécute correspond à la position d'un certain point d'arrêt à l'intérieur de la page mémoire allouée. ShellGhost résout cette position en calculant l'adresse virtuelle relative (RVA) depuis le RIP du thread jusqu'à l'adresse de base de la page mémoire allouée et l'ajoute à l'adresse de base du shellcode chiffré / des instructions chiffrées. Le nombre de points d'arrêt qui seront remplacés n'est pas toujours le même, mais varie en fonction du nombre d'opcodes dont chaque instruction a besoin pour être correctement générée et interprétée (QUOTA). Ainsi, par exemple, l'instruction 'POP RBP' est égale à '5D', ce qui signifie qu'un seul point d'arrêt sera remplacé. En revanche, l'instruction 'JMP RAX' nécessite les opcodes 'FF E0', donc deux points d'arrêt seront remplacés. Pour cette raison, j'ai créé la structure de données C suivante.

root@kitploit:~
typedef struct CRYPT_BYTES_QUOTA {

	DWORD RVA;		// offset to encrypted instruction 
	DWORD quota;	// number of opcodes that generate the instruction

} CRYPT_BYTES_QUOTA, * PCRYPT_BYTES_QUOTA;

Les points d'arrêt ne sont pas immédiatement remplacés par leurs instructions correspondantes. En effet, les instructions doivent subir une routine de déchiffrement avant d'être exécutées. C'est là que le DWORD quota entre en jeu. ShellGhost utilise la désormais populaire 'SystemFunction032' pour effectuer le déchiffrement RC4. Contrairement au XOR, RC4 n'est pas un schéma de chiffrement par octet unique. Cela signifie que le shellcode ne peut pas être chiffré et déchiffré en une seule fois. C'est également une autre raison pour laquelle chaque instruction est traitée séparément. Après le remplacement des points d'arrêt, la longueur du tampon dont SystemFunction032 a besoin sera égale au 'quota d'instruction', qui représente à nouveau le nombre d'opcodes dont l'instruction spécifique est composée. Ainsi, par exemple, considérez l'extrait suivant.

root@kitploit:~

CRYPT_BYTES_QUOTA instruction[200];
instruction[5].quota = 2

USTRING buf = { 0 }; 	// will contain the buffer to be decrypted and its length
USTRING key = { 0 }; 	// will contain the RC4 key and length

buf.Length = 2 		// buffer length, or length of the instruction to be decrypted

Nous savons que l'instruction du shellcode numéro 5 est composée de 2 opcodes, donc une longueur de tampon de 2 sera passée à SystemFunction032. Ceci est important car tenter de déchiffrer l'intégralité du shellcode avec un seul appel à SystemFunction032 le corrompra entièrement.

Comment le mappage du shellcode est-il effectué ?

Le shellcode doit être mappé avec ShellGhost_mapping.py avant la compilation. Le script extrait chaque instruction individuelle et la traite comme un petit shellcode indépendant. Les instructions sont chiffrées une par une et imprimées en format C ensemble sous forme de unsigned char. Le résultat peut être codé en dur dans le code C. Ci-dessous un exemple de ce à quoi ressemble un shellcode MSF chiffré pour calc.exe.

Ce shellcode contient 98 instructions, donc 98 structures CRYPT_BYTES_QUOTA sont déclarées. Lorsque le code s'exécute, ces structures doivent être remplies avec les RVA et QUOTA appropriés des instructions. Le paramètre '-1' indique au script de mappage d'imprimer le morceau de code qui effectue cette opération.

Ajustement des paramètres Winapi

Les shellcodes Metasploit x64 ont généralement des paramètres de chaîne Winapi stockés entre les instructions. Autrement dit, un shellcode MSF x64 qui appelle Winexec ne pousse pas une série d'octets avec un octet nul à la fin pour avoir la chaîne du premier paramètre sur la pile. Plutôt, le registre RCX (premier paramètre) est un pointeur à l'intérieur du shellcode lui-même, comme dans l'image suivante.

Cela signifie que les points d'arrêt dont la position est liée à la chaîne ne seront jamais résolus, car le RIP n'atteindra jamais cette position. En effet, ce code résout les instructions réelles du shellcode que le RIP traverse, pas les paramètres qui ne seront jamais exécutés comme des instructions. Pour résoudre ce problème, j'ai remarqué que les shellcodes MSF stockent toujours un pointeur vers l'API Windows qu'ils appellent dans le registre RAX, puis effectuent un saut vers le registre lui-même. Ainsi, lorsque ShellGhost VEH détecte que le point d'arrêt résolu est 'JMP RAX' et que le registre RCX contient un pointeur vers une position à l'intérieur du shellcode, il tente également de résoudre ce vers quoi pointe RCX. Ensuite, l'exécution n'est pas retournée à la mémoire allouée. Au lieu de cela, RAX (adresse de l'API) est copié dans RIP et l'exécution du thread reprend à partir de l'API, outrepassant ainsi le 'JMP RAX' et maintenant la mémoire allouée en RW. Ceci est nécessaire pour les reverse shells qui appellent WaitForSingleObject, ce qui ferait dormir le thread après le 'JMP RAX' tout en laissant la mémoire en RX aussi longtemps que le shell reste actif. L'extrait de code suivant contient les deux conditions qui doivent être remplies pour que ShellGhost ajuste le registre RCX lorsqu'il contient une chaîne de paramètre Winapi et permette au shellcode MSF d'effectuer correctement l'appel de fonction (WinExec dans l'exemple ci-dessous).

root@kitploit:~
<snip>
	
if (*(PWORD)exceptionData->ContextRecord->Rip == 0xe0ff) // if RIP is 'JMP RAX'

<snip>

if ((contextRecord->Rcx >= (DWORD_PTR)allocation_base) && (contextRecord->Rcx <= ((DWORD_PTR)allocation_base + sizeof(sh)))) // if RCX is inside the allocation

<snip>

RDX, R8 et R9 (deuxième, troisième et quatrième paramètres) ne sont pas encore couverts.

Différences et similitudes avec d'autres techniques

ShellcodeFluctuation est un concept d'évasion mémoire très similaire. Comme lui, la mémoire allouée 'fluctue' ici de RW à RX. En revanche, ShellGhost introduit les améliorations suivantes :

  • Chiffrement RC4 plus 'Mappage de shellcode' plutôt que XOR sur un seul octet
  • Pas besoin de hooker des fonctions
  • Prise en charge des shellcodes Metasploit

ShellGhost est loin d'être une technique parfaite. Elle souffre encore du plus grand inconvénient que toutes ces techniques ont, à savoir la nécessité d'avoir une mémoire exécutable privée à un moment donné de l'exécution. Des techniques plus avancées comme Foliage ont déjà trouvé une solution à cela. De plus, une allocation mémoire pleine de points d'arrêt logiciels peut être détectée par une règle YARA. L'image suivante montre Moneta détectant correctement un IOC pour l'allocation PRV RX.

En ce qui concerne l'évasion d'une solution EDR, l'analyse mémoire ne fait partie que d'un tableau plus large. L'absence totale d'IOC ne signifie pas nécessairement qu'un binaire utilisant cette technique sera efficace contre un EDR donné. Pour autant que je sache, j'ai rencontré des situations où la solution ne vous permet même pas de lancer le binaire de la manière dont vous le faites. L'autre côté de la médaille est que les IOC ne sont pas toujours des indicateurs précis, et certains d'entre eux peuvent s'avérer être des faux positifs. Cela dit, ce n'est qu'une technique brute et une inspiration que j'espère que le lecteur appréciera. Le Red Teamer sait que, tout comme les composants d'un EDR, l'évasion mémoire n'est qu'un composant du moteur.

Notes

La compilation nécessite la désactivation de l'édition de liens incrémentielle. Ce projet VS a déjà toutes les options du compilateur/éditeur de liens définies.

Télécharger l’outil