
Strumento per effettuare un attacco man in the middle in memoria
Lo strumento MemITM (Mem In The Middle) è stato sviluppato per intercettare facilmente i "messaggi" nella memoria dei processi Windows. Abbiamo sviluppato molti strumenti di intercettazione personalizzati della memoria per catturare messaggi di rete prima della crittografia, o messaggi IPC, e per poterli ispezionare o alterare per eseguire del fuzzing. Ogni strumento era molto specifico, non generico, implementato in C/ASM e non era facile da usare/mantenere/adattare.
Lo strumento MemITM è stato sviluppato per risolvere questi problemi e consiste in:
Puoi scaricarlo (sorgenti + compilato) qui: https://github.com/Amossys/MemITM
Supponiamo di voler intercettare le scritture su file (ci sono modi più semplici per farlo, ma è per l'esempio) in un processo Windows specifico. Le scritture su file vengono eseguite dalla funzione WriteFile, esportata da kernelbase.dll, e segue lo schema BOOL WriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, ...). WriteFile è una funzione __fastcall: lpBuffer è puntato da RDX e NumberOfBytesToWrite da R8. Intercettiamo questi buffer.
apri il tuo file "kernelbase.dll" in IDA Pro, carica lo script generate.idapython.py, e esegui semplicemente getHook(LocByName("WriteFile"), "rdx","r8") seguito da updateConfig("config.bin").
esegui un notepad e poi python memitm.py notepad.exe config.bin.
Questo è tutto! È stato posizionato un hook sulla funzione WriteFile, e le funzioni "logger" e "fuzzer" di memitm.py riceveranno una copia del buffer.
La funzione getHook permette di specificare i registri per il buffer e la sua dimensione, ma in alcuni casi potrebbe non essere così. Supponiamo di voler intercettare le syscall NtCreateFile e ottenere il nome del file. NtCreateFile segue lo schema NTSTATUS NtCreateFile( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, ...), e il nome del file può essere ottenuto tramite ObjectAttributes->ObjectName->Buffer (la dimensione è ObjectAttributes->ObjectName->Length * sizeof(WCHAR)).
getHook permette anche di specificare uno shellcode nel suo parametro t1customOpcodes invece dei registri. Questo shellcode deve:
RCX;RDX;Nel caso di NtCreateFile, possiamo eseguire questo codice ASM per fare il lavoro:
mov rax, [r8+0x10] ; rax ora è ObjectAttributes->ObjectName
mov rcx, [rax + 0x10] ; rcx ora è ObjectAttributes->ObjectName->Buffer
mov rdx, [rax] ; rdx ora è ObjectAttributes->ObjectName->Length
and rdx, 0xFFFF ; Length è un USHORT
shl rdx, 1 ; e deve essere *2
Assembliamolo (usando per esempio il sito web di disassemblaggio online): 498B4010488B4810488B104881E2FFFF000048D1E2. Ora la nostra chiamata getHook dovrebbe essere getHook(LocByName("NtCreateFile", t1customOpcodes="498B…E2").
Sì! Il file di config può contenere più definizioni di punti di intercettazione, e la funzione getHook aggiunge al file. Puoi impostare punti di intercettazione in più moduli. Ad esempio, puoi intercettare messaggi ALPC e IOCTL contemporaneamente.
Potrai differenziarli tramite il loro "ID messaggio" nei callback Python.
Non preoccuparti, c'è anche un semplice server HTTP (logserver.py) e la funzione httpNetSend che ti permettono di inviare i tuoi casi di test (e le modifiche) a un host remoto in tempo reale.
Beh, puoi iniziare usando la funzione bufferBitFlip per... capovolgere alcuni bit. La registrazione dei messaggi grezzi ti permetterà di scrivere il tuo dissector e iniziare a implementare manualmente un fuzzer migliore :). Ricorda: non puoi cambiare la dimensione del buffer!
Ad esempio, nel nostro esempio WriteFile, la seguente funzione fuzzer sostituirà "hello" con "world":
def fuzzer(data, msgID=None, pid = 0):
return data.replace("hello","world")
Ad esempio, per i messaggi ALPC, potresti avere la seguente configurazione memitm.py:
def logger(data, msgID=None, pid=0):
totalLen = 0
dataLen = 0
typ = 0
dataInfoOffset = 0
cid = 0
tid = 0
messageID = 0
clientViewSize = 0
# skip the PORT_MESSAGE header (0x18 bytes)
if len(data) > 0x18:
totalLen = struct.unpack("<H",data[:2])[0]
dataLen = struct.unpack("<H",data[2:4])[0]
typ = struct.unpack("<H",data[4:6])[0]
dataInfoOffset = struct.unpack("<H",data[6:8])[0]
zeroInit = struct.unpack("<L",data[4:8])[0]
cid = struct.unpack("<L",data[8:0xC])[0]
tid = struct.unpack("<L",data[0xC:0x10])[0]
messageID = struct.unpack("<L",data[0x10:0x14])[0]
clientViewSize = struct.unpack("<L",data[0x14:0x18])[0]
logMessage = "ALPC message : %x:%x - %x:%x - %d:%d - %x - %x" % (totalLen, dataLen, typ, dataInfoOffset, cid, tid, messageID, clientViewSize)
print logMessage
return
Lo script IDA Pro incorpora un semplice motore di "hooking", che permette di spostare diverse istruzioni in un'area specifica (derivato dal nostro strumento DIMCT). Deve gestire molti casi particolari, come istruzioni relative alla memoria, riferimenti incrociati, ecc. e probabilmente mostrerà molti "can't install hook here" se provi a intercettare blocchi di base molto piccoli (<5 byte per binari x86, <12 byte per x64) che contengono molteplici riferimenti incrociati. Per maggiori informazioni, leggi il codice sorgente :).
Genera le seguenti informazioni nel file di config:
RCX/RDX e chiama la routine di log della DLL;L'iniettore DLL è un semplice iniettore DLL (roba CreateRemoteThread/LoadLibraryA).