Questa lezione introduce le tecniche necessarie a iniettare del codice all’interno dei processi del sistema. Il mio codice effettua un’enumerazione dei processi attivi nel sistema e verifica se si tratta di un processo iniettabile e che quindi è possibile compromettere.
Le WINAPI utilizzate nell’enumeratore sono le seguenti: CreateToolhelp32Snapshot, Process32First, CloseHandle, OpenProcess e Process32Next. Di seguito l’analisi dettagliata delle singole API.
Esegue un’istantanea dei processi specificati, degli heap, dei moduli e dei thread utilizzati. Nel nostro caso utilizzeremo come parametro dwFlags TH32CS_SNAPPROCESS che includerà nell’enumerazione tutti i processi di sistema. Tale API presuppone l’uso di Process32First e Process32Next per lo scroll dei processi.
HANDLE CreateToolhelp32Snapshot(
[in] DWORD dwFlags,
[in] DWORD th32ProcessID
);
Recupera le informazioni sul primo processo individuato nell’istantanea del sistema (CreateToolhelp32Snapshot). Il puntatore di struttura PROCESSENTRY32 conterrà le informazioni sul processo come il nome del file eseguibile, il PID (identificatore numerico del processo) e il PID del processo padre.
BOOL Process32First(
[in] HANDLE hSnapshot,
[in, out] LPPROCESSENTRY32 lppe
);
Chiude l’handle di un oggetto aperto. Nel nostro caso è essenziale per distruggere lo snapshot.
BOOL CloseHandle(
[in] HANDLE hObject
);
Apre un processo esistente mediante il suo PID. L’uso del parametro dwDesiredAccess nel nostro caso richiede il diritto di accesso PROCESS_ALL_ACCESS: Tutti i possibili diritti di accesso per un oggetto di processo. Se l'apertura dell’oggetto avverrà con tali diritti, avremo la certezza che si tratti di un processo iniettabile.
HANDLE OpenProcess(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwProcessId
);
Diversamente da Process32First, Process32Next ci permette di recuperare le informazioni sul processo successivo registrato nell'istantanea di sistema. Anche in questo caso, l’uso del puntatore PROCESSENTRY32 è fondamentale per accedere alle informazioni dei processi.
BOOL Process32Next(
[in] HANDLE hSnapshot,
[out] LPPROCESSENTRY32 lppe
);
Per prima cosa è necessario costruire le struct e i vector necessarie a contenere le informazioni legate ai processi iniettabili ricavate dall’enumerazione.
typedef struct process_info {
DWORD PID;
std::wstring wszProcessName = L"";
BOOL injectableProcess = FALSE;
} PROCESS_INFO, * LPPROCESS_INFO;
typedef std::vector<PROCESS_INFO> PROCESS_VECTOR;
La struttura conterrà l’identificativo del processo (PID), il nome del processo è un valore booleano che identifica se un processo è iniettabile.
Per prima cosa catturiamo un’istantanea dei processi di sistema con l’API CreateToolhelp32Snapshot.
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapShot == NULL) return 0;
Da notare l’uso del parametro TH32CS_SNAPPROCESS che ci permetterà di includere nell’enumerazione anche i processi sistema. Prima di richiamare l’API Process32First è necessario inizializzare un PROCESSENTRY32 che conterrà le informazioni sui processi.
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
Ora tramite Process32First otteniamo il primo processo nell’istantanea.
if (!Process32First(hSnapShot, &pe32))
{
CloseHandle(hSnapShot);
return 0;
}
Se non vi saranno errori con l’API, il processo di enumerazione continuerà con lo scroll dello snapshot attraverso un’iterazione post-condizionale che terminerà solo quando tutti i processi presenti nello snap saranno stati elaborati.
do
{
PROCESS_INFO pi;
pi.PID = pe32.th32ProcessID;
pi.wszProcessName = pe32.szExeFile;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
if (hProcess != NULL)
{
pi.injectableProcess = TRUE;
CloseHandle(hProcess);
lpaProcessInfo->push_back(pi);
}
} while (Process32Next(hSnapShot, &pe32));
Ora richiamiamo nel main la funzione creata:
int main()
{
PROCESS_VECTOR pvProcesses; //Inizializziamo il vettore che conterrà le informazioni relative ai soli processi iniettabili.
FindInjectableProcesses(&pvProcesses); // Richiamiamo la nostra funzione
return 0;
}
Come potete vedere dal seguente screen, nella mia macchina ci sono 73 processi iniettabili. Tali processi potrebbero per esempio essere utilizzati per l’iniezione di librerie malevole (DLL Injection) o di uno shellcode, permettendo quindi al malware di infettare il sistema e ottenere, per esempio, il privilege escalation.