{
	"id": "e0a58bf6-c4b6-4068-a23e-c887c3a89bdc",
	"created_at": "2026-04-06T00:07:17.075883Z",
	"updated_at": "2026-04-10T03:20:21.083424Z",
	"deleted_at": null,
	"sha1_hash": "ec33c210d52f58eb456e4da8dce34f34d2144d09",
	"title": "Analisi tecnica e considerazioni sul malware Strela",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 3641923,
	"plain_text": "Analisi tecnica e considerazioni sul malware Strela\r\nArchived: 2026-04-05 21:05:08 UTC\r\nLa scorsa settimana il malware Strela è approdato in Italia. Strela è un semplice stealer specializzato nel furto\r\ndelle credenziali di posta dagli applicativi Thunderbird e Outlook. Il malware in sè è piuttosto semplice ma, fatta\r\neccezione per la prima ondata, il packer con cui viene veicolato è più complicato da analizzare per via della della\r\ntecnica di Control Flow Obfuscation (CFO) che impiega.\r\nVogliamo in questo articolo analizzare il malware, dividendo le analisi in tre sezioni:\r\n1. nella prima (e più tecnica) parte discuteremo alcune tecniche per analizzare il packer di Strela e scriveremo\r\nuno script per l’estrazione automatica del payload (che si rileverà estremamente semplice a discapito della\r\ncomplessità iniziale);\r\n2. nella seconda, descriveremo Strela stesso incluso il metodo utilizzato per inviare i dati al C2;\r\n3. nella terza parte caratterizzeremo proprio il C2.\r\nPer la sola descrizione malware Strela è possibile passare subito alla relativa sezione.\r\nL’infezione non usa TTP peculiari ed è essenzialmente un classico esempio di malware diffuso tramite packer.\r\nUna volta che il payload è in esecuzione, questo ruba gli account Thunderbird ed Outlook e li invia al C2.\r\nLe fasi dell’infezione di Strela\r\nIl packer di Strela\r\nIl packer sembra scritto con MinGW (di cui si riconoscono molte funzioni firma) ed il debugger IDA non ha\r\nparticolari problemi ad identificare buona parte del runtime. Sono presenti due callback TLS ma sono quelli tipici\r\ndi MinGW e per i quali non è necessaria che una veloce analisi superficiale.\r\nLa funzione WinMain è però stata offuscata facendo uso, come già anticipato, di CFO. Il CFG generato da IDA è\r\ndecisamente disarmante.\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 1 of 17\n\nIl grafico di esecuzione della principale funzione del packer (WinMain)\r\nLa procedura di analisi più diretta (ma più complessà) da seguie è quella che cerca di districare il flusso di\r\nesecuzione in modo simile a come era stato fatto per Emotet a suo tempo. Prima di vedere come affrontare un\r\nsimile problema è utile delineare brevemente alcune tecniche più immediate per il recupero del payload.\r\nAnalisi dinamica\r\nNavigando sommariamente i blocchi della funzione WinMain si può notare come molti di essi siano composti da\r\nistruzioni aritmetiche il cui unico scopo è quello di complicare la comprensione del codice. Dato che l’unica\r\nsezione eseguibile è quella del codice e che questa è in sola lettura, un eventuale payload o shellcode deve essere\r\ndecodificato in un’area appositamente allocata con i permessi di scrittura ed esecuzione. Torna utile quindi cercare\r\nistruzioni di chiamate o salti indiretti: le prime possono essere indicatrici di chiamate ad API, i secondi dell’inizio\r\ndell’esecuzione del payload.\r\nSebbene sia possibile effettuare ricerce più specifiche con IDA, una ricerca testuale della stringa “call” è\r\nsufficiente per trovare una chiamata a VirtualAlloc .\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 2 of 17\n\nIl blocco chiave per l’analisi dinamica del packer\r\nSebbene il codice sia ordinario ed è facile sorvolarci sopra, ci sono due dettagli importanti da notare:\r\n1. I permessi delle pagine allocate sono PAGE_EXECUTE_READWRITE. Questo indica che abbiamo\r\ntrovato il buffer ipotizzato precedentemente.\r\n2. La dimensione del buffer allocato (che, in quanto secondo parametro, si troverà in RDX prima della\r\nchiamata) è ottenuta leggendo da [rdx+50h] , dove RDX è la base di una struttura dati. 0x50 è l’offset del\r\ncampo ImageSize nell’Optional Header PE: possiamo quindi ipotizzare che il packer stia allocando l’area\r\ndi memoria in cui mappare il PE.\r\nTrattandosi di un’analisi dinamica, non ci rimane che piazzare un breakpoint dopo la chiamata a GetProcAddress\r\ne verificare se l’ipotesi è corretta. Dopo una manciata di Single Step otteniamo il PE di Strela:\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 3 of 17\n\nL’header PE del payload (Strela) decodificato.\r\nSempre mantenendoci nell’ambito dell’analisi dinamica, altri possibili approcci sono:\r\n1. mettere un breakpoint sull’intero buffer allocato e, una volta individuato il codice che vi scrive, identificare\r\nda dove sono letti i dati. Questo fornità la posizione del payload;\r\n2. cercare in memoria un header PE. E’ necessario essere consapevoli delle limitazioni di questo secondo\r\nmetodo: ad esempio il packer decodifca l’intero PE di Strela ma poi non ne mappa l’header e sono ben noti\r\ncasi in cui l’header PE è volutamente alterato per evitare match per firma.\r\nQuesto è sicuramente il metodo più veloce da usare quando ci si trova di fronte alla prima analisi di un nuovo\r\nmalware ed è il metodo che abbiamo usato per rispondere velocemente alla campagna ed estrarre e censire i\r\nrelativi IoC.\r\nCrittonalisi\r\nUn altro metodo di analizzare il packer è quello di cercare di capire come e dove è salvato il payload codificato.\r\nGeneralmente questa è una battaglia generalmentepersa in partenza se non si ha a disposizione il codice per\r\nguidarci poiché vi è un’infinità numerabile di codec utilizzabili e dedurre l’algoritmo usato dai soli dati è quasi\r\nsempre impossibile.\r\nTuttavia, vale la pena menzionare questo metodo perchè:\r\n1) funziona in questo caso specifico;\r\n2) è una utile forma mentis.\r\nDi tutte le sezioni PE presenti nel packer, quella .data è la più grande. Anche da IDA è possibile capire che è i\r\ndati sono più numerosi del codice stesso.\r\nLa sezione dati (verde e grigio) è più grande di quella del codice.\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 4 of 17\n\nQuesto ci fa pensare che il payload sia effettivamente nella sezione .data . Sebbene possa sembrare scontato che\r\nsia così (il payload è composto da dati) va ricordato che in realtà, trattandosi di dati in sola lettura, questi possono\r\nessere presenti ovunque (es: nelle risorse o nella sezione del codice stesso).\r\nE’ ben noto che i dati cifrati a dovere non sono (polinomialmente) distinguibili da dati pseudo-casuali quindi\r\ndobbiamo cercare necessariamente dei pattern.\r\nL’immagine sotto mostra la sezione dati del packer.\r\nLa sezione dati del packer, si notano alcuni pattern.\r\nSebbene inizialmente sia difficile notarli, sono presenti dei pattern. A parte il byte di valore 0x0a (che una veloce\r\nricerca su IDA mostra essere usato dal runtime di MinGW), il primo particolare che salta all’occhio è la DWORD\r\ndi valore 0x1b400 (che è presente in little endian quando visualizzata a byte). Vista la cifra tonda, questa\r\nprobabilmente rappresenta la dimensione del payload. Il valore è inoltre molto simile alla dimensione della\r\nsezione di dati stessa, rafforzando questa ipotesi.\r\nI dati seguenti però non sembrano rilevare altri pattern. Tuttavia, un’analisi più attenta mostra la ripetizione di\r\ncerti byte. Ad esempio i caratteri ripetuti che più saltano all’occhio sono ÉÜ , ripetuti ogni 20 byte. Questo\r\npotrebbe essere sintomatico dell’utilizzo di un rolling XOR per la codifica del payload. Dato che un PE ha\r\nnaturalmente molte sequenze di zeri, queste espongono in chiaro la chiave in caso di cifratura con XOR.\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 5 of 17\n\nIpotizzando un rolling XOR, la chiave potrebbe essere lunga 20 byte ma non sappiamo quale sia l’inizio.\r\nIspezionare l’inizio dei dati può aiutarci a capirlo. Dato che i caratteri ÉÜ compaiono poco dopo l’inizio dei dati,\r\nè probabile che la chiave sia in chiaro subito dopo la dimensione del payload. Possiamo quindi ipotizzare il\r\nformato \u003cdimensione\u003e\u003cchiave xor\u003e\u003cpayload\u003e .\r\nIn rosso la dimensione del payload. In verde la chiave XOR di decifratura. A seguire il payload\r\ncifrato.\r\nUna rapida verifica su Cyberchef ci conferma che le nostre ipotesi erano esatte. E’ ora possibile scrivere un\r\nsemplice script Python per l’estrazione automatica di Strela:\r\n#!/usr/bin/env python3 import struct, sys def read_pe_section(filename: str, section_name: str) -\u003e\r\nbytearray: pe = None sections = {} image_base = None def read_u64(x: int) -\u003e int: return\r\nstruct.unpack(\"\u003cQ\", pe[x:x+8])[0] def read_u32(x: int) -\u003e int: return struct.unpack(\"\u003cI\", pe[x:x+4])\r\n[0] def read_u16(x: int) -\u003e int: return struct.unpack(\"\u003cH\", pe[x:x+2])[0] def at_va(va: int, is_rva :\r\nbool = False) -\u003e slice: rva = va - (image_base if not is_rva else 0) for s in sections.values(): if\r\nrva \u003e= s[\"rva\"] and rva \u003c s[\"rva\"] + s[\"va_size\"]: return slice(rva - s[\"rva\"] + s[\"raw_offset\"],\r\ns[\"raw_size\"] + s[\"raw_offset\"]) raise ValueError(\"RVA is out of range\") with open(filename, \"rb\") as\r\nf: pe = f.read() nt_header_offset = read_u32(0x3c) n_sections = read_u16(nt_header_offset + 0x6)\r\nn_sections_offset = nt_header_offset + read_u16(nt_header_offset + 0x14) + 0x18 image_base =\r\nread_u64(nt_header_offset + 0x30) sections = {} for i in range(n_sections): section_start_offset =\r\nn_sections_offset + 0x28 * i name = pe[section_start_offset : section_start_offset +\r\n0x8].rstrip(b\"\\x00\").decode(\"iso-8859-1\") va_size = read_u32(section_start_offset + 0x8 ) rva =\r\nread_u32(section_start_offset + 0x0c ) raw_size = read_u32(section_start_offset + 0x10 ) raw_offset =\r\nread_u32(section_start_offset + 0x14 ) sections[name] = dict(rva=rva, va_size=va_size,\r\nraw_size=raw_size, raw_offset=raw_offset) return bytearray(pe[at_va(sections[section_name][\"rva\"],\r\nis_rva = True)]) def decode_payload(data_section: bytearray, key_len: int = 0x14, struct_offset:int =\r\n0x10) -\u003e bytes: payload_size = struct.unpack(\"\u003cI\", data_section[struct_offset:struct_offset+4])[0]\r\npayload_key = data_section[struct_offset+4:struct_offset+4+key_len] payload =\r\ndata_section[struct_offset+4+key_len:struct_offset+4+key_len + payload_size] for i in\r\nrange(len(payload)): payload[i] ^= payload_key[i % len(payload_key)] return payload def main(): if\r\nlen(sys.argv) != 3: print(\"Usage: ex.py INPUT OUTPUT\", file=sys.stderr) sys.exit(1) with\r\nopen(sys.argv[2], \"wb\") as f: f.write(decode_payload(read_pe_section(sys.argv[1], \".data\"))) if\r\n__name__ == \"__main__\": main()\r\nIn questo caso siamo stati fortunati poichè il packer utilizza una codifica semplice e riconoscibile ma, in generale,\r\nsenza il codice a fare da guida non è possibile determinare come estrarre automaticamente il payload.\r\nAnche se l’esperienza ci ha insegnato che non è conveniente spendere troppo tempo nel cercare di estrarre\r\nautomaticamente un malware (vista l’estrema variabilità dei packer), in questo specifico caso abbiamo utilizzato\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 6 of 17\n\nquanto appreso per velocizzare notevolmente l’estrazione degli IoC dalle campagne Strela che si sono verificate la\r\nscorsa settimana e che sono riprese anche in data odierna. Come vedremo, Strela non ha offuscazione e questo\r\nscript ci ha permesso di estrarre gli IoC senza la necessità di aprire una VM per l’analisi.\r\nAnalisi statica\r\nA questo punto sappiamo più o meno tutto quello che ci interessava sapere sul packer ma, paradossalmente, non\r\nsappiamo niente del suo codice per via della CFO.\r\nRimuovere uno strato di CFO è complesso. Nel tempo sono nati vari framework come Triton, Angr e Miasm per\r\nl’analisi concolica (crasi di Concreta e Simbolica) ma sono spesso poco o nulla documentati (con l’eccezione di\r\nAngr) e non sono comunque in grado di semplificare un blocco di istruzioni x86 o sbrigliare un CFG perchè, in\r\ngenerale, il problema è molto complesso.\r\nPrima di affrontare il problema del districare il CFG del packer è quindi opportuno verificarne il rapporto\r\nfattibilità/sforzo.\r\nGuardando meglio il grafico della funzione WinMain , si nota che è inizialmente composto da una catena di\r\nblocchi decisionali della forma mostrata sotto. Il ramo “Vero” della condizione porta ad un blocco di “codice di\r\nlavoro” che poi torna con un jump all’inizio della catena; il ramo “Falso” va invece ad un altro blocco.\r\nUn blocco decisionale del dispatcher\r\nI blocchi di lavoro sono composti da istruzioni originali del packer e da una serie di istruzioni aritmetiche che\r\ncalcolano un valore, detto ticket, per determinare il prossimo blocco di lavoro a cui saltare.\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 7 of 17\n\nLe istruzioni per il calcolo del prossimo blocco.\r\nQuesta è una classica struttura a dispatcher loop che possiamo schematizzare come segue:\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 8 of 17\n\nLa struttura del dispatcher loop del packer\r\nPer sbrogliare la matassa, che è al momento il CFG di WinMain , sono necessari i seguenti passi:\r\n1. Ricreare il CFG di WinMain in Python per poterlo analizzare.\r\n2. Determinare tutti i blocchi di dispatch (quelli rossi nell’immagine sopra) ed il valore del ticket a loro\r\nassociato. Per fortuna hanno tutti un formato fisso con tre istruzioni (tranne il primo che è una leggera\r\nvariante) e non sono presenti istruzioni superflue.\r\n3. Analizzare il blocchi che calcolano il prossimo (o i prossimi) ticket e determinare questi valori.\r\n4. Riscrivere il CFG collegando tra loro solo i blocchi di istruzioni utili.\r\n5. Riassemblare le istruzioni in un PE.\r\nPer gestire le istruzioni x86 abbiamo usato iced x86. Lo script finale è disponibile qui (lo script è pensato per un\r\nsample specifico). Descriviamo brevemente le parti più interessanti.\r\nL’analisi e la trasformazione di codice di basso livello non è generalmente complicata ma è laboriosa: nel caso di\r\ncodice x86 è proprio tediosa. Ogni singolo mnemonic ha svariate forme di codifica, senza contare che alcune\r\nistruzioni sono codificabili con opcode diversi ( add al, 1 potrebbe essere add al, imm8 o add rm8, imm8 ).\r\nInoltre la forma a due operandi delle istruzioni è più scomoda di quella a tre operandi per l’analisi (analogo\r\ndiscorso per il registro dei flag, che è un operando/output implicito). Infine il codice a 64 bit rende impossibile la\r\ntrasformazione di operazioni aritmetiche lavorando direttamente sulle istruzioni perchè gli immediati sono rimasti\r\na 32 bit e non è possibile unire due somme o due sottrazioni poichè il risultato potrebbe non essere rappresentabile\r\ncome immediato.\r\nI blocchi gialli delle istruzioni utili possono essere in realtà un grafo di blocchi ed è quindi necessario trovare i\r\nblocchi blu a partire a un blocco giallo. Per fortuna tutti i blocchi blu saltano ad un blocco unico che poi torna\r\nall’inizio del dispatcher.\r\nPer la costruzione del CFG abbiamo usato la proprietà flow_control delle istruzioni iced x86: questa indica il\r\ntipo di control flow in seguito all’esecuzione dell’istruzione stessa e permette quindi di determinare facilmente la\r\nfine dei Basic Block (BB). L’unica operazione da tenere in considerazione è la possibilità per un BB di saltare nel\r\nmezzo di un BB precedente, che andrà quindi diviso. Ogni blocco ha un possibile blocco successivo (next) ed un\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 9 of 17\n\npossibile blocco di salto (branch) a seconda del tipo di salto che lo termina (o nessuno dei due se, ad esempio\r\ntermina con un ret ). Quando si crea il CFG è utile ritornare il primo blocco ma anche una mappa che associ\r\nogni VA in cui inizia un blocco con il blocco stesso.\r\nLa funzione che districa il flusso di esecuzione è mostrata qui sotto. Vengono subito identificati i nodi iniziali e\r\nfinali del dispatcher loop (il primo è il secondo nodo della funzione, il secondo è il nodo che salta all’inizio). Il\r\nprimo blocco contiene il valore iniziale del ticket, da cui è anche possibile capire l’offset rispetto ad RBP in cui è\r\nsalvato.\r\ndef detangle(start: BasicBlock, context: typing.Dict[int, BasicBlock]): #Step 1: delimit the\r\ndispatcher loop dispatcher_start = start.next dispatcher_end = [bb for bb in context.values() if\r\nbb.next == dispatcher_start and bb != start][0] #Step 1b: find the initial ticket initial_ticket =\r\nNone ticket_var_offset = None for i in start.instructions: if i.code == ix86.Code.MOV_RM32_IMM32 and\r\ni.memory_base == ix86.Register.RBP and i.memory_index == ix86.Register.NONE: initial_ticket =\r\ni.immediate(1) ticket_var_offset = i.memory_displacement break if initial_ticket is None: raise\r\nValueError(\"Initial ticket not found\") #Step 2: find all the dispatching branches #The form is: jmp -\u003e\r\nmov, sub, jz (T)-\u003e work code # (NT)-\u003e next dispatching branch tickets = {None: None} blocks =\r\n[dispatcher_start] while blocks[-1].end == BasicBlockEnd.CONDITIONAL: cur_block = blocks[-1] for i in\r\ncur_block.instructions: if i.code in [ix86.Code.SUB_RM32_IMM32, ix86.Code.SUB_EAX_IMM32] and\r\ni.op0_register != ix86.Register.NONE: tickets[i.immediate(1)] = cur_block.branch if\r\nlen(cur_block.next.instructions) == 1 and cur_block.next.end == BasicBlockEnd.UNCONDITIONAL:\r\nblocks.append(cur_block.next.next) #Step 3: Rewire start.set_next(tickets[initial_ticket]) todo =\r\n[start.next] done = set() while todo: block = todo.pop(0) if block is None or block in done: continue\r\ndone.add(block) for b in dfs_reach_dispatcher_end(block, dispatcher_end.va): tn, tb = simplify(b,\r\nticket_var_offset) b.set_next(tickets[tn]) b.set_branch(tickets[tb]) todo.append(b.next)\r\ntodo.append(b.branch) def ilen(i, va): if i.len \u003e 0: return i.len e = ix86.Encoder(64) return\r\ne.encode(i, va) #Step 4: layout the BBS va = start.va code = [] done = {} blocks = [(None, start)]\r\nwhile blocks: j, b = blocks.pop() if b in done: block_va = done[b] if j is not None: code[j] =\r\nix86.Instruction.create_branch(code[j].code, block_va) else:\r\ncode.append(ix86.Instruction.create_branch(ix86.Code.JMP_REL32_64, block_va)) va += ilen(code[-1], va)\r\ncontinue elif j is not None: code[j] = ix86.Instruction.create_branch(code[j].code, va) done[b] = va\r\nni = b.new_instructions(ticket_var_offset) if len(ni) == 0: assert(b.branch is None) for i in ni: i.ip\r\n= va va += ilen(i, va) code.extend(ni) if b.branch is not None: blocks.append((len(code)-1, b.branch))\r\nif b.next is not None: blocks.append((None, b.next)) encoder = ix86.BlockEncoder(64, False)\r\nencoder.add_many(code) encoded_bytes = encoder.encode(start.va) return encoded_bytes\r\nI nodi rossi sono trovati partendo dal primo e seguendo i rami Falsi (ovvero i next , per la forma di salto) di ogni\r\nblocco, fino a che non si torna all’inizio. Mentre si attraversano i nodi rossi vengono collezionati anche i valori del\r\nticket corrispondenti.\r\nA questo punto abbiamo una mappa che per ogni ticket ci fornisce il blocco di istruzioni utili associato. Partendo\r\ndal ticket iniziale otteniamo il prossimo blocco di istruzioni utili. Tramite la funzione dfs_reach_dispatcher_end\r\ntroviamo la lista di nodi blu raggiungibili. I nodi intermedi non sono analizzati in quanto non necessari. Ogni nodo\r\nblu raggiunto viene semplificato e dalle istruzioni risultati viene determinato il prossimo ticket. In alcuni casi il\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 10 of 17\n\nvalore del prossimo ticket dipende da una condizione e per fortuna in questi casi il formato usato è sempre il solito\r\ne può essere gestito riconoscendolo esplicitamente. Ottenuti i possibili ticket del nodo blu corrente, modifichiamo\r\ni valori next e branch di questo e continuiamo l’analisi dai prossimi valori.\r\nLa semplificazione delle istruzioni consiste nelle seguenti operazioni ripetute in ciclo fino a che non vi sono\r\nulteriori operazioni da svolgere:\r\nRiscrittura degli zero idiom (es: xor eax, eax -\u003e mov eax, 0 )\r\nRiscrittura delle load da indirizzi noti (es: mov eax, dword_X -\u003e mov eax, \u003cdato ad X\u003e )\r\nFolding delle costanti da mov reg1, imm1 ad istruzioni aritmetiche tipo add reg1, imm2 o add reg2,\r\nreg1 o a store su variabili locali.\r\nRimozione di dead stores.\r\nRiscrittura di imul reg1, reg2 se reg1 e reg2 hanno valore costante.\r\nMerge di istruzioni add e sub sul solito registro.\r\nRiscrittura di cmp/setcc e test/cmovcc .\r\nL’implementazione di queste ottimizzazioni richiede un po’ di lavoro ma permette di trasformare il blocco blu\r\nmostrato ad esempio sopra (che contiene 45 istruzioni) nella singola istruzione mov dword ptr [rbp-38h],8858DF56h !\r\nUna volta riscritto il PE (abbiamo ignorato questioni legate allo spazio e rilocazioni, trattandosi di un PE a 64 bit)\r\nil CFG ottenuto è decisamente più semplice:\r\nIl flusso di esecuzione del packer una volta deoffuscato\r\nSebbene le istruzioni che effettuano operazioni a 64 bit non siano state semplificate è comunque possibile\r\nintravedere il comportamento del packer. E’ innanzi tutto interessante notare come venga usato yandex.com per\r\ndeterminare se la vittima ha accesso ad internet:\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 11 of 17\n\nControllo di connettività tramite yandex.com\r\nAnalizzando il codice, si vedono le istruzioni con le quali il packer recupera i dati dalla sezione .data e queste\r\nconfermano quanto scoperto tramite la crittoanalisi elementare:\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 12 of 17\n\nCodice che conferma che la chiave ha lunghezza 20 byte e si trova dopo la dimensione del payload.\r\nQuesto tipo di analisi statica è piuttosto laboriosa ed è generalmente fattibile solo in una seconda fase. Ha\r\nl’innegabile vantaggio di mostrare il vero codice del malware in oggetto, spesso necessario per determinare le TTP\r\nusate e non cadere vittima di comportamenti decoy.\r\nIl malware Strela\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 13 of 17\n\nA discapito della complessità del packer, il malware Strela è estremamente semplice.\r\nStrela è un malware minimale che ha come uniche funzionalità quelle di rubare gli account di posta da\r\nThunderbird ed Outlook.\r\nNon presenta alcun tipo di offuscazione, il C2 è in chiaro nel malware, così come tutte le altre stringhe. Il codice\r\ndella funzione principale del malware sta tutto nell’immagine sotto:\r\nIl codice di Strela\r\nIl malware si assicura che una sola istanza sia in esecuzione tramite un mutex (il cui nome è dato dal nome del\r\ncomputer della vittima XORato con la stringa strela, da cui il nome del malware). Dopodichè ruba gli account di\r\nThunderbird ed Outlook, mostra un messaggio di errore all’utente (in un italiano non proprio idiomatico) e\r\ntermina.\r\nIl codice della funzione steal_firefox mostra come Strela cerchi il primo profilo Thunderbird tramite\r\nFindFirstFileA e da questo legga i file logins.json e key4.db . Il seguente blocco di codice crea il buffer da\r\ninviare al C2.\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 14 of 17\n\nIl formato del buffer con i dati di Thunderbird.\r\nIl formato usato è \u003cfirma\u003e\u003cdimensione logins.json\u003e\u003cdati logins.json\u003e\u003cdati key4.db\u003e , dove firma è a stringa\r\nFF e la dimensione è una DWORD (in little endian).\r\nPer i dati di Outlook il formato è ancor a più semplice: dopo la firma OL è presente una serie di righe \u003cserver\u003e,\r\n\u003cusername\u003e, \u003cpassword\u003e .\r\nFormato dei dati inviati al C2\r\nUna volta ottenuto un buffer da inviare al C2, Strela usa la chiave 7a7dd62b-c4ea-4bbb-9f3f-2e6d58aada40 per\r\ncifrarlo tramite rolling XOR. Il risultato è inviato tramite POST all’URL http ://91 . 215 . 85. 209 / server .php.\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 15 of 17\n\nSe il formato è corretto, il C2 risponde con una sequenza casuale di byte terminata dalla stringa KH.\r\nIl C2\r\nIl C2 è localizzato in Russia, apparentemente di proprietà di una società di nome Prospero registrata nel novembre\r\ndel 2022 e di cui non si trovano servizi online (forse è un servizio di hosting). Da una rapida indagine OSINT si\r\ntrovano evidenze che sullo stesso ASN sono stati ospitati, dai primi del 2023, C2 di altre campagne malware.\r\nInviando al C2 un pacchetto corretto, questo risponde con una sequenza casuale di byte terminati dalla stringa KH.\r\nLa risposta del C2 ad un pacchetto valido.\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 16 of 17\n\nSe invece inviamo un pacchetto non valido, la risposta è vuota.\r\nMa cosa constituisce un pacchetto valido? Consideriamo il pacchetto per l’invio dei dati di Thunderbird, poichè è\r\npiù complesso. Ovviamente devono essere presenti la firma e la dimensione del primo file (logins.json) ma…\r\nsono eseguiti ulteriori controlli?\r\nDa una rapida serie di tentativi si evincono le seguenti evidenze:\r\n1. Il C2 ritorna una risposta vuota se il contenuto di logins.json non è un JSON valido.\r\n2. Il C2 ritorna una risposta vuota se il contenuto di key4.db non è un DB SQLite valido.\r\n3. Il C2 ritorna una risposta vuota se il database key4.db non contiene dati.\r\nIl terzo punto ed il primo punto sono interessanti perchè indicano che il C2 processa i dati online. Era auspicabile\r\nche i file venissero semplicemente salvati, invece sono processati al momento della loro ricezione e l’esito viene\r\nritornato al client sotto forma di risposta vuota (in caso di errore) o meno.\r\nQuesto permette di avere un oracolo e, associato al fatto che vengono effettuate delle query su un DB SQLite,\r\npermetterebbe di recuperare alcune informazioni.\r\nIdealmente, si potrebbe seguire una linea simile a quella delineata dall’ottimo articolo sul QOP di Checkpoint, ma\r\nle recenti versioni di SQLite non hanno vulnerabilità usabili come oracolo e inoltre la versione SQLite3 usata è la\r\n3.34.1 per la quale non valgono le CVE identificate nell’articolo di Checkpoint. Rimane il fatto che l’azione del\r\nC2 di fare query su un DB controllato dal client possa comunque essere un punto di partenza per ulteriori analisi\r\nin seguito.\r\nSource: https://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nhttps://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/\r\nPage 17 of 17",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://cert-agid.gov.it/news/analisi-tecnica-e-considerazioni-sul-malware-strela/"
	],
	"report_names": [
		"analisi-tecnica-e-considerazioni-sul-malware-strela"
	],
	"threat_actors": [],
	"ts_created_at": 1775434037,
	"ts_updated_at": 1775791221,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/ec33c210d52f58eb456e4da8dce34f34d2144d09.pdf",
		"text": "https://archive.orkl.eu/ec33c210d52f58eb456e4da8dce34f34d2144d09.txt",
		"img": "https://archive.orkl.eu/ec33c210d52f58eb456e4da8dce34f34d2144d09.jpg"
	}
}