A caccia di macro per ufficio con Sysmon e Panda.
Usando Panda e Jupyter ovunque
È divertente come quando parli di macro per l'ufficio, quelli di noi nel campo della sicurezza hanno un brivido lungo la schiena. Sappiamo tutti che ci sono diversi modi per proteggere un'azienda da loro, ma una cosa di cui sono sicuro è che, se dipendesse da noi, preferiremmo tutti che le macro fossero disabilitate nella nostra organizzazione.
Nel bene e nel male, queste decisioni devono essere molto ben motivate e, soprattutto, l'impatto sull'azienda deve essere misurato molto bene prima di poter prendere qualsiasi decisione.
Per controllare l'uso delle macro non ci sono molte opzioni se non disponiamo di servizi cloud come O365 ma lo proveremo.
Iniziamo l'avventura.
Primo tentativo, estensione file.
L'approccio semplice potrebbe essere quello di cercare nella nostra organizzazione file con macro cercando l'estensione del file, ma non è mai così facile.
Una cosa che è importante sapere, e che molte persone non sanno, è che affinché un file di Office contenga macro deve avere estensioni di file molto specifiche e altre estensioni sono incompatibili con esse.
Per molti anni, Microsoft ha voluto identificare i documenti contenenti macro con la lettera "m" nell'estensione come docm, xlsm... e rendere impossibile l'esecuzione di macro nelle nuove estensioni contenenti la lettera "x" alla fine come docx, xlsx... ma come sempre con Microsoft, la compatibilità con le versioni precedenti ha fatto la sua parte qui.
Nel formato di documento Office 97–2003 è consentito archiviare ed eseguire macro mantenendo estensioni semplici come doc, xls... rendendo impossibile per gli analisti della sicurezza sapere se un documento può contenere macro o meno utilizzando solo l'estensione del file.
Secondo tentativo, regola Sygma e Sysmon
Dopo una conversazione con altri professionisti, mi suggeriscono una regola Sygma scritta da Florian Roth . In questa regola è possibile vedere che ci sono 3 librerie che l'ufficio carica quando un file contiene macro VBA.
- '*\VBE7.DLL*'
- '*\VBEUI.DLL*'
- '*\VBE7INTL.DLL*'
Questo sembrava promettente se hai Sysmon o puoi distribuirlo.
<RuleGroup name="" groupRelation="or">
<ImageLoad onmatch="include">
<Rule name="Potential Macro file opened" groupRelation="or">
<ImageLoaded condition="end with">vbeintl.dll</ImageLoaded>
<ImageLoaded condition="end with">vbe7.dll</ImageLoaded>
<ImageLoaded condition="end with">vbeui.dll</ImageLoaded>
</Rule>
</ImageLoad>
</RuleGroup>
<RuleGroup name="" groupRelation="or">
<RegistryEvent onmatch="include">
<TargetObject name="T1060,RunKey" condition="contains">Documents\TrustRecords</TargetObject>
</RegistryEvent>
</RuleGroup>
Mi è venuto in mente di testare la regola Sygma con diversi file di Office e verificare cosa è successo se questi file sono stati scaricati o meno e questi erano i risultati.
Sembra che neanche il nostro secondo approccio sia valido. Sebbene le librerie VBE7.dll e VBEUI.dll servano per identificare i file con macro create localmente, quando un file viene scaricato da Internet, anche queste librerie vengono caricate, anche se il file non contiene macro.
Il colpevole di questo comportamento è il Mark-of-the-Web ( MOTW ) o il flusso di dati alternativo aggiunto ai file quando vengono scaricati tramite browser Web, che fa sì che i file vengano aperti in una visualizzazione protetta.
Poiché questo primo tentativo non ha funzionato, ho pensato di provare a utilizzare lo stesso approccio ma con un ambito più ampio, ovvero guardare tutte le librerie caricate da Excel in ciascuna di queste esecuzioni e osservarne le differenze.
Terzo e ultimo tentativo, Python e Panda
Chiunque abbia giocato con Procmon e il caricamento delle librerie saprà che questo può essere un po' noioso poiché ogni esecuzione di Excel carica centinaia di librerie. Ad aiutarci in questo compito abbiamo il nostro terzo protagonista Python con il suo fedele amico Jupyter .
Utilizzeremo gli eventi di caricamento di Sysmon e DLL per trovare differenze tra le esecuzioni di Office, in questo modo potremmo identificare differenze che potrebbero indicare che un documento aperto contenga o meno macro, indipendentemente dalla sua estensione.
Innanzitutto la regola Sysmon per monitorare le librerie caricate dal file Excel durante l'apertura di un documento.
<RuleGroup name="" groupRelation="or">
<ImageLoad onmatch="include">
<Rule name="Image Loaded by Excel" groupRelation="or">
<Image condition="end with">excel.exe</Image>
</Rule>
</ImageLoad>
</RuleGroup>
Per analizzare i file EVTX risultanti da Sysmon useremo la libreria PyEvtxParser , una vecchia conoscenza che mi è stata molto utile in passato quando ho scritto Grafiki . Qui puoi trovare questo Notebook sul mio Github tra gli altri.
def evtx_folder_to_dataframes(directory):
dataframes_list_seven = {}
for filename in os.listdir(directory):
f = os.path.join(directory, filename)
if os.path.isfile(f):
events_one_five = []
events_seven = []
a = open(f, 'rb')
parser = PyEvtxParser(a)
for record in parser.records_json():
event = json.loads(record['data'])
#Image loaded
if event["Event"]["System"]["EventID"] == 7:
event_list = [event["Event"]["EventData"]["UtcTime"],
event["Event"]["System"]["EventID"],
event["Event"]["EventData"]["ImageLoaded"],
event["Event"]["EventData"]["FileVersion"],
event["Event"]["EventData"]["Description"],
event["Event"]["EventData"]["Product"],
event["Event"]["EventData"]["Company"],
event["Event"]["EventData"]["OriginalFileName"],
event["Event"]["EventData"]["Hashes"],
event["Event"]["EventData"]["Signed"],
event["Event"]["EventData"]["Signature"],
event["Event"]["EventData"]["SignatureStatus"]]
events_seven.append(event_list)
name = filename.split("\\")[-1].split(".")[-2]
df_7 = pd.DataFrame.from_records(events_seven,
columns=['UtcTime','EventID', 'ImageLoaded', 'FileVersion', 'Description',
'Product', 'Company', 'OriginalFileName', 'Hashes', 'Signed',
'Signature', 'SignatureStatus'])
dataframes_list_seven[name] = df_7
return dataframes_list_seven
df_count = pd.DataFrame(index=list(dataframes_list_seven.keys()), columns=["Count"])
for e in list(dataframes_list_seven):
df_count["Count"][e] = dataframes_list_seven[e]["ImageLoaded"].nunique()
DLL count results
La prossima domanda che ci poniamo è: ci saranno librerie diverse tra queste esecuzioni che ci consentiranno di determinare che un documento ha macro?
Per fare ciò, ciò che faremo è confrontare tutte le misurazioni, creando così una tabella comparativa che ci consentirà di rilevare le anomalie. Nella parte sinistra della tabella avremo la base del nostro comparativo, ovvero la parte “Quali librerie contengono questa esecuzione” e nella parte superiore “Quali non contengono questa esecuzione”.
Per questo useremo questa semplice funzione che ci permette di fare un confronto di DataFrames e manterremo solo quelli che sono nel primo operatore.
def compare_df(df_in, df_not_in):
list = df_in.merge(df_not_in.drop_duplicates(),
on=['ImageLoaded'],
how='left',
indicator=True)
list_min = list[list['_merge'] == 'left_only']["ImageLoaded"].unique()
return(list_min)
df = pd.DataFrame(index=list(dataframes_list_seven.keys()), columns=list(dataframes_list_seven.keys()))
for e in list(dataframes_list_seven.keys()):
for i in list(dataframes_list_seven.keys()):
list_min = compare_df(dataframes_list_seven[e], dataframes_list_seven[i])
df[i][e] = len(list_min)
Comparation result
A questo punto possiamo trarre la conclusione che questo modo non è valido per il rilevamento di documenti Office con macro. Una grande scoperta che può risparmiarci di dedicare molto più tempo a implementare regole, analizzare risultati e trarre conclusioni su dati che ritenevamo attendibili.
Bonus
Poiché l'idea con Jupyter è quella di generare documenti riutilizzabili. Implementeremo un piccolo menu che ci aiuterà a vedere in dettaglio ciascuno dei confronti.
Forse in questo caso non è molto rilevante ma questo notebook è facilmente adattabile per monitorare altri tipi di esecuzioni rilevando ad esempio differenze nelle connessioni di rete, modifiche alle chiavi di registro, creazione di named pipe…
def compare_events():
import ipywidgets as widgets
from IPython.display import display, Markdown, clear_output
pd.set_option('display.max_rows', 500)
output = widgets.Output()
dfs = list(dataframes_list_seven.keys())
columns = dataframes_list_seven[list(dataframes_list_seven.keys())[0]].columns
in_widget = widgets.Dropdown(
options=dfs,
description='Events in:',
disabled=False)
not_in_widget = widgets.Dropdown(
options=dfs,
description='And not in:',
disabled=False)
columns = widgets.Dropdown(
options=columns,
description='Column:',
disabled=False)
button = widgets.Button(description=f'List')
display(in_widget, not_in_widget, columns, button, output)
def _click_function(_):
with output:
clear_output()
list = dataframes_list_seven[in_widget.value].merge(dataframes_list_seven[not_in_widget.value].drop_duplicates(),
on=['ImageLoaded'],
how='left',
indicator=True)
list_min = list[list['_merge'] == 'left_only'][columns.value].unique()
display(len(list_min))
display(pd.DataFrame(list_min).style.set_properties(**{'text-align': 'left'}))
button.on_click(_click_function)
Spero che vi sia piaciuto e che, come ho fatto io, non vi fidate dei miei risultati e abbiate il coraggio di provarlo da soli, se lo fate e i risultati sono diversi, per favore fatemelo sapere
Arrivederci alla prossima!

![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































