Quando cerchiamo di individuare e bloccare una minaccia informatica, la prima cosa che ci viene in mente sono spesso gli IoC (Indicator of Compromise), ovvero dati atomici che rappresentano informazioni tecniche specifiche, come hash di file, indirizzi IP, domini, stringhe presenti all’interno di un eseguibile, ecc… Sebbene questi indicatori siano molto utili per identificare attività malevole, non sono sufficienti per prevenire eventuali varianti della stessa minaccia, poiché possono essere facilmente modificati dagli attaccanti. Ad esempio, è sufficiente modificare l’hash di un eseguibile malevolo oppure registrare un nuovo dominio per un server C&C, e l’indicatore smette di essere utile.
Per superare questo problema, si ricorre agli indicatori comportamentali, i quali si basano sull’analisi dei comportamenti osservati da una minaccia. Grazie a questi indicatori è possibile creare regole di detection molto più robuste, in grado di rilevare anche eventuali variazioni di una stessa campagna.
Tra gli indicatori comportamentali troviamo anche l’entropia, una funzione matematica utile a misurare il grado di “casualità” di una stringa.
In parole semplici, più una stringa contiene caratteri casuali, più la sua entropia sarà alta. Questo parametro può aiutarci a riconoscere elementi anomali, come nomi di file o domini generati automaticamente da malware, che appaiono “strani” proprio perché troppo casuali rispetto al normale.
Shannon Entropy
In informatica, l’entropia è un concetto preso in prestito dalla teoria dell’informazione di Claude Shannon.
Per calcolare l’entropia di Shannon di una stringa, si utilizza la formula:

dove:
- “pi” è la probabilità di occorrenza del carattere “i”
- la somma è su tutti i caratteri distinti della stringa
Esempio:
Stringa: “cijjdejdhekhflm”
Lunghezza totale: 15
Frequenza dei caratteri:

Calcolo dell’entropia:

Come facciamo a capire se il valore dell’entropia è alto oppure no?
Per rispondere a questa domanda dobbiamo prima conoscere qual è il valore massimo che l’entropia può assumere.
Purtroppo, questo valore non è unico, ma dipende dal tipo di alfabeto utilizzato per generare le stringhe.
In altre parole, il valore massimo dell’entropia varia in base al numero di simboli distinti presenti nell’alfabeto, indicato con la lettera k:


Perché l’entropia è utile nel Threat Hunting
Nel Threat Hunting, l’obiettivo non è soltanto rilevare minacce già conosciute, ma soprattutto scovare comportamenti anomali che potrebbero indicare attività malevole ancora sconosciute.
È proprio in questo contesto che l’entropia diventa uno strumento particolarmente utile. Molti malware e strumenti utilizzati dagli attaccanti generano automaticamente nomi di file, domini, chiavi di registro e altri artefatti pseudo-casuali per eludere i controlli basati su firme o Indicatori di Compromissione (IoC).
Calcolando l’entropia di questi elementi, ad esempio dei domini contattati dai dispositivi aziendali, un analista può individuare valori anomali e riconoscere pattern che risultano “troppo casuali” per essere considerati legittimi.
L’entropia può essere applicata a qualsiasi tipo di dato, come nomi di file, domini, URL, ecc… per capire quanto questi si discostano da ciò che normalmente ci si aspetterebbe di vedere in un sistema legittimo.
Alcuni Esempi pratici
- Rilevazione di domini DGA (Domain Generation Algorithm):
Alcune famiglie di malware utilizzano algoritmi di Domain Generation Algorithm (DGA) per creare migliaia di nomi di dominio casuali con cui tentare la comunicazione con i server di Command and Control (C2). Poiché tali domini presentano generalmente un’elevata entropia, la loro identificazione può essere automatizzata attraverso il calcolo dell’entropia. - Analisi di file sospetti:
File o eseguibili con nomi apparentemente casuali, possono rappresentare un possibile segnale di attività sospetta. Un valore di entropia elevato può aiutare a mettere in evidenza questi artefatti, soprattutto quando si discostano da pattern più tipici e coerenti presenti nell’ambiente.È importante però ricordare che un nome “casuale” non è di per sé un indicatore di compromissione: può derivare anche da processi legittimi, packaging automatizzati o software che utilizza naming non descrittivo. L’entropia, quindi, non identifica una minaccia, ma contribuisce a selezionare elementi che meritano un’analisi più approfondita. - Analisi delle Command Line:
Un altro ambito in cui l’entropia può essere utile è l’analisi delle command line dei processi. In un ambiente aziendale, molti comandi legittimi tendono a essere relativamente strutturati e ripetitivi, ad esempio:
powershell.exe -ExecutionPolicy Bypass -File install.ps1cmd.exe /c "net use Z: \\fileserver\share"msiexec.exe /i agent.msi /quiet
Questi comandi, pur variando nei parametri, mantengono una struttura riconoscibile e spesso prevedibile. Al contrario, in contesti malevoli o di offuscamento si possono osservare command line molto più “rumorose” o apparentemente casuali, ad esempio:
powershell.exe -enc JABjAGwAaQBlAG4AdAAgAD0A...rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";x=...cmd.exe /c "set a=7f3k9d2j1&& set b=9xq2...&& %a%%b%"
In questi casi, il calcolo dell’entropia sulla command line può aiutare a evidenziare stringhe insolite rispetto al comportamento medio degli endpoint dell’organizzazione.
Un valore elevato può quindi indicare la presenza di offuscamento, encoding (es. Base64), concatenazioni anomale o variabili costruite dinamicamente, tutte tecniche frequentemente utilizzate per nascondere l’intento reale del comando.
Tuttavia, anche qui è fondamentale evitare interpretazioni automatiche: alcune applicazioni legittime (installer, script di deployment, tool di automazione) possono generare command line complesse o apparentemente “rumorose”. L’entropia, quindi, non identifica un attacco, ma aiuta a dare priorità ai casi che meritano analisi approfondita.
Conclusioni
L’entropia è uno strumento efficace per valutare il livello di ordine o casualità presente in un insieme di dati. In particolare, l’entropia di Shannon permette di quantificare quanto le informazioni siano prevedibili o meno, rappresentando un ottimo punto di partenza per molte analisi.
Tuttavia, in alcuni scenari non è sufficiente sapere quanto un dato sia casuale: è altrettanto importante capire quanto si discosti da ciò che normalmente ci si aspetta.
In questi casi entra in gioco l’entropia relativa, una misura che consente di confrontare il comportamento osservato con quello atteso e di evidenziare in modo più preciso eventuali differenze.
Per questo motivo, sebbene l’entropia di Shannon rimanga una metrica fondamentale, l’entropia relativa rappresenta un’evoluzione naturale che permette di ottenere analisi più accurate e contestualizzate. Nel prossimo articolo approfondiremo proprio questo concetto e vedremo come applicarlo.
Entropy in CrowdStrike
shannonEntropy()
Entropy in Splunk
ut_shannon()
Entropy in SentinelOne
Questa query è valida per le stringe composte dall’alfabeta [a-z]
//Query created by the 8BitSecurity team|let string = <STRING>//max value of Shannon Entropy in this case is log(26)|let count_a = len(string.extract_matches('a'))|let count_b = len(string.extract_matches('b'))|let count_c = len(string.extract_matches('c'))|let count_d = len(string.extract_matches('d')) |let count_e = len(string.extract_matches('e'))|let count_f = len(string.extract_matches('f'))|let count_g = len(string.extract_matches('g'))|let count_h = len(string.extract_matches('h'))|let count_i = len(string.extract_matches('i'))|let count_j = len(string.extract_matches('j'))|let count_k = len(string.extract_matches('k'))|let count_l = len(string.extract_matches('l'))|let count_m = len(string.extract_matches('m'))|let count_n = len(string.extract_matches('n'))|let count_o = len(string.extract_matches('o'))|let count_p = len(string.extract_matches('p'))|let count_q = len(string.extract_matches('q'))|let count_r = len(string.extract_matches('r'))|let count_s = len(string.extract_matches('s'))|let count_t = len(string.extract_matches('t'))|let count_u = len(string.extract_matches('u'))|let count_v = len(string.extract_matches('v'))|let count_w = len(string.extract_matches('w'))|let count_x = len(string.extract_matches('x'))|let count_y = len(string.extract_matches('y'))|let count_z = len(string.extract_matches('z'))|let size = count_a + count_b + count_c + count_d + count_e + count_f + count_g + count_h + count_i + count_j + count_k + count_l + count_m + count_n + count_o + count_p + count_q + count_r + count_s + count_t + count_u + count_v + count_w + count_x + count_y + count_z// Carattere a|let prob_a = count_a > 0 ? count_a / size : 0|let entropy_a = count_a > 0 ? (prob_a) * log(prob_a, 2) : 0|let entropy_a_tot = count_a > 0 ? entropy_a : 0// Carattere b|let prob_b = count_b > 0 ? count_b / size : 0|let entropy_b = count_b > 0 ? (prob_b) * log(prob_b, 2) : 0|let entropy_b_tot = count_b > 0 ? entropy_b : 0// Carattere c|let prob_c = count_c > 0 ? count_c / size : 0|let entropy_c = count_c > 0 ? (prob_c) * log(prob_c, 2) : 0|let entropy_c_tot = count_c > 0 ? entropy_c : 0// Carattere d|let prob_d = count_d > 0 ? count_d / size : 0|let entropy_d = count_d > 0 ? (prob_d) * log(prob_d, 2) : 0|let entropy_d_tot = count_d > 0 ? entropy_d : 0// Carattere e|let prob_e = count_e > 0 ? count_e / size : 0|let entropy_e = count_e > 0 ? (prob_e) * log(prob_e, 2) : 0|let entropy_e_tot = count_e > 0 ? entropy_e : 0// Carattere f|let prob_f = count_f > 0 ? count_f / size : 0|let entropy_f = count_f > 0 ? (prob_f) * log(prob_f, 2) : 0|let entropy_f_tot = count_f > 0 ? entropy_f : 0// Carattere g|let prob_g = count_g > 0 ? count_g / size : 0|let entropy_g = count_g > 0 ? (prob_g) * log(prob_g, 2) : 0|let entropy_g_tot = count_g > 0 ? entropy_g : 0// Carattere h|let prob_h = count_h > 0 ? count_h / size : 0|let entropy_h = count_h > 0 ? (prob_h) * log(prob_h, 2) : 0|let entropy_h_tot = count_h > 0 ? entropy_h : 0// Carattere i|let prob_i = count_i > 0 ? count_i / size : 0|let entropy_i = count_i > 0 ? (prob_i) * log(prob_i, 2) : 0|let entropy_i_tot = count_i > 0 ? entropy_i : 0// Carattere j|let prob_j = count_j > 0 ? count_j / size : 0|let entropy_j = count_j > 0 ? (prob_j) * log(prob_j, 2) : 0|let entropy_j_tot = count_j > 0 ? entropy_j : 0// Carattere k|let prob_k = count_k > 0 ? count_k / size : 0|let entropy_k = count_k > 0 ? (prob_k) * log(prob_k, 2) : 0|let entropy_k_tot = count_k > 0 ? entropy_k : 0// Carattere l|let prob_l = count_l > 0 ? count_l / size : 0|let entropy_l = count_l > 0 ? (prob_l) * log(prob_l, 2) : 0|let entropy_l_tot = count_l > 0 ? entropy_l : 0// Carattere m|let prob_m = count_m > 0 ? count_m / size : 0|let entropy_m = count_m > 0 ? (prob_m) * log(prob_m, 2) : 0|let entropy_m_tot = count_m > 0 ? entropy_m : 0// Carattere n|let prob_n = count_n > 0 ? count_n / size : 0|let entropy_n = count_n > 0 ? (prob_n) * log(prob_n, 2) : 0|let entropy_n_tot = count_n > 0 ? entropy_n : 0// Carattere o|let prob_o = count_o > 0 ? count_o / size : 0|let entropy_o = count_o > 0 ? (prob_o) * log(prob_o, 2) : 0|let entropy_o_tot = count_o > 0 ? entropy_o : 0// Carattere p|let prob_p = count_p > 0 ? count_p / size : 0|let entropy_p = count_p > 0 ? (prob_p) * log(prob_p, 2) : 0|let entropy_p_tot = count_p > 0 ? entropy_p : 0// Carattere q|let prob_q = count_q > 0 ? count_q / size : 0|let entropy_q = count_q > 0 ? (prob_q) * log(prob_q, 2) : 0|let entropy_q_tot = count_q > 0 ? entropy_q : 0// Carattere r|let prob_r = count_r > 0 ? count_r / size : 0|let entropy_r = count_r > 0 ? (prob_r) * log(prob_r, 2) : 0|let entropy_r_tot = count_r > 0 ? entropy_r : 0// Carattere s|let prob_s = count_s > 0 ? count_s / size : 0|let entropy_s = count_s > 0 ? (prob_s) * log(prob_s, 2) : 0|let entropy_s_tot = count_s > 0 ? entropy_s : 0// Carattere t|let prob_t = count_t > 0 ? count_t / size : 0|let entropy_t = count_t > 0 ? (prob_t) * log(prob_t, 2) : 0|let entropy_t_tot = count_t > 0 ? entropy_t : 0// Carattere u|let prob_u = count_u > 0 ? count_u / size : 0|let entropy_u = count_u > 0 ? (prob_u) * log(prob_u, 2) : 0|let entropy_u_tot = count_u > 0 ? entropy_u : 0// Carattere v|let prob_v = count_v > 0 ? count_v / size : 0|let entropy_v = count_v > 0 ? (prob_v) * log(prob_v, 2) : 0|let entropy_v_tot = count_v > 0 ? entropy_v : 0// Carattere w|let prob_w = count_w > 0 ? count_w / size : 0|let entropy_w = count_w > 0 ? (prob_w) * log(prob_w, 2) : 0|let entropy_w_tot = count_w > 0 ? entropy_w : 0// Carattere x|let prob_x = count_x > 0 ? count_x / size : 0|let entropy_x = count_x > 0 ? (prob_x) * log(prob_x, 2) : 0|let entropy_x_tot = count_x > 0 ? entropy_x : 0// Carattere y|let prob_y = count_y > 0 ? count_y / size : 0|let entropy_y = count_y > 0 ? (prob_y) * log(prob_y, 2) : 0|let entropy_y_tot = count_y > 0 ? entropy_y : 0// Carattere z|let prob_z = count_z > 0 ? count_z / size : 0|let entropy_z = count_z > 0 ? (prob_z) * log(prob_z, 2) : 0|let entropy_z_tot = count_z > 0 ? entropy_z : 0|let Array_Shannon_Entropy = array(entropy_a_tot, entropy_b_tot, entropy_c_tot, entropy_d_tot, entropy_e_tot, entropy_f_tot, entropy_g_tot, entropy_h_tot, entropy_i_tot, entropy_j_tot, entropy_k_tot, entropy_l_tot, entropy_m_tot, entropy_n_tot, entropy_o_tot, entropy_p_tot, entropy_q_tot, entropy_r_tot, entropy_s_tot, entropy_t_tot, entropy_u_tot, entropy_v_tot, entropy_w_tot, entropy_x_tot, entropy_y_tot, entropy_z_tot )|let Shannon_Entropy= abs(Array_Shannon_Entropy.sum())|group count() by Shannon_Entropy, string






