Sebbene le componenti di AI e machine learning di nuova generazione delle soluzioni di sicurezza continuino a migliorare le capacità di rilevamento basate sul comportamento, alla base molti si affidano ancora al rilevamento basato sulle firme. Cobalt Strike, un popolare framework di comando e controllo (C2) utilizzato sia dagli attori delle minacce che dai red team sin dal suo debutto, continua ad essere pesantemente firmato dalle soluzioni di sicurezza.
Per continuare l'uso operativo di Cobalt Strike in passato, noi del team IBM X-Force Red Adversary Simulation abbiamo investito importanti sforzi di ricerca e sviluppo per personalizzare Cobalt Strike con strumenti interni. Alcuni dei nostri strumenti interni specifici per Cobalt Strike hanno versioni pubbliche, come "InlineExecute-Assembly", "CredBandit", and "BokuLoader". Negli ultimi due anni, data la firma eccessiva di Cobalt Strike, ne abbiamo limitato l'uso alla simulazione di attori di minacce meno sofisticati e abbiamo invece sfruttato altri C2 di terze parti e interni quando eseguiamo esercitazioni di red team più avanzate.
Grazie agli sforzi di ricerca e sviluppo, abbiamo riscontrato un maggiore successo operativo nelle esercitazioni avanzate di red team con:
Tuttavia, sono ancora molti gli attori delle minacce che sfruttano copie pirata di Cobalt Strike, e resta importante essere in grado di simularli. Per i red team disposti a impegnarsi in ricerca e sviluppo, è possibile che riescano comunque a ottenere successo operativo con Cobalt Strike simulando questi avversari. Inoltre, Cobalt Strike è un ottimo strumento di apprendimento, che può essere utilizzato dai nuovi arrivati per acquisire esperienza pratica con un framework C2 attraverso corsi di formazione per red team.
Mentre continuiamo ad espandere le nostre capacità C2, condividiamo alcuni insight su come abbiamo costruito il framework Cobalt Strike in passato, in particolare sviluppando caricatori riflettenti personalizzati. È inoltre pensato per aiutare i difensori a comprendere il funzionamento di Cobalt Strike, al fine di creare rilevamenti più affidabili.
Newsletter di settore
Resta al passo con le tendenze più importanti e interessanti del settore relative ad AI, automazione, dati e oltre con la newsletter Think. Leggi l' Informativa sulla privacy IBM.
L'abbonamento sarà fornito in lingua inglese. Troverai un link per annullare l'iscrizione in tutte le newsletter. Puoi gestire i tuoi abbonamenti o annullarli qui. Per ulteriori informazioni, consulta l'Informativa sulla privacy IBM.
Questo post sul blog è il primo di una serie che funge da introduzione e tratta le basi dello sviluppo di un caricatore riflettente Cobalt Strike. Man mano che procederemo con questa serie, costruiremo su queste basi e faremo riferimento a questo post.
Entro la fine di questa serie, puntiamo a creare un caricatore riflettente che si integri con le caratteristiche di evasione esistenti di Cobalt Strike e le migliora persino con tecniche avanzate attualmente non presenti nello strumento. Nei prossimi post approfondiremo lo sviluppo di caratteristiche specifiche di evasione e come implementarle nel nostro caricatore riflettente Cobalt Strike.
Per iniziare, questo post tratterà:
Mentre esploriamo il caricamento riflettente di Cobalt Strike attraverso la lente di uno sviluppatore di strumenti di sicurezza offensiva, metteremo in evidenza le opportunità di rilevamento ed elusione. Alcuni aspetti dello sviluppo saranno omessi o semplificati e ti invitiamo a colmare le lacune eseguendo il debug dei progetti di caricamento riflettente, ricostruendoli da zero o cercando formazione.
L'impianto Cobalt Strike C2, noto come Beacon, è una libreria Windows Dynamic-Link (DLL), e la funzionalità di utilizzare il nostro caricatore DLL in Cobalt Strike è nota come User-Defined Reflective Loader (UDRL).
Tipicamente, il caricatore DDL di Windows integrato è responsabile del caricamento delle DLL nello spazio di memoria virtuale di un processo. Il caricatore DDL di Windows esiste principalmente nello spazio utente, anche se attraversa lo spazio kernel quando si mappano le DLL dal disco.
L'uso del caricatore DDL di Windows presenta alcuni inconvenienti quando viene utilizzato durante le simulazioni degli avversari:
Pertanto, utilizzare il caricatore DDL di Windows per caricare la nostra DLL beacon non è una soluzione ideale. Per superare queste sfide, carichiamo la DLL del beacon dalla memoria con un caricatore riflettente.
I tre principali punti di rilevamento evitati dal carico riflettente sono:
Il caricamento riflettente può essere concepito come il semplice caricamento di una DLL raw direttamente dalla memoria, anziché dal file system.
Il caricamento riflettente e il caricatore DDL di Windows integrato servono entrambi allo stesso scopo: caricare una DLL dal formato file grezzo nello spazio di memoria virtuale di un processo. Tuttavia, il caricamento riflettente ha un vantaggio fondamentale rispetto al caricatore DDL di Windows: non richiede che il file DLL esista nel file system. Questo caricamento in memoria consente un numero illimitato di fasi di caricamento della catena, in quanto la DLL dell'impianto C2 può essere nascosta all'interno di strati di crittografia e codifica nella memoria del processo.
Un concetto chiave da comprendere quando si carica una DLL è sapere che la DLL verrà formattata in modo diverso su disco rispetto alla memoria. Le principali differenze tra la DLL in formato file raw e quella in formato indirizzo virtuale sono:
Formato file raw:
Formato dell'indirizzo virtuale:
Esaminando una DLL beacon HTTP nello strumento PE-Bear di Aleksandra Doniec, vediamo le differenze tra l'indirizzamento raw e quello virtuale per ciascuna sezione della DLL:
Tabella che elenca gli indirizzi grezzi e virtuali di ogni sezione della DLL beacon.
Questa DLL beacon HTTP/S è
PE-Bear fornisce una rappresentazione visiva della nostra DLL beacon così come esiste nel formato file raw rispetto al formato dello spazio di indirizzo virtuale:
Rappresentazione visiva della DDL beacon in formato raw (sinistra) rispetto al formato virtuale (destra)
Sebbene non sia la mossa più saggia da compiere durante una simulazione avversaria, trasferire su disco una DLL beacon non elaborata, senza offuscamento, e caricarla con un caricatore DDL di Windows è un ottimo modo per sfatare i miti del caricamento del beacon e di quello della DLL. In sostanza, il beacon è solo una DLL. Il caricatore DDL di Windows e un caricatore riflettente caricano semplicemente una DLL in un processo.
Per caricare la DLL beacon con il caricatore DDL di Windows, eseguiamo i seguenti passaggi:
LoadLibrary
per caricare il nostro DLL beacon dal disco.Per prima cosa, disabilitiamo tutte le opzioni di Malleable PE che rendono la nostra DLL beacon non caricabile dalla DLL di Windows. Per fare ciò, modifichiamo il nostro profilo Malleable C2 e disabilitiamo le opzioni di evasione di Malleable PE situate nel blocco di stage:
Blocco di stage del profilo Malleable C2 modificato per disattivare le funzioni di evasione di Cobalt Strike.
Dopo aver modificato il profilo, riavviamo il server Cobalt Strike Team, fornendo il nostro
Ci colleghiamo al Team Server con il client Cobalt Strike. Quindi creiamo un
Schermata della creazione di una DLL beacon "raw stageless" dal client Cobalt Strike
Utilizzando il codice sottostante, creiamo un programma C chiamato
Codice C di Windows per caricare la DLL beacon dal disco utilizzando il caricatore DLL di Windows.
Utilizziamo l'API
Come parte integrante del processo di caricamento, il caricatore DLL di Windows inizializzerà la nostra DLL beacon chiamando il suo punto di ingresso con
Dopo che il caricatore DLL di Windows avrà caricato e inizializzato la nostra DLL beacon nello spazio di memoria virtuale del processo, dovremo nuovamente chiamare il punto di ingresso della DLL beacon virtuale con l'argomento
Il nostro programma deve conoscere il punto di ingresso della DLL beacon virtuale per poterla eseguire. Questa operazione può essere eseguita in modo dinamico all'interno del programma, analizzando le intestazioni della DLL beacon virtuale per trovare l'indirizzo virtuale relativo (RVA) del punto d'ingresso, oppure possiamo vedere rapidamente qual è e codificare il valore.
Per la nostra prova di concetto, scopriremo manualmente e codificheremo in modo rigido il punto di ingresso RVA della DLL beacon nel nostro programma. Utilizzando PE-Bear scopriamo che il punto di ingresso del RVA al beacon è
Schermata della ricerca del punto di ingresso DLL beacon RVA utilizzando PE-Bear
Le
Con il codice pronto all'uso, compiliamo il nostro programma C in un eseguibile Windows:
Comando usato per compilare il nostro programma.
Inserendo la DLL beacon e il programma eseguibile di caricamento del beacon nella stessa directory, il caricatore DLL di Windows sarà in grado di rilevare la nostra DLL mentre esegue la sua routine di caricamento.
Mettiamo entrambi
La DLL beacon e il programma di caricamento si trovano nella stessa directory.
Dal desktop di Windows, facciamo doppio clic sul nostro loadBeaconDLL.exe per programmare e stabilire una connessione beacon attiva al nostro Team Server.
Connessione riuscita al C2 Team Server dal beacon DLL caricato tramite il caricatore DLL di Windows.
Cobalt Strike utilizza una versione modificata del progetto Caricamento riflettente di Stephen Fewer. Questo leggendario DLL loader in memoria ha più di dieci anni ed è stato utilizzato in Metasploit e in altri notevoli strumenti di sicurezza offensiva.
Nel corso degli anni, il caricatore riflettente Cobalt Strike è stato migliorato per gestire tutte le caratteristiche di evasione del Malleable PE che Cobalt Strike ha da offrire. Il principale svantaggio dell'utilizzo di un UDRL (User-Defined Reflective Loader) personalizzato è che le caratteristiche di evasione del Malleable PE possono essere supportate o meno già di default.
Alcune caratteristiche di evasione sono completamente implementate quando si utilizza un UDRL, in quanto vengono inserite nella DLL beacon dal motore Malleable PE di Cobalt Strikes durante la creazione del payload del beacon. Tuttavia, attualmente caratteristiche come devono essere gestite dall'UDRL, mentre altre come e
Il progetto Caricatore riflettente originale richiede la compilazione di
Poi un altro progetto è responsabile di:
Diagramma del caricatore riflettente originale, che carica una DLL nella memoria virtuale.
Un metodo alternativo consiste nell'anteporre il caricatore riflettente alla DLL, consentendo di caricare qualsiasi DLL non gestita senza bisogno di compilare la DLL dal codice sorgente. Si tratta di un metodo di caricamento riflettente robusto, in grado di caricare qualsiasi file PE (EXE o DLL).
Diagramma di un caricatore riflettente anteposto a una DLL, che carica una DLL nella memoria virtuale.
L'implementazione del carico riflettente da Cobalt Strike utilizza un ibrido dei due metodi sopra menzionati. Questo metodo di caricamento riflettente potrebbe essere familiare a chi conosce come il Meterpreter di Metasploit effettua il carico riflettente.
Come il metodo originale del caricatore riflettente, la funzione
Quando un UDRL viene caricato in Cobalt Strike e un operatore genera un payload beacon dal client Cobalt Strike, il motore PE Malleable di Cobalt Strike aggiorna il shellcode del caricatore riflettente all'offset del file raw dell'esportazione
Quando il motore Malleable PE completa l'applicazione delle patch alla DLL beacon raw, la DLL del beacon raw viene fornita all'operatore in un formato eseguibile simile a uno shellcode.
Schema del caricatore riflettente Cobalt Strike, caricamento della DLL beacon nella memoria virtuale.
Osservando i byte iniziali nel disassemblatore PE-Bear possiamo vedere che la DLL beacon stessa è eseguibile:
Lo stub del caricatore riflettente della chiamata viene mostrato come codici di operazioni di assemblaggio eseguibili.
I byte iniziali
Dopo l'esecuzione di un'opzione anteposta
Confermiamo che l'offset del file raw per l'esportazione
Schermata dell'utilizzo di PE-Bear per determinare l'offset del file raw dell'esportazione ReflectiveLoader.
Poiché esiste all'interno della directory di esportazione, l'indirizzo per l'esportazione di
Per scoprire l'offset del file raw dell'esportazione di
Gli indirizzi virtuali e grezzi per la sezione
Indirizzi grezzi e virtuali della sezione .text della DLL beacon.
La differenza tra i due è di
Possiamo confermarlo in PE-Bear facendo clic con il tasto destro del mouse sulla funzione RVA di esportazione
In sintesi, il flusso del processo di caricamento riflettente di Cobalt Strike è:
Diagramma che mostra le fasi principali di come Cobalt Strike esegue il caricamento riflettente della DLL beacon.
Poiché il nostro caricatore riflettente viene eseguito prima del caricamento della DLL beacon, il codice del caricatore riflettente deve essere puro shellcode.
Il modo più semplice per creare uno shellcode complesso è scriverlo in C senza dipendenze esterne. Quindi il file C viene compilato in un file oggetto. Tutto deve essere incluso nella sezione Infine, estraiamo la sezione.text
Il motore Malleable PE di Cobalt Strike si occuperà di ottenere il codice shellcode dal nostro file oggetto del caricatore riflettente e di inserirlo nella DLL beacon raw con l'offset del file raw dell'esportazione
Script Aggressor per scrivere shellcode del caricatore riflettente nella DLL beacon raw utilizzando Cobalt Strike.
Il nostro script UDRL Aggressor fa in modo che Cobalt Strike scriva lo shellcode del caricatore riflettente eseguendo questi passaggi:
<a href="https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#extract_reflective_loader">extract_reflective_loader</a>Cobalt Strike Aggressor integrata analizza il nostro file oggetto UDRL dall'
<a href="https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#setup_reflective_loader">setup_reflective_loader</a>La funzione Cobalt Strike Aggressor utilizzerà il motore Malleable PE per scoprire l'offset del file raw della nostra esportazione
Cobalt Strike ha fatto il lavoro per noi riguardo all'estrazione della sezione .text sezione dal nostro file oggetto del caricatore riflettente, aggiornando lo shellcode del caricatore riflettente e chiamandolo con lo stub del caricatore riflettente situato nell'intestazione DLL beacon.
Ecco le fasi che dobbiamo sviluppare per caricare il beacon in modo riflettente:
Ci sono diversi metodi che possiamo usare per scoprire l'indirizzo della DLL beacon raw in memoria. Alcuni metodi sono:
Quando usiamo un metodo che ricerca a ritroso, dobbiamo prima ottenere l'indirizzo corrente del puntatore di istruzioni del thread (
Codice assembly Intel x64 per ottenere l'indirizzo base della DLL beacon raw dal registro RDI.
Il progetto originale del caricatore riflettente cerca a ritroso i collettori MZ e PE. Queste intestazioni sono diventate dei punti di rilevamento. Per superare questo problema, Cobalt Strike ha aggiunto le caratteristiche di evasione Malleable PE
La documentazione di Cobalt Strike afferma che l'opzione
Una volta configurati i byte
Questi byte devono essere in qualche modo univoci, altrimenti il caricatore riflettente non sarà in grado di trovarli. Inoltre, i byte per l'intestazione MZ devono essere senza spese operative e devono essere eseguibili. Non possono essere valori come
Dopo aver scoperto questo potenziale punto di rilevamento, ho sviluppato un metodo diverso, ma simile, per trovare l'indirizzo base della DLL beacon raw. Questo metodo utilizza un cacciatore di uova capace di cercare a ritroso da
L'indirizzo
Poiché non abbiamo facile accesso al motore Java Malleable PE, lo script UDRL Aggressor
Script Aggressor per scrivere un uovo nella DLL del beacon raw e visualizzare le modifiche nella console dello script Cobalt Strike.
Il codice UDRL deve conoscere il valore egg scritto nella DLL beacon raw dallo script UDRL. Con l'uovo conosciuto, il cacciatore di uova cerca a ritroso due istanze dell'uovo, come si vede nel codice qui sotto:
Codice assembly Intel x64 per un cacciatore di uova che cerca a ritroso due istanze di un uovo a 64 bit.
Ora che le intestazioni MZ e PE non sono più utilizzate, possiamo eliminarle nello script UDRL Aggressor:
Script Aggressor per mascherare MZ, PE e byte inutilizzati del banner DOS situato nelle intestazioni della DLL beacon non elaborata.
Esiste anche un altro modo, specifico di Cobalt Strike, per scoprire l'indirizzo di base della DLL beacon raw. Come abbiamo visto sopra, i byte iniziali nello stub del caricatore riflettente della chiamata memorizzano l'indirizzo base della DLL beacon raw nel registro
Per esaminare ulteriormente questo aspetto nel debugger, generiamo un beacon, anteponiamo un punto di interruzione (
Schermata X64dbg del passaggio attraverso lo stub del caricatore riflessivo per vedere che l'indirizzo di base della DLL beacon raw viene salvato nel registro RDI prima di chiamare il caricatore riflettente.
Di seguito è riportato un esempio pratico di come ottenere l'indirizzo base della DLL beacon raw dallo stub del caricatore riflettente della chiamata:
Codice C di assemblaggio in linea per ottenere l'indirizzo di base della DLL raw beacon dal registro RDI.
Con l'indirizzo base della DLL del beacon raw, ora possiamo ottenere i valori necessari per caricare il beacon nello spazio di indirizzi virtuale del processo.
La tabella seguente elenca i valori di cui abbiamo bisogno dalle intestazioni della DLL beacon raw, le posizioni in cui li troveremo e i relativi tipi:.
Tabella che elenca i valori di intestazione della DLL beacon raw, utili per il caricamento della DLL beacon.
Non tutti i contenuti delle intestazioni sono necessari per caricare la DLL beacon. I valori richiesti possono essere reimpacchettati o oscurati. I valori non richiesti possono essere rimossi o randomizzati.
Una volta che conosciamo l'intestazione della DLL del beacon raw della romSizeOfImagef
Possono essere utilizzati diversi metodi per allocare la memoria per la DLL beacon virtuale. Metodi diversi utilizzeranno tipi diversi di memoria. I diversi metodi supportati dal caricatore riflettente predefinito di Cobalt Strike sono:
Tabella che mostra le opzioni di allocazione della memoria di Cobalt Strike per la DLL beacon virtuale.
Con UDRL è possibile fare un ulteriore passo avanti. In alternativa è possibile utilizzare la versione NTAPI di queste funzioni. Inoltre, le funzioni NTAPI potrebbero essere richiamate tramite chiamate di sistema dirette o indirette, che possono aiutare o meno a rafforzare le capacità di evasione.
Quando il metodo allocatore è impostato su
Esempio di codice dal progetto BokuLoader che mostra una chiamata di sistema diretta utilizzata per allocare memoria per la DLL beacon virtuale.
L'immagine seguente mostra un esempio di codice che utilizza i metodi HellsGate e HalosGate per determinare i numeri di chiamata del sistema:
Esempio di codice dal progetto BokuLoader che mostra come vengono scoperte le chiamate di sistema dal processo.
Ora che abbiamo allocato la memoria per la nostra DLL del beacon virtuale, dobbiamo copiare le sezioni del beacon dagli offset di file raw, così come esistono nella DLL beacon raw, alla memoria allocata nei relativi offset virtuali.
Se allocassimo la nostra memoria conREADWRITE Prima di chiamare il punto di ingresso della DLL virtuale beacon dovremo modificare le protezioni di memoria della sezione eseguibile.
Assegnare la nostra memoria con facilita il processo di caricamento riflessivo, ma aumenta le possibilità di rilevamento da parte delle soluzioni di sicurezza.
Di seguito è riportato un esempio semplificato di codice, tratto dal progetto BokuLoader, che dimostra questo.
Esempio di codice dal progetto BokuLoader che mostra le sezioni copiate dalla DLL beacon raw alla DLL beacon virtuale.
Alcune caratteristiche di evasione riguardanti le sezioni di carico sono:
Nel progetto pubblico BokuLoader, le intestazioni per la DLL beacon non vengono copiate dalla DLL beacon raw alla DLL beacon virtuale. Attualmente i primi
Un'altra possibile opportunità di evasione è far criptare le sezioni dallo script UDRL Aggressor. Le sezioni potevano essere decriptate in memoria dall'UDRL, utilizzando una chiave condivisa tra l'UDRL e lo script Aggressor UDRL.
Per funzionare correttamente, il beacon HTTP/S x64 si basa su quattro DLL. Se queste DLL non sono attualmente caricate nel processo, il nostro caricatore riflettente dovrà caricarle.
Le quattro DLL sono elencate nella cartella di importazione delle DLL beacon HTTP/S:
Schermata di PE-Bear che elenca le DLL dalla directory di importazione della DLL del beacon.
Il caricatore riflettente integrato di Cobalt Strike utilizza l'API kernel32.LoadLibraryA per caricare le DLL.
Il caricamento delle DLL può essere effettuato in vari modi diversi, con diverse considerazioni sulla sicurezza operativa. Alcuni metodi sono:
Se la DLL è già presente nel processo, è comunque possibile utilizzare le API di Windows sopra indicate per ottenere gli indirizzi di base della DLL, anche se ciò potrebbe attivare avvisi di rilevamento indesiderati.
In alternativa, il PEB contiene un puntatore alla struttura
<a title="https://learn.microsoft.com/it-it/windows/win32/api/winternl/ns-winternl-peb_ldr_data" href="https://learn.microsoft.com/it-it/windows/win32/api/winternl/ns-winternl-peb_ldr_data">_PEB_LDR_DATA</a>
. All'interno, c'è un elenco collegato di tutte le DLL caricate nel processo e delle loro informazioni relative (
). BokuLoader utilizza questa funzionalità per scoprire le informazioni DLL, evitando chiamate API non necessarie.
Se la DLL non esiste nel
Il caricamento riflettente annidato non può essere utilizzato facilmente per caricare le dipendenze DLL perché i caricatori riflettenti in genere non registrano la DLL nel processo. Il codice esterno alla DLL non può utilizzare correttamente una DLL caricata in modo riflettente. Il progetto DarkLoadLibrary potrebbe essere in grado di caricare correttamente una DLL in memoria senza attivare un evento di caricamento dell'immagine del kernel.
Esempio di codice dal progetto BokuLoader che mostra come gli indirizzi di base delle DLL caricate possono essere risolti esaminando InMemoryOrderModuleList.
Una volta caricate le DLL richieste nel processo, è necessario risolvere le API elencate nella directory di importazione. Gli indirizzi API dovranno quindi essere scritti nella tabella degli indirizzi di importazione (IAT) della DLL del beacon virtuale. In questo modo il beacon sa a quale indirizzo saltare quando deve chiamare API come
La voce di importazione dovrà essere risolta tramite la stringa ordinale o del nome.
Nell'immagine sottostante, vediamo che la DLL del beacon Cobalt Strike utilizza una combinazione di ordinali e stringhe di nome per le voci di importazione:
Schermata di PE-Bear che mostra alcune voci di importazione per la DLL beacon che devono essere risolte tramite ordinale.
Il caricatore riflettente Cobalt Strike integrato utilizza l'API
Alcuni metodi di evasione per risolvere gli indirizzi API sono:
GetProcAddress
NTDLL.LdrGetProcedureAddress
BokuLoader utilizza un'implementazione di codice personalizzata diGetProcAddress
Il è in grado di gestire sia stringhe di nomi che ordinali. Se l'indirizzo restituito per la voce di importazione è un forwarder di un'altra DLL, il valore predefinito di BokuLoader è per risolvere il forwarder.
Durante la scrittura dello IAT, è possibile implementare l'hooking scrivendo gli indirizzi virtuali delle funzioni hook che abbiamo implementato anziché l'indirizzo virtuale delle API previsto. Finché l'output atteso viene restituito al beacon quando viene chiamato l'indirizzo nell'IAC, possiamo eseguire codice aggiuntivo prima di tornare al beacon. I prossimi post e le pubbliche uscite di BokuLoader dimostreranno come possiamo utilizzare l'aggancio IAT per caratteristiche di evasione avanzate.
Con una recente release, il progetto pubblico BokuLoader supporta la funzionalità Malleable PE dal profilo C2 di Cobalt Strike con un'implementazione personalizzata. Modificando la chiave di mascheramento nellaBokuLoader.cna
Per quanto riguarda la sicurezza operativa, è importante sapere che i motori di pattern matching sono in grado di forzare le maschere XOR a singolo byte. I prossimi post dimostreranno come possiamo creare il nostro motore Malleable PE utilizzando la funzionalità di scripting Cobalt Strikes Aggressor per offuscare i beacon e superare il pattern matching.
La DLL beacon presenta numerosi trasferimenti che devono essere risolti e scritti nella tabella dei trasferimenti di base della DLL del beacon virtuale prima di essere eseguita.
In PE-Bear possiamo vedere che la DLL del beacon ha di default l'indirizzo base dell'immagine di
Schermata da PE-Bear che mostra l'indirizzo base dell'immagine della DLL beacon.
Prima di iniziare a scrivere i trasferimenti, dobbiamo calcolare il delta tra l'indirizzo di base della nostra DLL beacon virtuale e l'indirizzo di base codificato.
Ad esempio, facciamo finta che l'indirizzo di base della nostra DLL beacon virtuale sia
Successivamente, per determinare l'indirizzo virtuale per ogni voce di trasferimento nella Tabella di trasferimento base, aggiungiamo il delta dell'indirizzo di riferimento base all'indirizzo di ingresso del trasferimento codificato per determinare il trasferimento all'interno della nostra DLL beacon virtuale.
Nell'immagine qui sotto possiamo vedere che le voci di trasferimento dei beacon sono scritte al contrario nel formato little-endian:
Schermata di PE-Bear che mostra alcune voci di trasferimento presenti in formato little-endian.
L'indirizzo codificato fisso per questa voce di trasferimento è
Aggiungiamo questo indirizzo al delta dell'indirizzo base, per ottenere l'indirizzo virtuale per il trasferimento così come esiste nella DLL beacon virtuale:
Per ogni voce di trasferimento dovremo verificare che il tipo sia
<a title="https://learn.microsoft.com/it-it/windows/win32/debug/pe-format" href="https://learn.microsoft.com/it-it/windows/win32/debug/pe-format">IMAGE_REL_BASED_DIR64 (0xA)</a>
. Se è falso, salteremo la scrittura del trasferimento.
Una volta determinato l'indirizzo virtuale del trasferimento così come esiste all'interno della DLL beacon virtuale, la scriviamo nello spazio di memoria che contiene l'indirizzo di ingresso del trasferimento codificato.
Se ti interessa saperne di più su come effettuare i trasferimenti PE, dai un'occhiata al codice della funzione doRelocations nel progetto pubblico BokuLoader. Prima di pubblicare questo post sul blog, ho modificato il codice dei trasferimenti da assembly a codice C, sperando che sia leggibile dagli esseri umani, per aiutare chi desidera conoscere i dettagli tecnici su come viene effettuato.
L'esecuzione del beacon può essere suddivisa in tre fasi:
Se la memoria che abbiamo allocato per il nostro DLL beacon virtuale è
Se abbiamo allocato la memoria del nostro beacon virtuale come non eseguibile (
Nel progetto pubblico BokuLoader, le modifiche alle protezioni della memoria vengono eseguite tramite chiamate dirette del sistema a
Esempio di codice dal progetto BokuLoader che dimostra la modifica della sezione .text della DLL beacon virtuale in eseguibile.
The
Per eseguire correttamente la DLL beacon virtuale, è necessario anzitutto inizializzarla chiamando il punto di ingresso della DLL beacon virtuale. Il primo argomento è l'indirizzo base della DLL beacon virtuale. Il secondo argomento è il
Esempio di codice dal progetto BokuLoader che inizializza la DLL beacon virtuale.
Dopo aver inizializzato la DLL beacon virtuale, possiamo restituire il punto di ingresso del beacon virtuale allo stub del caricatore riflettente della chiamata oppure possiamo chiamare il punto di ingresso della DLL beacon virtuale nel nostro UDRL con
A differenza di una tipica DLL in cui il primo argomento
<a href="https://learn.microsoft.com/it-it/windows/win32/dlls/dllmain">DLLMAIN</a>
sarebbe l'indirizzo di base della DLL virtuale, il beacon prevede l'indirizzo di base della DLL beacon raw. Se non viene fornito, alcune funzioni di evasione di Malleable PE potrebbero non riuscire.
Esempio di codice dal progetto BokuLoader che mostra due modi diversi per eseguire la DLL beacon virtuale.
Ci auguriamo che questo post sul blog aiuti sia i red team che i blue team a comprendere meglio Cobalt Strike e il processo di caricamento riflettente. Esistono ancora moltissime opportunità di evasione che possono essere implementate tramite il caricamento riflettente. Con una comprensione più profonda di questi concetti, le organizzazioni possono prepararsi meglio per una difesa efficace contro le minacce informatiche.
I prossimi post di questa serie si concentreranno sull'integrazione dell'UDRL con le attuali caratteristiche di evasione di Cobalt Strike, approfondiranno le caratteristiche di evasione non documentate già presenti nel BokuLoader pubblico, oltre a caratteristiche avanzate che non sono ancora state rilasciate al pubblico. Restate sintonizzati per informazioni e tecniche più approfondite su come portare il gioco Cobalt Strike a un livello superiore con lo sviluppo UDRL!