Python Digital Forensics - Guida rapida
Questo capitolo ti fornirà un'introduzione a cosa sia la digital forensics e la sua revisione storica. Capirai anche dove puoi applicare la digital forensics nella vita reale e i suoi limiti.
Cos'è la Digital Forensics?
La digital forensics può essere definita come la branca della scienza forense che analizza, esamina, identifica e recupera le prove digitali che risiedono su dispositivi elettronici. È comunemente usato per il diritto penale e le indagini private.
Ad esempio, puoi fare affidamento su prove di estrazione forense digitale nel caso in cui qualcuno rubi alcuni dati su un dispositivo elettronico.
Breve rassegna storica della digital forensics
La storia dei crimini informatici e la revisione storica della digital forensics è spiegata in questa sezione come indicato di seguito:
Anni '70 -'80: primo crimine informatico
Prima di questo decennio, nessun crimine informatico è stato riconosciuto. Tuttavia, se si suppone che ciò accada, le leggi allora esistenti se ne occupano. Successivamente, nel 1978, il primo crimine informatico fu riconosciuto nel Florida Computer Crime Act, che includeva la legislazione contro la modifica o la cancellazione non autorizzata dei dati su un sistema informatico. Ma nel tempo, a causa del progresso della tecnologia, è aumentata anche la gamma di crimini informatici commessi. Per affrontare i crimini legati al diritto d'autore, alla privacy e alla pornografia infantile, sono state approvate varie altre leggi.
Anni '80 -'90: Decennio di sviluppo
Questo decennio è stato il decennio di sviluppo per la digital forensics, tutto a causa della prima indagine in assoluto (1986) in cui Cliff Stoll ha rintracciato l'hacker di nome Markus Hess. Durante questo periodo, si svilupparono due tipi di discipline forensi digitali: la prima era con l'aiuto di strumenti e tecniche ad-hoc sviluppate da professionisti che l'hanno presa come hobby, mentre la seconda è stata sviluppata dalla comunità scientifica. Nel 1992, il termine“Computer Forensics”è stato utilizzato nella letteratura accademica.
2000-2010: Decennio di standardizzazione
Dopo lo sviluppo della digital forensics a un certo livello, c'era la necessità di creare alcuni standard specifici che possono essere seguiti durante lo svolgimento delle indagini. Di conseguenza, vari enti e agenzie scientifiche hanno pubblicato linee guida per la digital forensics. Nel 2002, il gruppo di lavoro scientifico sull'evidenza digitale (SWGDE) ha pubblicato un documento intitolato "Best practice for Computer Forensics". Un altro fiore all'occhiello è stato un trattato internazionale guidato dall'Europa“The Convention on Cybercrime”è stato firmato da 43 nazioni e ratificato da 16 nazioni. Anche dopo tali standard, c'è ancora la necessità di risolvere alcuni problemi che sono stati identificati dai ricercatori.
Processo di Digital Forensics
Dal primo crimine informatico in assoluto nel 1978, c'è stato un enorme incremento nelle attività criminali digitali. A causa di questo incremento, è necessario un modo strutturato per gestirli. Nel 1984 è stato introdotto un processo formalizzato e successivamente è stato sviluppato un gran numero di processi di indagine forense informatica nuovi e migliorati.
Un processo di indagine informatica forense prevede tre fasi principali, come spiegato di seguito:
Fase 1: acquisizione o imaging di oggetti in esposizione
La prima fase della digital forensics prevede il salvataggio dello stato del sistema digitale in modo che possa essere analizzato successivamente. È molto simile a scattare fotografie, campioni di sangue ecc. Da una scena del crimine. Ad esempio, implica l'acquisizione di un'immagine delle aree allocate e non allocate di un disco rigido o di una RAM.
Fase 2: analisi
L'input di questa fase sono i dati acquisiti in fase di acquisizione. Qui, questi dati sono stati esaminati per identificare le prove. Questa fase fornisce tre tipi di prove come segue:
Inculpatory evidences - Queste prove supportano una data storia.
Exculpatory evidences - Queste prove contraddicono una data storia.
Evidence of tampering- Queste prove mostrano che il sistema è stato temperato per evitare l'identificazione. Include l'esame dei file e del contenuto della directory per il recupero dei file eliminati.
Fase 3: presentazione o reportistica
Come suggerisce il nome, questa fase presenta la conclusione e le prove corrispondenti dell'indagine.
Applicazioni di Digital Forensics
La digital forensics si occupa di raccogliere, analizzare e preservare le prove contenute in qualsiasi dispositivo digitale. L'uso della digital forensics dipende dall'applicazione. Come accennato in precedenza, viene utilizzato principalmente nelle seguenti due applicazioni:
Diritto penale
Nel diritto penale, le prove vengono raccolte per sostenere o opporsi a un'ipotesi in tribunale. Le procedure forensi sono molto simili a quelle utilizzate nelle indagini penali ma con requisiti e limitazioni legali differenti.
Investigazione privata
Principalmente il mondo aziendale utilizza la digital forensics per indagini private. Viene utilizzato quando le aziende sospettano che i dipendenti possano svolgere un'attività illegale sui propri computer contraria alla politica aziendale. La digital forensics fornisce una delle migliori rotte da intraprendere per un'azienda o una persona quando indaga su qualcuno per cattiva condotta digitale.
Filiali di Digital Forensics
La criminalità digitale non è limitata ai soli computer, tuttavia hacker e criminali utilizzano anche piccoli dispositivi digitali come tablet, smartphone, ecc. Su larga scala. Alcuni dispositivi hanno una memoria volatile, mentre altri hanno una memoria non volatile. Quindi, a seconda del tipo di dispositivi, la digital forensics ha i seguenti rami:
Computer Forensics
Questa branca della digital forensics si occupa di computer, sistemi embedded e memorie statiche come le unità USB. Un'ampia gamma di informazioni, dai registri ai file effettivi sull'unità, può essere esaminata in informatica forense.
Mobile Forensics
Si occupa dell'analisi dei dati da dispositivi mobili. Questo ramo è diverso dall'analisi forense dei computer nel senso che i dispositivi mobili hanno un sistema di comunicazione integrato che è utile per fornire informazioni utili relative alla posizione.
Forense di rete
Si occupa del monitoraggio e dell'analisi del traffico di rete del computer, sia locale che WAN (wide area network) ai fini della raccolta di informazioni, raccolta di prove o rilevamento di intrusioni.
Database Forensics
Questa branca della digital forensics si occupa dello studio forense dei database e dei loro metadati.
Competenze richieste per le indagini di Digital Forensics
Gli esaminatori forensi digitali aiutano a rintracciare gli hacker, recuperare i dati rubati, seguire gli attacchi informatici alla loro fonte e aiutare in altri tipi di indagini che coinvolgono i computer. Alcune delle competenze chiave richieste per diventare esaminatore forense digitale come discusso di seguito:
Capacità di pensiero eccezionali
Un investigatore forense digitale deve essere un pensatore eccezionale e dovrebbe essere in grado di applicare diversi strumenti e metodologie su un particolare compito per ottenere l'output. Deve essere in grado di trovare diversi modelli e stabilire correlazioni tra di loro.
Abilità tecniche
Un esaminatore forense digitale deve avere buone capacità tecnologiche perché questo campo richiede la conoscenza della rete, come interagisce il sistema digitale.
Appassionato di Cyber Security
Poiché il campo della digital forensics è incentrato sulla risoluzione dei cyber-crimini e questo è un compito noioso, è necessaria molta passione per qualcuno per diventare un asso dell'investigatore digital forensic.
Capacità di comunicazione
Buone capacità di comunicazione sono indispensabili per coordinarsi con i vari team e per estrarre dati o informazioni mancanti.
Abile nella creazione di rapporti
Dopo aver implementato con successo l'acquisizione e l'analisi, un esaminatore forense digitale deve menzionare tutti i risultati nella relazione finale e nella presentazione. Quindi deve avere buone capacità di redazione di rapporti e attenzione ai dettagli.
Limitazioni
L'indagine forense digitale offre alcune limitazioni, come discusso qui:
Necessità di produrre prove convincenti
Una delle principali battute d'arresto delle indagini forensi digitali è che l'esaminatore deve conformarsi agli standard richiesti per le prove in tribunale, poiché i dati possono essere facilmente manomessi. D'altra parte, l'investigatore forense informatico deve avere una conoscenza completa dei requisiti legali, della gestione delle prove e delle procedure di documentazione per presentare prove convincenti in tribunale.
Strumenti investigativi
L'efficacia dell'indagine digitale risiede interamente sull'esperienza dell'esaminatore forense digitale e sulla selezione dello strumento di indagine appropriato. Se lo strumento utilizzato non è conforme a standard specifici, in tribunale, le prove possono essere negate dal giudice.
Mancanza di conoscenza tecnica tra il pubblico
Un altro limite è che alcuni individui non hanno familiarità con la informatica forense; quindi, molte persone non capiscono questo campo. Gli investigatori devono essere sicuri di comunicare i loro risultati con i tribunali in modo tale da aiutare tutti a comprendere i risultati.
Costo
La produzione di prove digitali e la loro conservazione sono molto costose. Quindi questo processo potrebbe non essere scelto da molte persone che non possono permettersi il costo.
Nel capitolo precedente abbiamo appreso le basi della digital forensics, i suoi vantaggi e limiti. Questo capitolo ti metterà a tuo agio con Python, lo strumento essenziale che stiamo utilizzando in questa indagine forense digitale.
Perché Python for Digital Forensics?
Python è un linguaggio di programmazione popolare e viene utilizzato come strumento per la sicurezza informatica, i test di penetrazione e le indagini forensi digitali. Quando scegli Python come strumento per la digital forensics, non hai bisogno di alcun altro software di terze parti per completare l'attività.
Di seguito vengono fornite alcune delle caratteristiche uniche del linguaggio di programmazione Python che lo rendono adatto ai progetti di digital forensics:
Simplicity of Syntax - La sintassi di Python è semplice rispetto ad altri linguaggi, il che rende più facile l'apprendimento e l'utilizzo per la digital forensics.
Comprehensive inbuilt modules - I moduli integrati completi di Python sono un eccellente aiuto per eseguire un'indagine forense digitale completa.
Help and Support - Essendo un linguaggio di programmazione open source, Python gode di un eccellente supporto da parte della comunità degli sviluppatori e degli utenti.
Caratteristiche di Python
Python, essendo un linguaggio di scripting di alto livello, interpretato, interattivo e orientato agli oggetti, fornisce le seguenti funzionalità:
Easy to Learn - Python è un linguaggio amichevole per gli sviluppatori e facile da imparare, perché ha meno parole chiave e struttura più semplice.
Expressive and Easy to read- Il linguaggio Python è di natura espressiva; quindi il suo codice è più comprensibile e leggibile.
Cross-platform Compatible - Python è un linguaggio compatibile multipiattaforma, il che significa che può essere eseguito in modo efficiente su varie piattaforme come UNIX, Windows e Macintosh.
Interactive Mode Programming - Possiamo eseguire test interattivi e debug del codice perché Python supporta una modalità interattiva per la programmazione.
Provides Various Modules and Functions - Python ha un'ampia libreria standard che ci consente di utilizzare un ricco set di moduli e funzioni per il nostro script.
Supports Dynamic Type Checking - Python supporta il controllo dinamico dei tipi e fornisce tipi di dati dinamici di altissimo livello.
GUI Programming - Python supporta la programmazione GUI per sviluppare interfacce utente grafiche.
Integration with other programming languages - Python può essere facilmente integrato con altri linguaggi di programmazione come C, C ++, JAVA ecc.
Installazione di Python
La distribuzione di Python è disponibile per varie piattaforme come Windows, UNIX, Linux e Mac. Abbiamo solo bisogno di scaricare il codice binario secondo la nostra piattaforma. Nel caso in cui il codice binario per qualsiasi piattaforma non fosse disponibile, dobbiamo avere un compilatore C in modo che il codice sorgente possa essere compilato manualmente.
Questa sezione ti renderà familiare con l'installazione di Python su varie piattaforme
Installazione di Python su Unix e Linux
È possibile seguire i passaggi indicati di seguito per installare Python su una macchina Unix / Linux.
Step 1- Apri un browser web. Digita e inserisci www.python.org/downloads/
Step 2 - Scarica il codice sorgente zippato disponibile per Unix / Linux.
Step 3 - Estrai i file compressi scaricati.
Step 4 - Se desideri personalizzare alcune opzioni, puoi modificare il file Modules/Setup file.
Step 5 - Utilizzare i seguenti comandi per completare l'installazione:
run ./configure script
make
make install
Dopo aver completato con successo i passaggi sopra indicati, Python verrà installato nella sua posizione standard /usr/local/bin e le sue biblioteche all'indirizzo /usr/local/lib/pythonXX dove XX è la versione di Python.
Installazione di Python su Windows
Possiamo seguire i seguenti semplici passaggi per installare Python su macchina Windows.
Step 1- Apri un browser web. Digita e inserisci www.python.org/downloads/
Step 2 - Scarica il programma di installazione di Windows python-XYZ.msi file, dove XYZ è la versione che dobbiamo installare.
Step 3 - Ora esegui quel file MSI dopo aver salvato il file di installazione sul tuo computer locale.
Step 4 - Esegui il file scaricato che aprirà la procedura guidata di installazione di Python.
Installazione di Python su Macintosh
Per installare Python 3 su Mac OS X, dobbiamo utilizzare un programma di installazione del pacchetto denominato Homebrew.
Puoi usare il seguente comando per installare Homebrew, nel caso in cui non lo hai sul tuo sistema -
$ ruby -e "$(curl -fsSL
https://raw.githubusercontent.com/Homebrew/install/master/install)"
Se è necessario aggiornare il gestore di pacchetti, è possibile farlo con l'aiuto del seguente comando:
$ brew update
Ora, usa il seguente comando per installare Python3 sul tuo sistema:
$ brew install python3
Impostazione del PERCORSO
Abbiamo bisogno di impostare il percorso per l'installazione di Python e questo differisce con piattaforme come UNIX, WINDOWS o MAC.
Impostazione del percorso su Unix / Linux
È possibile utilizzare le seguenti opzioni per impostare il percorso su Unix / Linux:
If using csh shell - Genere setenv PATH "$PATH:/usr/local/bin/python" e quindi premere Invio.
If using bash shell (Linux) - Tipo export ATH="$PATH:/usr/local/bin/python" e quindi premere Invio.
If using sh or ksh shell - Genere PATH="$PATH:/usr/local/bin/python" e quindi premere Invio.
Impostazione del percorso in Windows
genere path %path%;C:\Python al prompt dei comandi e quindi premere Invio.
Esecuzione di Python
Puoi scegliere uno dei seguenti tre metodi per avviare l'interprete Python:
Metodo 1: utilizzo dell'interprete interattivo
Un sistema che fornisce un interprete della riga di comando o una shell può essere facilmente utilizzato per avviare Python. Ad esempio, Unix, DOS ecc. È possibile seguire i passaggi indicati di seguito per avviare la codifica nell'interprete interattivo -
Step 1 - Entra python dalla riga di comando.
Step 2 - Inizia subito a scrivere codice nell'interprete interattivo utilizzando i comandi mostrati di seguito -
$python # Unix/Linux
or
python% # Unix/Linux
or
C:> python # Windows/DOS
Metodo 2: utilizzo dello script dalla riga di comando
Possiamo anche eseguire uno script Python dalla riga di comando invocando l'interprete sulla nostra applicazione. È possibile utilizzare i comandi mostrati di seguito:
$python script.py # Unix/Linux
or
python% script.py # Unix/Linux
or
C: >python script.py # Windows/DOS
Metodo 3: ambiente di sviluppo integrato
Se un sistema ha un'applicazione GUI che supporta Python, allora Python può essere eseguito da quell'ambiente GUI. Alcuni degli IDE per varie piattaforme sono riportati di seguito:
Unix IDE - UNIX ha IDLE IDE per Python.
Windows IDE - Windows ha PythonWin, la prima interfaccia Windows per Python insieme alla GUI.
Macintosh IDE - Macintosh ha IDLE IDE disponibile dal sito Web principale, scaricabile come file MacBinary o BinHex.
Ora che hai dimestichezza con l'installazione e l'esecuzione dei comandi Python sul tuo sistema locale, passiamo ai concetti di medicina legale in dettaglio. Questo capitolo spiegherà i vari concetti coinvolti nella gestione degli artefatti in Python digital forensics.
Necessità di creazione di report
Il processo di digital forensics include la segnalazione come terza fase. Questa è una delle parti più importanti del processo forense digitale. La creazione del report è necessaria per i seguenti motivi:
È il documento in cui l'esaminatore forense digitale delinea il processo di indagine e le sue conclusioni.
Un buon rapporto forense digitale può essere referenziato da un altro esaminatore per ottenere lo stesso risultato da dati stessi repository.
È un documento tecnico e scientifico che contiene fatti trovati all'interno degli 1 e degli 0 delle prove digitali.
Linee guida generali per la creazione di report
I rapporti sono scritti per fornire informazioni al lettore e devono iniziare con una solida base. gli investigatori possono incontrare difficoltà nel presentare in modo efficiente i loro risultati se il rapporto è preparato senza alcune linee guida o standard generali. Di seguito vengono fornite alcune linee guida generali che devono essere seguite durante la creazione di rapporti forensi digitali:
Summary - Il report deve contenere il breve riassunto delle informazioni in modo che il lettore possa accertare lo scopo del report.
Tools used - Dobbiamo menzionare gli strumenti che sono stati utilizzati per portare avanti il processo di digital forensics, compreso il loro scopo.
Repository - Supponiamo di aver esaminato il computer di qualcuno, quindi il riepilogo delle prove e l'analisi di materiale pertinente come e-mail, cronologia delle ricerche interne ecc., Quindi devono essere inclusi nel rapporto in modo che il caso possa essere presentato chiaramente.
Recommendations for counsel - Il rapporto deve contenere le raccomandazioni per l'avvocato di continuare o interrompere le indagini sulla base dei risultati del rapporto.
Creazione di diversi tipi di rapporti
Nella sezione precedente, siamo venuti a conoscenza dell'importanza del report nella digital forensics insieme alle linee guida per la creazione dello stesso. Alcuni dei formati in Python per la creazione di diversi tipi di report sono discussi di seguito:
Rapporti CSV
Uno dei formati di output più comuni dei rapporti è un foglio di calcolo CSV. È possibile creare un CSV per creare un report dei dati elaborati utilizzando il codice Python come mostrato di seguito -
Innanzitutto, importa le librerie utili per scrivere il foglio di calcolo -
from __future__ import print_function
import csv
import os
import sys
Ora, chiama il seguente metodo:
Write_csv(TEST_DATA_LIST, ["Name", "Age", "City", "Job description"], os.getcwd())
Stiamo utilizzando la seguente variabile globale per rappresentare i tipi di dati di esempio:
TEST_DATA_LIST = [["Ram", 32, Bhopal, Manager],
["Raman", 42, Indore, Engg.],
["Mohan", 25, Chandigarh, HR],
["Parkash", 45, Delhi, IT]]
Successivamente, definiamo il metodo per procedere per ulteriori operazioni. Apriamo il file in modalità "w" e impostiamo l'argomento della parola chiave newline su una stringa vuota.
def Write_csv(data, header, output_directory, name = None):
if name is None:
name = "report1.csv"
print("[+] Writing {} to {}".format(name, output_directory))
with open(os.path.join(output_directory, name), "w", newline = "") as \ csvfile:
writer = csv.writer(csvfile)
writer.writerow(header)
writer.writerow(data)
Se esegui lo script precedente, otterrai i seguenti dettagli memorizzati nel file report1.csv.
Nome | Età | Città | Designazione |
---|---|---|---|
Ariete | 32 | Bhopal | Managerh |
Raman | 42 | Indore | Engg |
Mohan | 25 | Chandigarh | HR |
Parkash | 45 | Delhi | IT |
Rapporti Excel
Un altro formato di output comune dei report è il foglio di calcolo Excel (.xlsx). Possiamo creare una tabella e anche tracciare il grafico utilizzando Excel. Possiamo creare report dei dati elaborati in formato Excel utilizzando il codice Python come mostrato di seguito
Innanzitutto, importa il modulo XlsxWriter per la creazione di un foglio di calcolo -
import xlsxwriter
Ora crea un oggetto cartella di lavoro. Per questo, dobbiamo usare il costruttore Workbook ().
workbook = xlsxwriter.Workbook('report2.xlsx')
Ora, crea un nuovo foglio di lavoro utilizzando il modulo add_worksheet ().
worksheet = workbook.add_worksheet()
Quindi, scrivi i seguenti dati nel foglio di lavoro:
report2 = (['Ram', 32, ‘Bhopal’],['Mohan',25, ‘Chandigarh’] ,['Parkash',45, ‘Delhi’])
row = 0
col = 0
Puoi iterare su questi dati e scriverli come segue:
for item, cost in (a):
worksheet.write(row, col, item)
worksheet.write(row, col+1, cost)
row + = 1
Ora, chiudiamo questo file Excel utilizzando il metodo close ().
workbook.close()
Lo script sopra creerà un file Excel denominato report2.xlsx con i seguenti dati:
Ariete | 32 | Bhopal |
Mohan | 25 | Chandigarh |
Parkash | 45 | Delhi |
Investigation Acquisition Media
È importante che un investigatore abbia le note investigative dettagliate per ricordare accuratamente i risultati o mettere insieme tutti i pezzi dell'indagine. Uno screenshot è molto utile per tenere traccia dei passaggi effettuati per una particolare indagine. Con l'aiuto del seguente codice Python, possiamo fare lo screenshot e salvarlo sul disco rigido per un uso futuro.
Innanzitutto, installa il modulo Python denominato pyscreenshot utilizzando il seguente comando:
Pip install pyscreenshot
Ora, importa i moduli necessari come mostrato -
import pyscreenshot as ImageGrab
Usa la seguente riga di codice per ottenere lo screenshot:
image = ImageGrab.grab()
Usa la seguente riga di codice per salvare lo screenshot nella posizione specificata -
image.save('d:/image123.png')
Ora, se vuoi far apparire lo screenshot come un grafico, puoi usare il seguente codice Python:
import numpy as np
import matplotlib.pyplot as plt
import pyscreenshot as ImageGrab
imageg = ImageGrab.grab()
plt.imshow(image, cmap='gray', interpolation='bilinear')
plt.show()
Questo capitolo spiegherà le analisi forensi digitali di Python su dispositivi mobili e i concetti coinvolti.
introduzione
La mobile device forensics è quella branca della digital forensics che si occupa dell'acquisizione e dell'analisi di dispositivi mobili per il recupero di evidenze digitali di interesse investigativo. Questo ramo è diverso dall'analisi forense dei computer perché i dispositivi mobili hanno un sistema di comunicazione integrato che è utile per fornire informazioni utili relative alla posizione.
Sebbene l'uso degli smartphone stia aumentando giorno per giorno nell'analisi forense digitale, è ancora considerato non standard a causa della sua eterogeneità. D'altra parte, l'hardware del computer, come il disco rigido, è considerato standard e sviluppato anche come disciplina stabile. Nell'industria forense digitale, c'è molto dibattito sulle tecniche utilizzate per dispositivi non standard, che hanno prove transitorie, come gli smartphone.
Artefatti estraibili da dispositivi mobili
I dispositivi mobili moderni possiedono molte informazioni digitali rispetto ai telefoni più vecchi che hanno solo un registro delle chiamate o messaggi SMS. Pertanto, i dispositivi mobili possono fornire agli investigatori molte informazioni sul suo utente. Alcuni artefatti che possono essere estratti dai dispositivi mobili sono i seguenti:
Messages - Questi sono gli artefatti utili che possono rivelare lo stato d'animo del proprietario e possono persino fornire alcune informazioni precedenti sconosciute all'investigatore.
Location History- I dati della cronologia delle posizioni sono un utile artefatto che può essere utilizzato dagli investigatori per convalidare la posizione particolare di una persona.
Applications Installed - Accedendo al tipo di applicazioni installate, l'investigatore ottiene alcune informazioni sulle abitudini e sul pensiero dell'utente mobile.
Fonti ed elaborazione delle prove in Python
Gli smartphone hanno database SQLite e file PLIST come le principali fonti di prove. In questa sezione elaboreremo i sorgenti delle evidenze in python.
Analisi dei file PLIST
Un PLIST (Property List) è un formato flessibile e conveniente per la memorizzazione dei dati delle applicazioni soprattutto sui dispositivi iPhone. Usa l'estensione.plist. Questo tipo di file viene utilizzato per archiviare informazioni su bundle e applicazioni. Può essere in due formati:XML e binary. Il seguente codice Python si aprirà e leggerà il file PLIST. Nota che prima di procedere in questo, dobbiamo creare il nostroInfo.plist file.
Innanzitutto, installa una libreria di terze parti denominata biplist dal seguente comando -
Pip install biplist
Ora importa alcune librerie utili per elaborare i file plist -
import biplist
import os
import sys
Ora, utilizzare il seguente comando sotto il metodo principale può essere utilizzato per leggere il file plist in una variabile:
def main(plist):
try:
data = biplist.readPlist(plist)
except (biplist.InvalidPlistException,biplist.NotBinaryPlistException) as e:
print("[-] Invalid PLIST file - unable to be opened by biplist")
sys.exit(1)
Ora possiamo leggere i dati sulla console o stamparli direttamente da questa variabile.
Database SQLite
SQLite funge da repository di dati principale sui dispositivi mobili. SQLite una libreria in-process che implementa un motore di database SQL transazionale autonomo, senza server, a configurazione zero. È un database, che è a configurazione zero, non è necessario configurarlo nel sistema, a differenza di altri database.
Se sei un principiante o non hai familiarità con i database SQLite, puoi seguire il link www.tutorialspoint.com/sqlite/index.htm Inoltre, puoi seguire il link www.tutorialspoint.com/sqlite/sqlite_python.htm nel caso lo desideri entrare nei dettagli di SQLite con Python.
Durante l'analisi forense mobile, possiamo interagire con sms.db file di un dispositivo mobile e può estrarre informazioni preziose da messagetavolo. Python ha una libreria incorporata denominatasqlite3per la connessione con il database SQLite. Puoi importare lo stesso con il seguente comando:
import sqlite3
Ora, con l'aiuto del seguente comando, possiamo connetterci al database, diciamo sms.db in caso di dispositivi mobili -
Conn = sqlite3.connect(‘sms.db’)
C = conn.cursor()
Qui, C è l'oggetto cursore con l'aiuto del quale possiamo interagire con il database.
Supponiamo ora di voler eseguire un particolare comando, diciamo di ottenere i dettagli dal file abc table, può essere fatto con l'aiuto del seguente comando:
c.execute(“Select * from abc”)
c.close()
Il risultato del comando precedente verrà memorizzato nel file cursoroggetto. Allo stesso modo possiamo usarefetchall() metodo per scaricare il risultato in una variabile che possiamo manipolare.
Possiamo usare il seguente comando per ottenere i dati dei nomi delle colonne della tabella dei messaggi in sms.db -
c.execute(“pragma table_info(message)”)
table_data = c.fetchall()
columns = [x[1] for x in table_data
Si noti che qui stiamo usando il comando SQLite PRAGMA che è un comando speciale da utilizzare per controllare varie variabili ambientali e flag di stato all'interno dell'ambiente SQLite. Nel comando precedente, il filefetchall()restituisce una tupla di risultati. Il nome di ogni colonna viene archiviato nel primo indice di ogni tupla.
Ora, con l'aiuto del seguente comando, possiamo interrogare la tabella per tutti i suoi dati e memorizzarli nella variabile denominata data_msg -
c.execute(“Select * from message”)
data_msg = c.fetchall()
Il comando precedente memorizzerà i dati nella variabile e inoltre possiamo anche scrivere i dati sopra nel file CSV utilizzando csv.writer() metodo.
Backup di iTunes
L'iPhone mobile forensics può essere eseguito sui backup effettuati da iTunes. Gli esaminatori forensi si affidano all'analisi dei backup logici dell'iPhone acquisiti tramite iTunes. Il protocollo AFC (connessione file Apple) viene utilizzato da iTunes per eseguire il backup. Inoltre, il processo di backup non modifica nulla sull'iPhone tranne i record della chiave di deposito.
Ora, sorge la domanda: perché è importante per un esperto di medicina legale digitale comprendere le tecniche sui backup di iTunes? È importante nel caso in cui otteniamo l'accesso diretto al computer del sospetto anziché all'iPhone perché quando un computer viene utilizzato per la sincronizzazione con iPhone, è probabile che la maggior parte delle informazioni su iPhone venga salvata sul computer.
Processo di backup e sua posizione
Ogni volta che viene eseguito il backup di un prodotto Apple sul computer, è sincronizzato con iTunes e ci sarà una cartella specifica con l'ID univoco del dispositivo. Nel formato di backup più recente, i file vengono archiviati in sottocartelle contenenti i primi due caratteri esadecimali del nome file. Da questi file di backup, ci sono alcuni file come info.plist che sono utili insieme al database denominato Manifest.db. La tabella seguente mostra le posizioni dei backup, che variano in base ai sistemi operativi dei backup di iTunes:
OS | Posizione di backup |
---|---|
Win7 | C: \ Users \ [nome utente] \ AppData \ Roaming \ AppleComputer \ MobileSync \ Backup \ |
MAC OS X | ~ / Library / Application Suport / MobileSync / Backup / |
Per elaborare il backup di iTunes con Python, dobbiamo prima identificare tutti i backup nella posizione di backup secondo il nostro sistema operativo. Quindi itereremo attraverso ogni backup e leggeremo il database Manifest.db.
Ora, con l'aiuto del codice Python possiamo fare lo stesso -
Innanzitutto, importa le librerie necessarie come segue:
from __future__ import print_function
import argparse
import logging
import os
from shutil import copyfile
import sqlite3
import sys
logger = logging.getLogger(__name__)
Ora, fornisci due argomenti posizionali, ovvero INPUT_DIR e OUTPUT_DIR che rappresenta il backup di iTunes e la cartella di output desiderata -
if __name__ == "__main__":
parser.add_argument("INPUT_DIR",help = "Location of folder containing iOS backups, ""e.g. ~\Library\Application Support\MobileSync\Backup folder")
parser.add_argument("OUTPUT_DIR", help = "Output Directory")
parser.add_argument("-l", help = "Log file path",default = __file__[:-2] + "log")
parser.add_argument("-v", help = "Increase verbosity",action = "store_true") args = parser.parse_args()
Ora, imposta il registro come segue:
if args.v:
logger.setLevel(logging.DEBUG)
else:
logger.setLevel(logging.INFO)
Ora, imposta il formato del messaggio per questo registro come segue:
msg_fmt = logging.Formatter("%(asctime)-15s %(funcName)-13s""%(levelname)-8s %(message)s")
strhndl = logging.StreamHandler(sys.stderr)
strhndl.setFormatter(fmt = msg_fmt)
fhndl = logging.FileHandler(args.l, mode = 'a')
fhndl.setFormatter(fmt = msg_fmt)
logger.addHandler(strhndl)
logger.addHandler(fhndl)
logger.info("Starting iBackup Visualizer")
logger.debug("Supplied arguments: {}".format(" ".join(sys.argv[1:])))
logger.debug("System: " + sys.platform)
logger.debug("Python Version: " + sys.version)
La seguente riga di codice creerà le cartelle necessarie per la directory di output desiderata utilizzando os.makedirs() funzione -
if not os.path.exists(args.OUTPUT_DIR):
os.makedirs(args.OUTPUT_DIR)
Ora, passa le directory di input e output fornite alla funzione main () come segue:
if os.path.exists(args.INPUT_DIR) and os.path.isdir(args.INPUT_DIR):
main(args.INPUT_DIR, args.OUTPUT_DIR)
else:
logger.error("Supplied input directory does not exist or is not ""a directory")
sys.exit(1)
Ora Scrivi main() funzione che chiamerà ulteriormente backup_summary() funzione per identificare tutti i backup presenti nella cartella di input -
def main(in_dir, out_dir):
backups = backup_summary(in_dir)
def backup_summary(in_dir):
logger.info("Identifying all iOS backups in {}".format(in_dir))
root = os.listdir(in_dir)
backups = {}
for x in root:
temp_dir = os.path.join(in_dir, x)
if os.path.isdir(temp_dir) and len(x) == 40:
num_files = 0
size = 0
for root, subdir, files in os.walk(temp_dir):
num_files += len(files)
size += sum(os.path.getsize(os.path.join(root, name))
for name in files)
backups[x] = [temp_dir, num_files, size]
return backups
Ora, stampa il riepilogo di ogni backup sulla console come segue:
print("Backup Summary")
print("=" * 20)
if len(backups) > 0:
for i, b in enumerate(backups):
print("Backup No.: {} \n""Backup Dev. Name: {} \n""# Files: {} \n""Backup Size (Bytes): {}\n".format(i, b, backups[b][1], backups[b][2]))
Ora, scarica il contenuto del file Manifest.db nella variabile denominata db_items.
try:
db_items = process_manifest(backups[b][0])
except IOError:
logger.warn("Non-iOS 10 backup encountered or " "invalid backup. Continuing to next backup.")
continue
Ora, definiamo una funzione che prenderà il percorso della directory del backup -
def process_manifest(backup):
manifest = os.path.join(backup, "Manifest.db")
if not os.path.exists(manifest):
logger.error("Manifest DB not found in {}".format(manifest))
raise IOError
Ora, usando SQLite3 ci collegheremo al database tramite il cursore denominato c -
c = conn.cursor()
items = {}
for row in c.execute("SELECT * from Files;"):
items[row[0]] = [row[2], row[1], row[3]]
return items
create_files(in_dir, out_dir, b, db_items)
print("=" * 20)
else:
logger.warning("No valid backups found. The input directory should be
" "the parent-directory immediately above the SHA-1 hash " "iOS device backups")
sys.exit(2)
Ora, definisci il file create_files() metodo come segue -
def create_files(in_dir, out_dir, b, db_items):
msg = "Copying Files for backup {} to {}".format(b, os.path.join(out_dir, b))
logger.info(msg)
Ora, iterare attraverso ogni chiave nel file db_items dizionario -
for x, key in enumerate(db_items):
if db_items[key][0] is None or db_items[key][0] == "":
continue
else:
dirpath = os.path.join(out_dir, b,
os.path.dirname(db_items[key][0]))
filepath = os.path.join(out_dir, b, db_items[key][0])
if not os.path.exists(dirpath):
os.makedirs(dirpath)
original_dir = b + "/" + key[0:2] + "/" + key
path = os.path.join(in_dir, original_dir)
if os.path.exists(filepath):
filepath = filepath + "_{}".format(x)
Ora usa shutil.copyfile() metodo per copiare il file di backup come segue:
try:
copyfile(path, filepath)
except IOError:
logger.debug("File not found in backup: {}".format(path))
files_not_found += 1
if files_not_found > 0:
logger.warning("{} files listed in the Manifest.db not" "found in
backup".format(files_not_found))
copyfile(os.path.join(in_dir, b, "Info.plist"), os.path.join(out_dir, b,
"Info.plist"))
copyfile(os.path.join(in_dir, b, "Manifest.db"), os.path.join(out_dir, b,
"Manifest.db"))
copyfile(os.path.join(in_dir, b, "Manifest.plist"), os.path.join(out_dir, b,
"Manifest.plist"))
copyfile(os.path.join(in_dir, b, "Status.plist"),os.path.join(out_dir, b,
"Status.plist"))
Con lo script Python sopra, possiamo ottenere la struttura dei file di backup aggiornata nella nostra cartella di output. Possiamo usarepycrypto libreria python per decrittografare i backup.
Wi-Fi
I dispositivi mobili possono essere utilizzati per connettersi al mondo esterno connettendosi tramite reti Wi-Fi disponibili ovunque. A volte il dispositivo si connette automaticamente a queste reti aperte.
In caso di iPhone, l'elenco delle connessioni Wi-Fi aperte con cui il dispositivo si è connesso viene memorizzato in un file PLIST denominato com.apple.wifi.plist. Questo file conterrà l'SSID Wi-Fi, il BSSID e il tempo di connessione.
Dobbiamo estrarre i dettagli Wi-Fi dal report XML standard di Cellebrite utilizzando Python. Per questo, dobbiamo utilizzare l'API del Wireless Geographic Logging Engine (WIGLE), una piattaforma popolare che può essere utilizzata per trovare la posizione di un dispositivo utilizzando i nomi delle reti Wi-Fi.
Possiamo usare la libreria Python denominata requestsper accedere all'API da WIGLE. Può essere installato come segue:
pip install requests
API di WIGLE
Dobbiamo registrarci sul sito web di WIGLE https://wigle.net/accountper ottenere un'API gratuita da WIGLE. Lo script Python per ottenere le informazioni sul dispositivo dell'utente e la sua connessione tramite l'API di WIGEL è discusso di seguito:
Innanzitutto, importa le seguenti librerie per gestire cose diverse:
from __future__ import print_function
import argparse
import csv
import os
import sys
import xml.etree.ElementTree as ET
import requests
Fornisci ora due argomenti posizionali, vale a dire INPUT_FILE e OUTPUT_CSV che rappresenterà rispettivamente il file di input con l'indirizzo MAC Wi-Fi e il file CSV di output desiderato -
if __name__ == "__main__":
parser.add_argument("INPUT_FILE", help = "INPUT FILE with MAC Addresses")
parser.add_argument("OUTPUT_CSV", help = "Output CSV File")
parser.add_argument("-t", help = "Input type: Cellebrite XML report or TXT
file",choices = ('xml', 'txt'), default = "xml")
parser.add_argument('--api', help = "Path to API key
file",default = os.path.expanduser("~/.wigle_api"),
type = argparse.FileType('r'))
args = parser.parse_args()
Ora le seguenti righe di codice verificheranno se il file di input esiste ed è un file. In caso contrario, esce dallo script -
if not os.path.exists(args.INPUT_FILE) or \ not os.path.isfile(args.INPUT_FILE):
print("[-] {} does not exist or is not a
file".format(args.INPUT_FILE))
sys.exit(1)
directory = os.path.dirname(args.OUTPUT_CSV)
if directory != '' and not os.path.exists(directory):
os.makedirs(directory)
api_key = args.api.readline().strip().split(":")
Ora, passa l'argomento a main come segue:
main(args.INPUT_FILE, args.OUTPUT_CSV, args.t, api_key)
def main(in_file, out_csv, type, api_key):
if type == 'xml':
wifi = parse_xml(in_file)
else:
wifi = parse_txt(in_file)
query_wigle(wifi, out_csv, api_key)
Ora, analizzeremo il file XML come segue:
def parse_xml(xml_file):
wifi = {}
xmlns = "{http://pa.cellebrite.com/report/2.0}"
print("[+] Opening {} report".format(xml_file))
xml_tree = ET.parse(xml_file)
print("[+] Parsing report for all connected WiFi addresses")
root = xml_tree.getroot()
Ora, itera attraverso l'elemento figlio della radice come segue:
for child in root.iter():
if child.tag == xmlns + "model":
if child.get("type") == "Location":
for field in child.findall(xmlns + "field"):
if field.get("name") == "TimeStamp":
ts_value = field.find(xmlns + "value")
try:
ts = ts_value.text
except AttributeError:
continue
Ora, controlleremo che la stringa 'ssid' sia presente o meno nel testo del valore -
if "SSID" in value.text:
bssid, ssid = value.text.split("\t")
bssid = bssid[7:]
ssid = ssid[6:]
Ora, dobbiamo aggiungere BSSID, SSID e timestamp al dizionario wifi come segue:
if bssid in wifi.keys():
wifi[bssid]["Timestamps"].append(ts)
wifi[bssid]["SSID"].append(ssid)
else:
wifi[bssid] = {"Timestamps": [ts], "SSID":
[ssid],"Wigle": {}}
return wifi
Il parser di testo che è molto più semplice del parser XML è mostrato di seguito -
def parse_txt(txt_file):
wifi = {}
print("[+] Extracting MAC addresses from {}".format(txt_file))
with open(txt_file) as mac_file:
for line in mac_file:
wifi[line.strip()] = {"Timestamps": ["N/A"], "SSID":
["N/A"],"Wigle": {}}
return wifi
Ora, usiamo il modulo delle richieste per fare WIGLE APIchiamate e devi passare al query_wigle() metodo -
def query_wigle(wifi_dictionary, out_csv, api_key):
print("[+] Querying Wigle.net through Python API for {} "
"APs".format(len(wifi_dictionary)))
for mac in wifi_dictionary:
wigle_results = query_mac_addr(mac, api_key)
def query_mac_addr(mac_addr, api_key):
query_url = "https://api.wigle.net/api/v2/network/search?" \
"onlymine = false&freenet = false&paynet = false" \ "&netid = {}".format(mac_addr)
req = requests.get(query_url, auth = (api_key[0], api_key[1]))
return req.json()
In realtà esiste un limite giornaliero per le chiamate API WIGLE, se tale limite supera, deve mostrare un errore come segue:
try:
if wigle_results["resultCount"] == 0:
wifi_dictionary[mac]["Wigle"]["results"] = []
continue
else:
wifi_dictionary[mac]["Wigle"] = wigle_results
except KeyError:
if wigle_results["error"] == "too many queries today":
print("[-] Wigle daily query limit exceeded")
wifi_dictionary[mac]["Wigle"]["results"] = []
continue
else:
print("[-] Other error encountered for " "address {}: {}".format(mac,
wigle_results['error']))
wifi_dictionary[mac]["Wigle"]["results"] = []
continue
prep_output(out_csv, wifi_dictionary)
Ora useremo prep_output() metodo per appiattire il dizionario in blocchi facilmente scrivibili -
def prep_output(output, data):
csv_data = {}
google_map = https://www.google.com/maps/search/
Ora accedi a tutti i dati che abbiamo raccolto finora come segue:
for x, mac in enumerate(data):
for y, ts in enumerate(data[mac]["Timestamps"]):
for z, result in enumerate(data[mac]["Wigle"]["results"]):
shortres = data[mac]["Wigle"]["results"][z]
g_map_url = "{}{},{}".format(google_map, shortres["trilat"],shortres["trilong"])
Ora, possiamo scrivere l'output nel file CSV come abbiamo fatto negli script precedenti in questo capitolo usando write_csv() funzione.
In questo capitolo apprenderemo in dettaglio come investigare i metadati incorporati usando Python digital forensics.
introduzione
I metadati incorporati sono le informazioni sui dati archiviati nello stesso file che contiene l'oggetto descritto da quei dati. In altre parole, sono le informazioni su una risorsa digitale memorizzata nel file digitale stesso. È sempre associato al file e non può mai essere separato.
In caso di analisi forense digitale, non possiamo estrarre tutte le informazioni su un particolare file. Dall'altro lato, i metadati incorporati possono fornirci informazioni fondamentali per l'indagine. Ad esempio, i metadati di un file di testo possono contenere informazioni sull'autore, la sua lunghezza, la data scritta e persino un breve riassunto su quel documento. Un'immagine digitale può includere metadati come la lunghezza dell'immagine, la velocità dell'otturatore, ecc.
Artefatti contenenti attributi di metadati e loro estrazione
In questa sezione, apprenderemo vari artefatti contenenti attributi di metadati e il loro processo di estrazione utilizzando Python.
Audio e video
Questi sono i due artefatti molto comuni che hanno i metadati incorporati. Questi metadati possono essere estratti a scopo di indagine.
È possibile utilizzare il seguente script Python per estrarre attributi o metadati comuni da file audio o MP3 e un video o un file MP4.
Nota che per questo script, abbiamo bisogno di installare una libreria Python di terze parti chiamata mutagen che ci consente di estrarre metadati da file audio e video. Può essere installato con l'aiuto del seguente comando:
pip install mutagen
Alcune delle librerie utili che dobbiamo importare per questo script Python sono le seguenti:
from __future__ import print_function
import argparse
import json
import mutagen
Il gestore della riga di comando prenderà un argomento che rappresenta il percorso dei file MP3 o MP4. Quindi, useremomutagen.file() metodo per aprire un handle al file come segue:
if __name__ == '__main__':
parser = argparse.ArgumentParser('Python Metadata Extractor')
parser.add_argument("AV_FILE", help="File to extract metadata from")
args = parser.parse_args()
av_file = mutagen.File(args.AV_FILE)
file_ext = args.AV_FILE.rsplit('.', 1)[-1]
if file_ext.lower() == 'mp3':
handle_id3(av_file)
elif file_ext.lower() == 'mp4':
handle_mp4(av_file)
Ora, dobbiamo usare due maniglie, una per estrarre i dati da MP3 e una per estrarre i dati dal file MP4. Possiamo definire queste maniglie come segue:
def handle_id3(id3_file):
id3_frames = {'TIT2': 'Title', 'TPE1': 'Artist', 'TALB': 'Album','TXXX':
'Custom', 'TCON': 'Content Type', 'TDRL': 'Date released','COMM': 'Comments',
'TDRC': 'Recording Date'}
print("{:15} | {:15} | {:38} | {}".format("Frame", "Description","Text","Value"))
print("-" * 85)
for frames in id3_file.tags.values():
frame_name = id3_frames.get(frames.FrameID, frames.FrameID)
desc = getattr(frames, 'desc', "N/A")
text = getattr(frames, 'text', ["N/A"])[0]
value = getattr(frames, 'value', "N/A")
if "date" in frame_name.lower():
text = str(text)
print("{:15} | {:15} | {:38} | {}".format(
frame_name, desc, text, value))
def handle_mp4(mp4_file):
cp_sym = u"\u00A9"
qt_tag = {
cp_sym + 'nam': 'Title', cp_sym + 'art': 'Artist',
cp_sym + 'alb': 'Album', cp_sym + 'gen': 'Genre',
'cpil': 'Compilation', cp_sym + 'day': 'Creation Date',
'cnID': 'Apple Store Content ID', 'atID': 'Album Title ID',
'plID': 'Playlist ID', 'geID': 'Genre ID', 'pcst': 'Podcast',
'purl': 'Podcast URL', 'egid': 'Episode Global ID',
'cmID': 'Camera ID', 'sfID': 'Apple Store Country',
'desc': 'Description', 'ldes': 'Long Description'}
genre_ids = json.load(open('apple_genres.json'))
Ora, dobbiamo scorrere questo file MP4 come segue:
print("{:22} | {}".format('Name', 'Value'))
print("-" * 40)
for name, value in mp4_file.tags.items():
tag_name = qt_tag.get(name, name)
if isinstance(value, list):
value = "; ".join([str(x) for x in value])
if name == 'geID':
value = "{}: {}".format(
value, genre_ids[str(value)].replace("|", " - "))
print("{:22} | {}".format(tag_name, value))
Lo script sopra ci fornirà ulteriori informazioni sui file MP3 e MP4.
immagini
Le immagini possono contenere diversi tipi di metadati a seconda del formato del file. Tuttavia, la maggior parte delle immagini incorporano informazioni GPS. Possiamo estrarre queste informazioni GPS utilizzando librerie Python di terze parti. È possibile utilizzare il seguente script Python può essere utilizzato per fare lo stesso:
Innanzitutto, scarica la libreria python di terze parti denominata Python Imaging Library (PIL) come segue -
pip install pillow
Questo ci aiuterà a estrarre i metadati dalle immagini.
Possiamo anche scrivere i dettagli GPS incorporati nelle immagini in un file KML, ma per questo dobbiamo scaricare la libreria Python di terze parti denominata simplekml come segue -
pip install simplekml
In questo script, prima dobbiamo importare le seguenti librerie:
from __future__ import print_function
import argparse
from PIL import Image
from PIL.ExifTags import TAGS
import simplekml
import sys
Ora, il gestore della riga di comando accetterà un argomento posizionale che sostanzialmente rappresenta il percorso del file delle foto.
parser = argparse.ArgumentParser('Metadata from images')
parser.add_argument('PICTURE_FILE', help = "Path to picture")
args = parser.parse_args()
Ora, dobbiamo specificare gli URL che popoleranno le informazioni sulle coordinate. Gli URL sonogmaps e open_maps. Abbiamo anche bisogno di una funzione per convertire la coordinata della tupla gradi minuto secondo (DMS), fornita dalla libreria PIL, in decimale. Può essere fatto come segue:
gmaps = "https://www.google.com/maps?q={},{}"
open_maps = "http://www.openstreetmap.org/?mlat={}&mlon={}"
def process_coords(coord):
coord_deg = 0
for count, values in enumerate(coord):
coord_deg += (float(values[0]) / values[1]) / 60**count
return coord_deg
Ora useremo image.open() funzione per aprire il file come oggetto PIL.
img_file = Image.open(args.PICTURE_FILE)
exif_data = img_file._getexif()
if exif_data is None:
print("No EXIF data found")
sys.exit()
for name, value in exif_data.items():
gps_tag = TAGS.get(name, name)
if gps_tag is not 'GPSInfo':
continue
Dopo aver trovato il file GPSInfo , memorizzeremo il riferimento GPS ed elaboreremo le coordinate con il process_coords() metodo.
lat_ref = value[1] == u'N'
lat = process_coords(value[2])
if not lat_ref:
lat = lat * -1
lon_ref = value[3] == u'E'
lon = process_coords(value[4])
if not lon_ref:
lon = lon * -1
Ora inizia kml oggetto da simplekml libreria come segue -
kml = simplekml.Kml()
kml.newpoint(name = args.PICTURE_FILE, coords = [(lon, lat)])
kml.save(args.PICTURE_FILE + ".kml")
Ora possiamo stampare le coordinate dalle informazioni elaborate come segue:
print("GPS Coordinates: {}, {}".format(lat, lon))
print("Google Maps URL: {}".format(gmaps.format(lat, lon)))
print("OpenStreetMap URL: {}".format(open_maps.format(lat, lon)))
print("KML File {} created".format(args.PICTURE_FILE + ".kml"))
Documenti PDF
I documenti PDF hanno un'ampia varietà di supporti, tra cui immagini, testo, moduli, ecc. Quando estraiamo metadati incorporati nei documenti PDF, possiamo ottenere i dati risultanti nel formato chiamato Extensible Metadata Platform (XMP). Possiamo estrarre i metadati con l'aiuto del seguente codice Python:
Innanzitutto, installa una libreria Python di terze parti denominata PyPDF2per leggere i metadati archiviati in formato XMP. Può essere installato come segue:
pip install PyPDF2
Ora importa le seguenti librerie per estrarre i metadati dai file PDF:
from __future__ import print_function
from argparse import ArgumentParser, FileType
import datetime
from PyPDF2 import PdfFileReader
import sys
Ora, il gestore della riga di comando accetterà un argomento posizionale che sostanzialmente rappresenta il percorso del file PDF.
parser = argparse.ArgumentParser('Metadata from PDF')
parser.add_argument('PDF_FILE', help='Path to PDF file',type=FileType('rb'))
args = parser.parse_args()
Ora possiamo usare getXmpMetadata() metodo per fornire un oggetto contenente i metadati disponibili come segue:
pdf_file = PdfFileReader(args.PDF_FILE)
xmpm = pdf_file.getXmpMetadata()
if xmpm is None:
print("No XMP metadata found in document.")
sys.exit()
Possiamo usare custom_print() metodo per estrarre e stampare i valori rilevanti come titolo, creatore, collaboratore ecc. come segue:
custom_print("Title: {}", xmpm.dc_title)
custom_print("Creator(s): {}", xmpm.dc_creator)
custom_print("Contributors: {}", xmpm.dc_contributor)
custom_print("Subject: {}", xmpm.dc_subject)
custom_print("Description: {}", xmpm.dc_description)
custom_print("Created: {}", xmpm.xmp_createDate)
custom_print("Modified: {}", xmpm.xmp_modifyDate)
custom_print("Event Dates: {}", xmpm.dc_date)
Possiamo anche definire custom_print() metodo nel caso in cui il PDF venga creato utilizzando più software come segue:
def custom_print(fmt_str, value):
if isinstance(value, list):
print(fmt_str.format(", ".join(value)))
elif isinstance(value, dict):
fmt_value = [":".join((k, v)) for k, v in value.items()]
print(fmt_str.format(", ".join(value)))
elif isinstance(value, str) or isinstance(value, bool):
print(fmt_str.format(value))
elif isinstance(value, bytes):
print(fmt_str.format(value.decode()))
elif isinstance(value, datetime.datetime):
print(fmt_str.format(value.isoformat()))
elif value is None:
print(fmt_str.format("N/A"))
else:
print("warn: unhandled type {} found".format(type(value)))
Possiamo anche estrarre qualsiasi altra proprietà personalizzata salvata dal software come segue:
if xmpm.custom_properties:
print("Custom Properties:")
for k, v in xmpm.custom_properties.items():
print("\t{}: {}".format(k, v))
Lo script sopra leggerà il documento PDF e stamperà i metadati memorizzati in formato XMP comprese alcune proprietà personalizzate memorizzate dal software con l'aiuto di cui è stato creato quel PDF.
File eseguibili di Windows
A volte potremmo incontrare un file eseguibile sospetto o non autorizzato. Ma ai fini dell'indagine può essere utile a causa dei metadati incorporati. Possiamo ottenere le informazioni come la sua posizione, il suo scopo e altri attributi come il produttore, la data di compilazione, ecc. Con l'aiuto del seguente script Python possiamo ottenere la data di compilazione, i dati utili dalle intestazioni e i simboli importati ed esportati.
A tale scopo, installa prima la libreria Python di terze parti pefile. Può essere fatto come segue:
pip install pefile
Una volta installato correttamente, importa le seguenti librerie come segue:
from __future__ import print_function
import argparse
from datetime import datetime
from pefile import PE
Ora, il gestore della riga di comando accetterà un argomento posizionale che sostanzialmente rappresenta il percorso del file eseguibile. Puoi anche scegliere lo stile di output, se ti serve in modo dettagliato e dettagliato o in modo semplificato. Per questo è necessario fornire un argomento opzionale come mostrato di seguito:
parser = argparse.ArgumentParser('Metadata from executable file')
parser.add_argument("EXE_FILE", help = "Path to exe file")
parser.add_argument("-v", "--verbose", help = "Increase verbosity of output",
action = 'store_true', default = False)
args = parser.parse_args()
Ora, caricheremo il file eseguibile di input utilizzando la classe PE. Useremo anche il dump dei dati eseguibili in un oggetto dizionariodump_dict() metodo.
pe = PE(args.EXE_FILE)
ped = pe.dump_dict()
Possiamo estrarre i metadati dei file di base come la paternità incorporata, la versione e il tempo di compilazione utilizzando il codice mostrato di seguito -
file_info = {}
for structure in pe.FileInfo:
if structure.Key == b'StringFileInfo':
for s_table in structure.StringTable:
for key, value in s_table.entries.items():
if value is None or len(value) == 0:
value = "Unknown"
file_info[key] = value
print("File Information: ")
print("==================")
for k, v in file_info.items():
if isinstance(k, bytes):
k = k.decode()
if isinstance(v, bytes):
v = v.decode()
print("{}: {}".format(k, v))
comp_time = ped['FILE_HEADER']['TimeDateStamp']['Value']
comp_time = comp_time.split("[")[-1].strip("]")
time_stamp, timezone = comp_time.rsplit(" ", 1)
comp_time = datetime.strptime(time_stamp, "%a %b %d %H:%M:%S %Y")
print("Compiled on {} {}".format(comp_time, timezone.strip()))
Possiamo estrarre i dati utili dalle intestazioni come segue:
for section in ped['PE Sections']:
print("Section '{}' at {}: {}/{} {}".format(
section['Name']['Value'], hex(section['VirtualAddress']['Value']),
section['Misc_VirtualSize']['Value'],
section['SizeOfRawData']['Value'], section['MD5'])
)
Ora, estrai l'elenco delle importazioni e delle esportazioni dai file eseguibili come mostrato di seguito:
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
print("\nImports: ")
print("=========")
for dir_entry in pe.DIRECTORY_ENTRY_IMPORT:
dll = dir_entry.dll
if not args.verbose:
print(dll.decode(), end=", ")
continue
name_list = []
for impts in dir_entry.imports:
if getattr(impts, "name", b"Unknown") is None:
name = b"Unknown"
else:
name = getattr(impts, "name", b"Unknown")
name_list.append([name.decode(), hex(impts.address)])
name_fmt = ["{} ({})".format(x[0], x[1]) for x in name_list]
print('- {}: {}'.format(dll.decode(), ", ".join(name_fmt)))
if not args.verbose:
print()
Ora stampa exports, names e addresses utilizzando il codice come mostrato di seguito -
if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):
print("\nExports: ")
print("=========")
for sym in pe.DIRECTORY_ENTRY_EXPORT.symbols:
print('- {}: {}'.format(sym.name.decode(), hex(sym.address)))
Lo script precedente estrarrà i metadati di base, le informazioni dalle intestazioni dai file eseguibili di Windows.
Metadati del documento di Office
La maggior parte del lavoro sul computer viene svolto in tre applicazioni di MS Office: Word, PowerPoint ed Excel. Questi file possiedono enormi metadati, che possono esporre informazioni interessanti sulla loro paternità e storia.
Si noti che i metadati dal formato 2007 di word (.docx), excel (.xlsx) e powerpoint (.pptx) sono archiviati in un file XML. Possiamo elaborare questi file XML in Python con l'aiuto del seguente script Python mostrato di seguito:
Innanzitutto, importa le librerie richieste come mostrato di seguito:
from __future__ import print_function
from argparse import ArgumentParser
from datetime import datetime as dt
from xml.etree import ElementTree as etree
import zipfile
parser = argparse.ArgumentParser('Office Document Metadata’)
parser.add_argument("Office_File", help="Path to office file to read")
args = parser.parse_args()
Ora controlla se il file è un file ZIP. Altrimenti, solleva un errore. Ora apri il file ed estrai gli elementi chiave per l'elaborazione utilizzando il seguente codice:
zipfile.is_zipfile(args.Office_File)
zfile = zipfile.ZipFile(args.Office_File)
core_xml = etree.fromstring(zfile.read('docProps/core.xml'))
app_xml = etree.fromstring(zfile.read('docProps/app.xml'))
Ora, crea un dizionario per avviare l'estrazione dei metadati -
core_mapping = {
'title': 'Title',
'subject': 'Subject',
'creator': 'Author(s)',
'keywords': 'Keywords',
'description': 'Description',
'lastModifiedBy': 'Last Modified By',
'modified': 'Modified Date',
'created': 'Created Date',
'category': 'Category',
'contentStatus': 'Status',
'revision': 'Revision'
}
Uso iterchildren() metodo per accedere a ciascuno dei tag all'interno del file XML -
for element in core_xml.getchildren():
for key, title in core_mapping.items():
if key in element.tag:
if 'date' in title.lower():
text = dt.strptime(element.text, "%Y-%m-%dT%H:%M:%SZ")
else:
text = element.text
print("{}: {}".format(title, text))
Allo stesso modo, fallo per il file app.xml che contiene informazioni statistiche sui contenuti del documento -
app_mapping = {
'TotalTime': 'Edit Time (minutes)',
'Pages': 'Page Count',
'Words': 'Word Count',
'Characters': 'Character Count',
'Lines': 'Line Count',
'Paragraphs': 'Paragraph Count',
'Company': 'Company',
'HyperlinkBase': 'Hyperlink Base',
'Slides': 'Slide count',
'Notes': 'Note Count',
'HiddenSlides': 'Hidden Slide Count',
}
for element in app_xml.getchildren():
for key, title in app_mapping.items():
if key in element.tag:
if 'date' in title.lower():
text = dt.strptime(element.text, "%Y-%m-%dT%H:%M:%SZ")
else:
text = element.text
print("{}: {}".format(title, text))
Ora, dopo aver eseguito lo script precedente, possiamo ottenere i diversi dettagli sul particolare documento. Tieni presente che possiamo applicare questo script solo a documenti di Office 2007 o versioni successive.
Questo capitolo spiegherà i fondamenti coinvolti nell'esecuzione di analisi di rete utilizzando Python.
Capire la Network Forensics
Network forensics è una branca della digital forensics che si occupa del monitoraggio e dell'analisi del traffico di rete di computer, sia locale che WAN (wide area network), ai fini della raccolta di informazioni, raccolta di prove o rilevamento di intrusioni. L'analisi forense della rete svolge un ruolo fondamentale nelle indagini sui crimini digitali come il furto di proprietà intellettuale o la fuga di informazioni. Un'immagine delle comunicazioni di rete aiuta un investigatore a risolvere alcune domande cruciali come segue:
Quali siti web sono stati consultati?
Che tipo di contenuto è stato caricato sulla nostra rete?
Che tipo di contenuto è stato scaricato dalla nostra rete?
A quali server si accede?
Qualcuno sta inviando informazioni sensibili al di fuori dei firewall aziendali?
Internet Evidence Finder (IEF)
IEF è uno strumento forense digitale per trovare, analizzare e presentare prove digitali trovate su diversi media digitali come computer, smartphone, tablet, ecc. È molto popolare e utilizzato da migliaia di professionisti della medicina legale.
Uso di IEF
A causa della sua popolarità, IEF è utilizzato in larga misura dai professionisti forensi. Alcuni degli usi di IEF sono i seguenti:
Grazie alle sue potenti capacità di ricerca, viene utilizzato per cercare più file o supporti dati contemporaneamente.
Viene anche utilizzato per recuperare i dati cancellati dallo spazio non allocato della RAM attraverso nuove tecniche di intaglio.
Se gli investigatori vogliono ricostruire le pagine web nel loro formato originale alla data in cui sono state aperte, possono usare IEF.
Viene anche utilizzato per cercare volumi di dischi logici o fisici.
Scaricare i report da IEF a CSV utilizzando Python
IEF memorizza i dati in un database SQLite e il seguente script Python identificherà dinamicamente le tabelle dei risultati all'interno del database IEF e le scaricherà nei rispettivi file CSV.
Questo processo viene eseguito nei passaggi mostrati di seguito
Innanzitutto, genera il database dei risultati IEF che sarà un file di database SQLite che termina con l'estensione .db.
Quindi, interroga quel database per identificare tutte le tabelle.
Infine, scrivi queste tabelle dei risultati in un singolo file CSV.
Codice Python
Vediamo come utilizzare il codice Python per questo scopo -
Per lo script Python, importa le librerie necessarie come segue:
from __future__ import print_function
import argparse
import csv
import os
import sqlite3
import sys
Ora, dobbiamo fornire il percorso al file di database IEF -
if __name__ == '__main__':
parser = argparse.ArgumentParser('IEF to CSV')
parser.add_argument("IEF_DATABASE", help="Input IEF database")
parser.add_argument("OUTPUT_DIR", help="Output DIR")
args = parser.parse_args()
Ora, confermeremo l'esistenza del database IEF come segue:
if not os.path.exists(args.OUTPUT_DIR):
os.makedirs(args.OUTPUT_DIR)
if os.path.exists(args.IEF_DATABASE) and \ os.path.isfile(args.IEF_DATABASE):
main(args.IEF_DATABASE, args.OUTPUT_DIR)
else:
print("[-] Supplied input file {} does not exist or is not a " "file".format(args.IEF_DATABASE))
sys.exit(1)
Ora, come abbiamo fatto negli script precedenti, effettua la connessione con il database SQLite come segue per eseguire le query tramite il cursore:
def main(database, out_directory):
print("[+] Connecting to SQLite database")
conn = sqlite3.connect(database)
c = conn.cursor()
Le seguenti righe di codice recupereranno i nomi delle tabelle dal database:
print("List of all tables to extract")
c.execute("select * from sqlite_master where type = 'table'")
tables = [x[2] for x in c.fetchall() if not x[2].startswith('_') and not x[2].endswith('_DATA')]
Ora selezioneremo tutti i dati dalla tabella e utilizzando fetchall() sull'oggetto cursore memorizzeremo l'elenco delle tuple contenenti i dati della tabella nella sua interezza in una variabile -
print("Dumping {} tables to CSV files in {}".format(len(tables), out_directory))
for table in tables:
c.execute("pragma table_info('{}')".format(table))
table_columns = [x[1] for x in c.fetchall()]
c.execute("select * from '{}'".format(table))
table_data = c.fetchall()
Ora, usando CSV_Writer() metodo scriveremo il contenuto nel file CSV -
csv_name = table + '.csv'
csv_path = os.path.join(out_directory, csv_name)
print('[+] Writing {} table to {} CSV file'.format(table,csv_name))
with open(csv_path, "w", newline = "") as csvfile:
csv_writer = csv.writer(csvfile)
csv_writer.writerow(table_columns)
csv_writer.writerows(table_data)
Lo script precedente recupererà tutti i dati dalle tabelle del database IEF e scriverà il contenuto nel file CSV di nostra scelta.
Lavorare con i dati memorizzati nella cache
Dal database dei risultati di IEF, possiamo recuperare più informazioni che non sono necessariamente supportate da IEF stesso. Possiamo recuperare i dati memorizzati nella cache, un prodotto bi per informazioni, da provider di servizi di posta elettronica come Yahoo, Google, ecc. Utilizzando il database dei risultati IEF.
Di seguito è riportato lo script Python per accedere alle informazioni sui dati memorizzati nella cache da Yahoo Mail, accessibile su Google Chrome, utilizzando il database IEF. Nota che i passaggi sarebbero più o meno gli stessi seguiti nell'ultimo script Python.
Innanzitutto, importa le librerie necessarie per Python come segue:
from __future__ import print_function
import argparse
import csv
import os
import sqlite3
import sys
import json
Ora, fornisci il percorso del file di database IEF insieme a due argomenti posizionali accettati dal gestore della riga di comando come fatto nell'ultimo script -
if __name__ == '__main__':
parser = argparse.ArgumentParser('IEF to CSV')
parser.add_argument("IEF_DATABASE", help="Input IEF database")
parser.add_argument("OUTPUT_DIR", help="Output DIR")
args = parser.parse_args()
Ora, conferma l'esistenza del database IEF come segue:
directory = os.path.dirname(args.OUTPUT_CSV)
if not os.path.exists(directory):os.makedirs(directory)
if os.path.exists(args.IEF_DATABASE) and \ os.path.isfile(args.IEF_DATABASE):
main(args.IEF_DATABASE, args.OUTPUT_CSV)
else: print("Supplied input file {} does not exist or is not a " "file".format(args.IEF_DATABASE))
sys.exit(1)
Ora, effettua la connessione con il database SQLite come segue per eseguire le query tramite il cursore:
def main(database, out_csv):
print("[+] Connecting to SQLite database")
conn = sqlite3.connect(database)
c = conn.cursor()
Puoi utilizzare le seguenti righe di codice per recuperare le istanze del record della cache dei contatti di Yahoo Mail:
print("Querying IEF database for Yahoo Contact Fragments from " "the Chrome Cache Records Table")
try:
c.execute("select * from 'Chrome Cache Records' where URL like " "'https://data.mail.yahoo.com" "/classicab/v2/contacts/?format=json%'")
except sqlite3.OperationalError:
print("Received an error querying the database -- database may be" "corrupt or not have a Chrome Cache Records table")
sys.exit(2)
Ora, l'elenco delle tuple restituite dalla query precedente per essere salvato in una variabile come segue:
contact_cache = c.fetchall()
contact_data = process_contacts(contact_cache)
write_csv(contact_data, out_csv)
Nota che qui useremo due metodi vale a dire process_contacts() per impostare l'elenco dei risultati e scorrere ogni record della cache dei contatti e json.loads() per memorizzare i dati JSON estratti dalla tabella in una variabile per ulteriori manipolazioni -
def process_contacts(contact_cache):
print("[+] Processing {} cache files matching Yahoo contact cache " " data".format(len(contact_cache)))
results = []
for contact in contact_cache:
url = contact[0]
first_visit = contact[1]
last_visit = contact[2]
last_sync = contact[3]
loc = contact[8]
contact_json = json.loads(contact[7].decode())
total_contacts = contact_json["total"]
total_count = contact_json["count"]
if "contacts" not in contact_json:
continue
for c in contact_json["contacts"]:
name, anni, bday, emails, phones, links = ("", "", "", "", "", "")
if "name" in c:
name = c["name"]["givenName"] + " " + \ c["name"]["middleName"] + " " + c["name"]["familyName"]
if "anniversary" in c:
anni = c["anniversary"]["month"] + \"/" + c["anniversary"]["day"] + "/" + \c["anniversary"]["year"]
if "birthday" in c:
bday = c["birthday"]["month"] + "/" + \c["birthday"]["day"] + "/" + c["birthday"]["year"]
if "emails" in c:
emails = ', '.join([x["ep"] for x in c["emails"]])
if "phones" in c:
phones = ', '.join([x["ep"] for x in c["phones"]])
if "links" in c:
links = ', '.join([x["ep"] for x in c["links"]])
Ora per azienda, titolo e note, viene utilizzato il metodo get come mostrato di seguito:
company = c.get("company", "")
title = c.get("jobTitle", "")
notes = c.get("notes", "")
Ora, aggiungiamo l'elenco dei metadati e degli elementi di dati estratti all'elenco dei risultati come segue:
results.append([url, first_visit, last_visit, last_sync, loc, name, bday,anni, emails, phones, links, company, title, notes,total_contacts, total_count])
return results
Ora, usando CSV_Writer() metodo, scriveremo il contenuto nel file CSV -
def write_csv(data, output):
print("[+] Writing {} contacts to {}".format(len(data), output))
with open(output, "w", newline="") as csvfile:
csv_writer = csv.writer(csvfile)
csv_writer.writerow([
"URL", "First Visit (UTC)", "Last Visit (UTC)",
"Last Sync (UTC)", "Location", "Contact Name", "Bday",
"Anniversary", "Emails", "Phones", "Links", "Company", "Title",
"Notes", "Total Contacts", "Count of Contacts in Cache"])
csv_writer.writerows(data)
Con l'aiuto dello script precedente, possiamo elaborare i dati memorizzati nella cache da Yahoo mail utilizzando il database IEF.
Il capitolo precedente ha trattato alcuni dei concetti di network forensics usando Python. In questo capitolo, cerchiamo di comprendere la rete forense utilizzando Python a un livello più profondo.
Conservazione delle pagine Web con una bella zuppa
Il World Wide Web (WWW) è una risorsa di informazioni unica. Tuttavia, la sua eredità è ad alto rischio a causa della perdita di contenuti a un ritmo allarmante. Diverse istituzioni culturali e accademiche, organizzazioni non profit e imprese private hanno esplorato le problematiche coinvolte e hanno contribuito allo sviluppo di soluzioni tecniche per l'archiviazione web.
La conservazione della pagina Web o l'archiviazione sul Web è il processo di raccolta dei dati dal World Wide Web, garantendo che i dati siano conservati in un archivio e rendendoli disponibili per futuri ricercatori, storici e pubblico. Prima di procedere ulteriormente nella conservazione della pagina web, discutiamo di alcune importanti questioni relative alla conservazione della pagina web come indicato di seguito:
Change in Web Resources - Le risorse Web continuano a cambiare ogni giorno, il che rappresenta una sfida per la conservazione delle pagine Web.
Large Quantity of Resources - Un altro problema relativo alla conservazione delle pagine web è la grande quantità di risorse che deve essere preservata.
Integrity - Le pagine Web devono essere protette da modifiche, eliminazioni o eliminazioni non autorizzate per tutelarne l'integrità.
Dealing with multimedia data - Pur preservando le pagine web, dobbiamo trattare anche i dati multimediali, e questi potrebbero causare problemi mentre lo facciamo.
Providing access - Oltre a preservare, deve essere risolta anche la questione della fornitura di accesso alle risorse web e la gestione dei problemi di proprietà.
In questo capitolo, useremo la libreria Python denominata Beautiful Soup per la conservazione della pagina web.
Cos'è Beautiful Soup?
Beautiful Soup è una libreria Python per estrarre dati da file HTML e XML. Può essere utilizzato conurlibperché ha bisogno di un input (documento o URL) per creare un oggetto soup, poiché non può recuperare la pagina web stessa. Puoi saperne di più in dettaglio su www.crummy.com/software/BeautifulSoup/bs4/doc/
Nota che prima di usarlo, dobbiamo installare una libreria di terze parti utilizzando il seguente comando:
pip install bs4
Successivamente, utilizzando il gestore di pacchetti Anaconda, possiamo installare Beautiful Soup come segue:
conda install -c anaconda beautifulsoup4
Script Python per la conservazione delle pagine Web
Lo script Python per preservare le pagine web utilizzando la libreria di terze parti chiamata Beautiful Soup è discusso qui -
Innanzitutto, importa le librerie richieste come segue:
from __future__ import print_function
import argparse
from bs4 import BeautifulSoup, SoupStrainer
from datetime import datetime
import hashlib
import logging
import os
import ssl
import sys
from urllib.request import urlopen
import urllib.error
logger = logging.getLogger(__name__)
Nota che questo script prenderà due argomenti posizionali, uno è l'URL che deve essere preservato e l'altro è la directory di output desiderata come mostrato di seguito -
if __name__ == "__main__":
parser = argparse.ArgumentParser('Web Page preservation')
parser.add_argument("DOMAIN", help="Website Domain")
parser.add_argument("OUTPUT_DIR", help="Preservation Output Directory")
parser.add_argument("-l", help="Log file path",
default=__file__[:-3] + ".log")
args = parser.parse_args()
Ora, configura la registrazione per lo script specificando un file e un gestore di flusso per essere in loop e documenta il processo di acquisizione come mostrato -
logger.setLevel(logging.DEBUG)
msg_fmt = logging.Formatter("%(asctime)-15s %(funcName)-10s""%(levelname)-8s %(message)s")
strhndl = logging.StreamHandler(sys.stderr)
strhndl.setFormatter(fmt=msg_fmt)
fhndl = logging.FileHandler(args.l, mode='a')
fhndl.setFormatter(fmt=msg_fmt)
logger.addHandler(strhndl)
logger.addHandler(fhndl)
logger.info("Starting BS Preservation")
logger.debug("Supplied arguments: {}".format(sys.argv[1:]))
logger.debug("System " + sys.platform)
logger.debug("Version " + sys.version)
Ora, eseguiamo la convalida dell'input nella directory di output desiderata come segue:
if not os.path.exists(args.OUTPUT_DIR):
os.makedirs(args.OUTPUT_DIR)
main(args.DOMAIN, args.OUTPUT_DIR)
Ora definiremo il file main() funzione che estrarrà il nome di base del sito Web rimuovendo gli elementi non necessari prima del nome effettivo insieme a una convalida aggiuntiva sull'URL di input come segue:
def main(website, output_dir):
base_name = website.replace("https://", "").replace("http://", "").replace("www.", "")
link_queue = set()
if "http://" not in website and "https://" not in website:
logger.error("Exiting preservation - invalid user input: {}".format(website))
sys.exit(1)
logger.info("Accessing {} webpage".format(website))
context = ssl._create_unverified_context()
Ora, dobbiamo aprire una connessione con l'URL usando il metodo urlopen (). Usiamo il blocco try-tranne come segue:
try:
index = urlopen(website, context=context).read().decode("utf-8")
except urllib.error.HTTPError as e:
logger.error("Exiting preservation - unable to access page: {}".format(website))
sys.exit(2)
logger.debug("Successfully accessed {}".format(website))
Le successive righe di codice includono tre funzioni come spiegato di seguito:
write_output() per scrivere la prima pagina web nella directory di output
find_links() funzione per identificare i collegamenti su questa pagina web
recurse_pages() funzione per scorrere e scoprire tutti i collegamenti nella pagina web.
write_output(website, index, output_dir)
link_queue = find_links(base_name, index, link_queue)
logger.info("Found {} initial links on webpage".format(len(link_queue)))
recurse_pages(website, link_queue, context, output_dir)
logger.info("Completed preservation of {}".format(website))
Ora, definiamo write_output() metodo come segue -
def write_output(name, data, output_dir, counter=0):
name = name.replace("http://", "").replace("https://", "").rstrip("//")
directory = os.path.join(output_dir, os.path.dirname(name))
if not os.path.exists(directory) and os.path.dirname(name) != "":
os.makedirs(directory)
Abbiamo bisogno di registrare alcuni dettagli sulla pagina web e quindi registriamo l'hash dei dati utilizzando hash_data() metodo come segue -
logger.debug("Writing {} to {}".format(name, output_dir)) logger.debug("Data Hash: {}".format(hash_data(data)))
path = os.path.join(output_dir, name)
path = path + "_" + str(counter)
with open(path, "w") as outfile:
outfile.write(data)
logger.debug("Output File Hash: {}".format(hash_file(path)))
Adesso definisci hash_data() metodo con l'aiuto del quale leggiamo il UTF-8 dati codificati e quindi generare il file SHA-256 hash di esso come segue -
def hash_data(data):
sha256 = hashlib.sha256()
sha256.update(data.encode("utf-8"))
return sha256.hexdigest()
def hash_file(file):
sha256 = hashlib.sha256()
with open(file, "rb") as in_file:
sha256.update(in_file.read())
return sha256.hexdigest()
Ora, creiamo un file Beautifulsoup oggetto fuori dai dati della pagina web sotto find_links() metodo come segue -
def find_links(website, page, queue):
for link in BeautifulSoup(page, "html.parser",parse_only = SoupStrainer("a", href = True)):
if website in link.get("href"):
if not os.path.basename(link.get("href")).startswith("#"):
queue.add(link.get("href"))
return queue
Ora, dobbiamo definire recurse_pages() fornendogli gli input dell'URL del sito Web, della coda di collegamento corrente, del contesto SSL non verificato e della directory di output come segue:
def recurse_pages(website, queue, context, output_dir):
processed = []
counter = 0
while True:
counter += 1
if len(processed) == len(queue):
break
for link in queue.copy(): if link in processed:
continue
processed.append(link)
try:
page = urlopen(link, context=context).read().decode("utf-8")
except urllib.error.HTTPError as e:
msg = "Error accessing webpage: {}".format(link)
logger.error(msg)
continue
Ora, scrivi l'output di ciascuna pagina web a cui si accede in un file passando il nome del collegamento, i dati della pagina, la directory di output e il contatore come segue:
write_output(link, page, output_dir, counter)
queue = find_links(website, page, queue)
logger.info("Identified {} links throughout website".format(
len(queue)))
Ora, quando eseguiamo questo script fornendo l'URL del sito Web, la directory di output e un percorso al file di registro, otterremo i dettagli su quella pagina Web che può essere utilizzata per un utilizzo futuro.
Caccia al virus
Ti sei mai chiesto come gli analisti forensi, i ricercatori sulla sicurezza e gli intervistati sugli incidenti possano capire la differenza tra software utile e malware? La risposta sta nella domanda stessa, perché senza studiare il malware, generato rapidamente dagli hacker, è del tutto impossibile per ricercatori e specialisti capire la differenza tra software utile e malware. In questa sezione, discutiamo diVirusShare, uno strumento per eseguire questa operazione.
Capire VirusShare
VirusShare è la più grande raccolta privata di campioni di malware per fornire a ricercatori di sicurezza, soccorritori di incidenti e analisti forensi campioni di codice dannoso in tempo reale. Contiene oltre 30 milioni di campioni.
Il vantaggio di VirusShare è l'elenco degli hash di malware che è disponibile gratuitamente. Chiunque può utilizzare questi hash per creare un set di hash molto completo e utilizzarlo per identificare file potenzialmente dannosi. Ma prima di utilizzare VirusShare, ti consigliamo di visitarehttps://virusshare.com per ulteriori dettagli.
Creazione di un elenco hash delimitato da una nuova riga da VirusShare utilizzando Python
Un elenco hash di VirusShare può essere utilizzato da vari strumenti forensi come X-ways ed EnCase. Nello script discusso di seguito, automatizzeremo il download di elenchi di hash da VirusShare per creare un elenco di hash delimitato da una nuova riga.
Per questo script, abbiamo bisogno di una libreria Python di terze parti tqdm che può essere scaricato come segue -
pip install tqdm
Nota che in questo script, per prima cosa leggeremo la pagina hash di VirusShare e identificheremo dinamicamente l'elenco hash più recente. Quindi inizializzeremo la barra di avanzamento e scaricheremo l'elenco hash nell'intervallo desiderato.
Innanzitutto, importa le seguenti librerie:
from __future__ import print_function
import argparse
import os
import ssl
import sys
import tqdm
from urllib.request import urlopen
import urllib.error
Questo script prenderà un argomento posizionale, che sarebbe il percorso desiderato per il set di hash -
if __name__ == '__main__':
parser = argparse.ArgumentParser('Hash set from VirusShare')
parser.add_argument("OUTPUT_HASH", help = "Output Hashset")
parser.add_argument("--start", type = int, help = "Optional starting location")
args = parser.parse_args()
Ora, eseguiremo la convalida dell'input standard come segue:
directory = os.path.dirname(args.OUTPUT_HASH)
if not os.path.exists(directory):
os.makedirs(directory)
if args.start:
main(args.OUTPUT_HASH, start=args.start)
else:
main(args.OUTPUT_HASH)
Ora dobbiamo definire main() funzione con **kwargs come argomento perché questo creerà un dizionario a cui possiamo fare riferimento per supportare gli argomenti chiave forniti come mostrato di seguito -
def main(hashset, **kwargs):
url = "https://virusshare.com/hashes.4n6"
print("[+] Identifying hash set range from {}".format(url))
context = ssl._create_unverified_context()
Ora, dobbiamo aprire la pagina hash di VirusShare utilizzando urlib.request.urlopen()metodo. Useremo il blocco try-tranne come segue:
try:
index = urlopen(url, context = context).read().decode("utf-8")
except urllib.error.HTTPError as e:
print("[-] Error accessing webpage - exiting..")
sys.exit(1)
Ora, identifica l'ultimo elenco hash dalle pagine scaricate. Puoi farlo trovando l'ultima istanza di HTMLhreftag nell'elenco hash di VirusShare. Può essere fatto con le seguenti righe di codice:
tag = index.rfind(r'a href = "hashes/VirusShare_')
stop = int(index[tag + 27: tag + 27 + 5].lstrip("0"))
if "start" not in kwa<rgs:
start = 0
else:
start = kwargs["start"]
if start < 0 or start > stop:
print("[-] Supplied start argument must be greater than or equal ""to zero but less than the latest hash list, ""currently: {}".format(stop))
sys.exit(2)
print("[+] Creating a hashset from hash lists {} to {}".format(start, stop))
hashes_downloaded = 0
Ora useremo tqdm.trange() metodo per creare un ciclo e una barra di avanzamento come segue:
for x in tqdm.trange(start, stop + 1, unit_scale=True,desc="Progress"):
url_hash = "https://virusshare.com/hashes/VirusShare_"\"{}.md5".format(str(x).zfill(5))
try:
hashes = urlopen(url_hash, context=context).read().decode("utf-8")
hashes_list = hashes.split("\n")
except urllib.error.HTTPError as e:
print("[-] Error accessing webpage for hash list {}"" - continuing..".format(x))
continue
Dopo aver eseguito correttamente i passaggi precedenti, apriremo il file di testo del set di hash in modalità + da aggiungere alla fine del file di testo.
with open(hashset, "a+") as hashfile:
for line in hashes_list:
if not line.startswith("#") and line != "":
hashes_downloaded += 1
hashfile.write(line + '\n')
print("[+] Finished downloading {} hashes into {}".format(
hashes_downloaded, hashset))
Dopo aver eseguito lo script precedente, otterrai l'ultimo elenco hash contenente i valori hash MD5 in formato testo.
I capitoli precedenti hanno discusso dell'importanza e del processo di analisi di rete e dei concetti coinvolti. In questo capitolo, impariamo a conoscere il ruolo delle e-mail nell'analisi forense digitale e la loro indagine utilizzando Python.
Ruolo dell'e-mail nelle indagini
Le e-mail svolgono un ruolo molto importante nelle comunicazioni aziendali e sono emerse come una delle applicazioni più importanti su Internet. Sono una comoda modalità per inviare messaggi e documenti, non solo da computer ma anche da altri gadget elettronici come telefoni cellulari e tablet.
Il lato negativo delle e-mail è che i criminali possono far trapelare informazioni importanti sulla loro azienda. Pertanto, il ruolo delle e-mail nell'analisi forense digitale è stato aumentato negli ultimi anni. Nella medicina legale digitale, le e-mail sono considerate prove cruciali e l'analisi dell'intestazione delle e-mail è diventata importante per raccogliere prove durante il processo forense.
Un investigatore ha i seguenti obiettivi durante l'esecuzione di analisi forensi della posta elettronica:
- Per identificare il criminale principale
- Per raccogliere le prove necessarie
- Per presentare i risultati
- Per costruire il caso
Sfide in Email Forensics
L'analisi forense della posta elettronica svolge un ruolo molto importante nelle indagini poiché la maggior parte della comunicazione nell'era attuale si basa sulle e-mail. Tuttavia, un investigatore forense di posta elettronica potrebbe affrontare le seguenti sfide durante l'indagine:
E-mail false
La sfida più grande nell'analisi forense delle e-mail è l'uso di e-mail false che vengono create manipolando e scripting intestazioni ecc. In questa categoria i criminali usano anche e-mail temporanee che è un servizio che consente a un utente registrato di ricevere e-mail a un indirizzo temporaneo che scade dopo un certo periodo di tempo.
Spoofing
Un'altra sfida nell'analisi forense della posta elettronica è lo spoofing in cui i criminali presentavano un'e-mail come quella di qualcun altro. In questo caso la macchina riceverà sia un indirizzo IP falso che uno originale.
Re-mail anonimo
Qui, il server di posta elettronica rimuove le informazioni di identificazione dal messaggio di posta elettronica prima di inoltrarlo ulteriormente. Questo porta a un'altra grande sfida per le indagini sulla posta elettronica.
Tecniche utilizzate nelle indagini forensi tramite posta elettronica
L'analisi forense delle e-mail è lo studio dell'origine e del contenuto delle e-mail come prova per identificare il mittente e il destinatario effettivi di un messaggio insieme ad alcune altre informazioni come la data / ora di trasmissione e l'intenzione del mittente. Implica l'analisi dei metadati, la scansione delle porte e la ricerca di parole chiave.
Alcune delle tecniche comuni che possono essere utilizzate per l'indagine forense della posta elettronica sono
- Analisi dell'intestazione
- Indagine sul server
- Indagine sui dispositivi di rete
- Impronte digitali del mittente del bollettino
- Identificatori software incorporati
Nelle sezioni seguenti, impareremo come recuperare le informazioni utilizzando Python ai fini dell'indagine sulla posta elettronica.
Estrazione di informazioni dai file EML
I file EML sono fondamentalmente messaggi di posta elettronica in formato di file ampiamente utilizzati per la memorizzazione dei messaggi di posta elettronica. Sono file di testo strutturati compatibili con più client di posta elettronica come Microsoft Outlook, Outlook Express e Windows Live Mail.
Un file EML memorizza le intestazioni delle e-mail, il contenuto del corpo, i dati degli allegati come testo normale. Utilizza base64 per codificare i dati binari e la codifica Quoted-Printable (QP) per memorizzare le informazioni sul contenuto. Lo script Python che può essere utilizzato per estrarre le informazioni dal file EML è fornito di seguito:
Innanzitutto, importa le seguenti librerie Python come mostrato di seguito:
from __future__ import print_function
from argparse import ArgumentParser, FileType
from email import message_from_file
import os
import quopri
import base64
Nelle biblioteche di cui sopra, quopriviene utilizzato per decodificare i valori codificati QP dai file EML. Qualsiasi dato codificato in base64 può essere decodificato con l'aiuto dibase64 biblioteca.
Successivamente, forniamo un argomento per il gestore della riga di comando. Nota che qui accetterà solo un argomento che sarebbe il percorso del file EML come mostrato di seguito -
if __name__ == '__main__':
parser = ArgumentParser('Extracting information from EML file')
parser.add_argument("EML_FILE",help="Path to EML File", type=FileType('r'))
args = parser.parse_args()
main(args.EML_FILE)
Ora, dobbiamo definire main() funzione in cui useremo il metodo denominato message_from_file()dalla libreria di posta elettronica per leggere il file come oggetto. Qui accediamo alle intestazioni, al contenuto del corpo, agli allegati e ad altre informazioni sul payload utilizzando la variabile risultante denominataemlfile come mostrato nel codice riportato di seguito -
def main(input_file):
emlfile = message_from_file(input_file)
for key, value in emlfile._headers:
print("{}: {}".format(key, value))
print("\nBody\n")
if emlfile.is_multipart():
for part in emlfile.get_payload():
process_payload(part)
else:
process_payload(emlfile[1])
Ora, dobbiamo definire process_payload() metodo in cui estrarremo il contenuto del corpo del messaggio utilizzando get_payload()metodo. Decodificheremo i dati codificati QP utilizzandoquopri.decodestring()funzione. Verificheremo anche il tipo MIME del contenuto in modo che possa gestire correttamente l'archiviazione dell'email. Rispettare il codice riportato di seguito -
def process_payload(payload):
print(payload.get_content_type() + "\n" + "=" * len(payload.get_content_type()))
body = quopri.decodestring(payload.get_payload())
if payload.get_charset():
body = body.decode(payload.get_charset())
else:
try:
body = body.decode()
except UnicodeDecodeError:
body = body.decode('cp1252')
if payload.get_content_type() == "text/html":
outfile = os.path.basename(args.EML_FILE.name) + ".html"
open(outfile, 'w').write(body)
elif payload.get_content_type().startswith('application'):
outfile = open(payload.get_filename(), 'wb')
body = base64.b64decode(payload.get_payload())
outfile.write(body)
outfile.close()
print("Exported: {}\n".format(outfile.name))
else:
print(body)
Dopo aver eseguito lo script precedente, otterremo le informazioni di intestazione insieme a vari payload sulla console.
Analisi dei file MSG utilizzando Python
I messaggi di posta elettronica sono disponibili in molti formati diversi. MSG è uno di questi tipi di formato utilizzato da Microsoft Outlook ed Exchange. I file con estensione MSG possono contenere testo ASCII semplice per le intestazioni e il corpo principale del messaggio, nonché collegamenti ipertestuali e allegati.
In questa sezione, impareremo come estrarre le informazioni dal file MSG utilizzando l'API di Outlook. Nota che il seguente script Python funzionerà solo su Windows. Per questo, dobbiamo installare la libreria Python di terze parti denominatapywin32 come segue -
pip install pywin32
Ora, importa le seguenti librerie usando i comandi mostrati:
from __future__ import print_function
from argparse import ArgumentParser
import os
import win32com.client
import pywintypes
Ora, forniamo un argomento per il gestore della riga di comando. Qui accetterà due argomenti uno sarebbe il percorso del file MSG e l'altro sarebbe la cartella di output desiderata come segue:
if __name__ == '__main__':
parser = ArgumentParser(‘Extracting information from MSG file’)
parser.add_argument("MSG_FILE", help="Path to MSG file")
parser.add_argument("OUTPUT_DIR", help="Path to output folder")
args = parser.parse_args()
out_dir = args.OUTPUT_DIR
if not os.path.exists(out_dir):
os.makedirs(out_dir)
main(args.MSG_FILE, args.OUTPUT_DIR)
Ora, dobbiamo definire main() funzione in cui chiameremo win32com libreria per la configurazione Outlook API che consente inoltre l'accesso a MAPI spazio dei nomi.
def main(msg_file, output_dir):
mapi = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
msg = mapi.OpenSharedItem(os.path.abspath(args.MSG_FILE))
display_msg_attribs(msg)
display_msg_recipients(msg)
extract_msg_body(msg, output_dir)
extract_attachments(msg, output_dir)
Ora, definisci le diverse funzioni che stiamo usando in questo script. Il codice riportato di seguito mostra la definizione didisplay_msg_attribs() funzione che ci consente di visualizzare vari attributi di un messaggio come oggetto, a, BCC, CC, Dimensione, SenderName, inviato, ecc.
def display_msg_attribs(msg):
attribs = [
'Application', 'AutoForwarded', 'BCC', 'CC', 'Class',
'ConversationID', 'ConversationTopic', 'CreationTime',
'ExpiryTime', 'Importance', 'InternetCodePage', 'IsMarkedAsTask',
'LastModificationTime', 'Links','ReceivedTime', 'ReminderSet',
'ReminderTime', 'ReplyRecipientNames', 'Saved', 'Sender',
'SenderEmailAddress', 'SenderEmailType', 'SenderName', 'Sent',
'SentOn', 'SentOnBehalfOfName', 'Size', 'Subject',
'TaskCompletedDate', 'TaskDueDate', 'To', 'UnRead'
]
print("\nMessage Attributes")
for entry in attribs:
print("{}: {}".format(entry, getattr(msg, entry, 'N/A')))
Ora, definisci il file display_msg_recipeints() funzione che itera attraverso i messaggi e visualizza i dettagli del destinatario.
def display_msg_recipients(msg):
recipient_attrib = ['Address', 'AutoResponse', 'Name', 'Resolved', 'Sendable']
i = 1
while True:
try:
recipient = msg.Recipients(i)
except pywintypes.com_error:
break
print("\nRecipient {}".format(i))
print("=" * 15)
for entry in recipient_attrib:
print("{}: {}".format(entry, getattr(recipient, entry, 'N/A')))
i += 1
Successivamente, definiamo extract_msg_body() funzione che estrae il contenuto del corpo, HTML e testo normale, dal messaggio.
def extract_msg_body(msg, out_dir):
html_data = msg.HTMLBody.encode('cp1252')
outfile = os.path.join(out_dir, os.path.basename(args.MSG_FILE))
open(outfile + ".body.html", 'wb').write(html_data)
print("Exported: {}".format(outfile + ".body.html"))
body_data = msg.Body.encode('cp1252')
open(outfile + ".body.txt", 'wb').write(body_data)
print("Exported: {}".format(outfile + ".body.txt"))
Successivamente, definiremo il extract_attachments() funzione che esporta i dati degli allegati nella directory di output desiderata.
def extract_attachments(msg, out_dir):
attachment_attribs = ['DisplayName', 'FileName', 'PathName', 'Position', 'Size']
i = 1 # Attachments start at 1
while True:
try:
attachment = msg.Attachments(i)
except pywintypes.com_error:
break
Una volta definite tutte le funzioni, stamperemo tutti gli attributi sulla console con la seguente riga di codici:
print("\nAttachment {}".format(i))
print("=" * 15)
for entry in attachment_attribs:
print('{}: {}'.format(entry, getattr(attachment, entry,"N/A")))
outfile = os.path.join(os.path.abspath(out_dir),os.path.split(args.MSG_FILE)[-1])
if not os.path.exists(outfile):
os.makedirs(outfile)
outfile = os.path.join(outfile, attachment.FileName)
attachment.SaveAsFile(outfile)
print("Exported: {}".format(outfile))
i += 1
Dopo aver eseguito lo script precedente, otterremo gli attributi del messaggio e dei suoi allegati nella finestra della console insieme a diversi file nella directory di output.
Strutturazione di file MBOX da Google Takeout utilizzando Python
I file MBOX sono file di testo con una formattazione speciale che suddividono i messaggi memorizzati all'interno. Si trovano spesso in associazione con sistemi UNIX, Thunderbolt e Google Takeouts.
In questa sezione, vedrai uno script Python, in cui struttureremo i file MBOX ottenuti da Google Takeouts. Ma prima dobbiamo sapere come possiamo generare questi file MBOX utilizzando il nostro account Google o l'account Gmail.
Acquisizione della casella di posta dell'account Google nel formato MBX
L'acquisizione della casella di posta dell'account Google implica il backup del nostro account Gmail. Il backup può essere eseguito per vari motivi personali o professionali. Tieni presente che Google fornisce il backup dei dati di Gmail. Per acquisire la nostra casella di posta dell'account Google nel formato MBOX, è necessario seguire i passaggi indicati di seguito:
Aperto My account pannello di controllo.
Vai alla sezione Informazioni personali e privacy e seleziona il collegamento Controlla i tuoi contenuti.
Puoi creare un nuovo archivio o puoi gestirne uno esistente. Se clicchiamo,CREATE ARCHIVE link, quindi avremo alcune caselle di controllo per ogni prodotto Google che desideriamo includere.
Dopo aver selezionato i prodotti, avremo la libertà di scegliere il tipo di file e la dimensione massima per il nostro archivio insieme al metodo di consegna da selezionare dall'elenco.
Infine, otterremo questo backup in formato MBOX.
Codice Python
Ora, il file MBOX discusso sopra può essere strutturato utilizzando Python come mostrato di seguito -
Innanzitutto, è necessario importare le librerie Python come segue:
from __future__ import print_function
from argparse import ArgumentParser
import mailbox
import os
import time
import csv
from tqdm import tqdm
import base64
Tutte le librerie sono state utilizzate e spiegate negli script precedenti, ad eccezione di mailbox libreria che viene utilizzata per analizzare i file MBOX.
Fornisci ora un argomento per il gestore della riga di comando. Qui accetterà due argomenti: uno sarebbe il percorso del file MBOX e l'altro sarebbe la cartella di output desiderata.
if __name__ == '__main__':
parser = ArgumentParser('Parsing MBOX files')
parser.add_argument("MBOX", help="Path to mbox file")
parser.add_argument(
"OUTPUT_DIR",help = "Path to output directory to write report ""and exported content")
args = parser.parse_args()
main(args.MBOX, args.OUTPUT_DIR)
Ora, definirà main() funzione e chiamata mbox classe di libreria di cassette postali con l'aiuto della quale possiamo analizzare un file MBOX fornendo il suo percorso -
def main(mbox_file, output_dir):
print("Reading mbox file")
mbox = mailbox.mbox(mbox_file, factory=custom_reader)
print("{} messages to parse".format(len(mbox)))
Ora, definisci un metodo di lettura per mailbox libreria come segue -
def custom_reader(data_stream):
data = data_stream.read()
try:
content = data.decode("ascii")
except (UnicodeDecodeError, UnicodeEncodeError) as e:
content = data.decode("cp1252", errors="replace")
return mailbox.mboxMessage(content)
Ora, crea alcune variabili per ulteriori elaborazioni come segue:
parsed_data = []
attachments_dir = os.path.join(output_dir, "attachments")
if not os.path.exists(attachments_dir):
os.makedirs(attachments_dir)
columns = [
"Date", "From", "To", "Subject", "X-Gmail-Labels", "Return-Path", "Received",
"Content-Type", "Message-ID","X-GM-THRID", "num_attachments_exported", "export_path"]
Quindi, usa tqdm per generare una barra di avanzamento e per tenere traccia del processo di iterazione come segue:
for message in tqdm(mbox):
msg_data = dict()
header_data = dict(message._headers)
for hdr in columns:
msg_data[hdr] = header_data.get(hdr, "N/A")
Ora, controlla che il messaggio meteo abbia carichi utili o meno. Se sta avendo, definiremowrite_payload() metodo come segue -
if len(message.get_payload()):
export_path = write_payload(message, attachments_dir)
msg_data['num_attachments_exported'] = len(export_path)
msg_data['export_path'] = ", ".join(export_path)
Ora, i dati devono essere aggiunti. Allora chiameremocreate_report() metodo come segue -
parsed_data.append(msg_data)
create_report(
parsed_data, os.path.join(output_dir, "mbox_report.csv"), columns)
def write_payload(msg, out_dir):
pyld = msg.get_payload()
export_path = []
if msg.is_multipart():
for entry in pyld:
export_path += write_payload(entry, out_dir)
else:
content_type = msg.get_content_type()
if "application/" in content_type.lower():
content = base64.b64decode(msg.get_payload())
export_path.append(export_content(msg, out_dir, content))
elif "image/" in content_type.lower():
content = base64.b64decode(msg.get_payload())
export_path.append(export_content(msg, out_dir, content))
elif "video/" in content_type.lower():
content = base64.b64decode(msg.get_payload())
export_path.append(export_content(msg, out_dir, content))
elif "audio/" in content_type.lower():
content = base64.b64decode(msg.get_payload())
export_path.append(export_content(msg, out_dir, content))
elif "text/csv" in content_type.lower():
content = base64.b64decode(msg.get_payload())
export_path.append(export_content(msg, out_dir, content))
elif "info/" in content_type.lower():
export_path.append(export_content(msg, out_dir,
msg.get_payload()))
elif "text/calendar" in content_type.lower():
export_path.append(export_content(msg, out_dir,
msg.get_payload()))
elif "text/rtf" in content_type.lower():
export_path.append(export_content(msg, out_dir,
msg.get_payload()))
else:
if "name=" in msg.get('Content-Disposition', "N/A"):
content = base64.b64decode(msg.get_payload())
export_path.append(export_content(msg, out_dir, content))
elif "name=" in msg.get('Content-Type', "N/A"):
content = base64.b64decode(msg.get_payload())
export_path.append(export_content(msg, out_dir, content))
return export_path
Osserva che le affermazioni if-else di cui sopra sono facili da capire. Ora, dobbiamo definire un metodo che estrarrà il nome del file dal filemsg oggetto come segue -
def export_content(msg, out_dir, content_data):
file_name = get_filename(msg)
file_ext = "FILE"
if "." in file_name: file_ext = file_name.rsplit(".", 1)[-1]
file_name = "{}_{:.4f}.{}".format(file_name.rsplit(".", 1)[0], time.time(), file_ext)
file_name = os.path.join(out_dir, file_name)
Ora, con l'aiuto delle seguenti righe di codice, puoi effettivamente esportare il file -
if isinstance(content_data, str):
open(file_name, 'w').write(content_data)
else:
open(file_name, 'wb').write(content_data)
return file_name
Ora, definiamo una funzione per estrarre i nomi dei file dal file message per rappresentare accuratamente i nomi di questi file come segue:
def get_filename(msg):
if 'name=' in msg.get("Content-Disposition", "N/A"):
fname_data = msg["Content-Disposition"].replace("\r\n", " ")
fname = [x for x in fname_data.split("; ") if 'name=' in x]
file_name = fname[0].split("=", 1)[-1]
elif 'name=' in msg.get("Content-Type", "N/A"):
fname_data = msg["Content-Type"].replace("\r\n", " ")
fname = [x for x in fname_data.split("; ") if 'name=' in x]
file_name = fname[0].split("=", 1)[-1]
else:
file_name = "NO_FILENAME"
fchars = [x for x in file_name if x.isalnum() or x.isspace() or x == "."]
return "".join(fchars)
Ora possiamo scrivere un file CSV definendo l'estensione create_report() funzionare come segue -
def create_report(output_data, output_file, columns):
with open(output_file, 'w', newline="") as outfile:
csvfile = csv.DictWriter(outfile, columns)
csvfile.writeheader()
csvfile.writerows(output_data)
Una volta eseguito lo script sopra riportato, otterremo il rapporto CSV e la directory piena di allegati.
Questo capitolo illustrerà i vari concetti coinvolti nell'analisi forense di Microsoft Windows e gli importanti artefatti che un investigatore può ottenere dal processo di indagine.
introduzione
Gli artefatti sono gli oggetti o le aree all'interno di un sistema informatico che contengono informazioni importanti relative alle attività svolte dall'utente del computer. Il tipo e la posizione di queste informazioni dipendono dal sistema operativo. Durante l'analisi forense, questi artefatti svolgono un ruolo molto importante nell'approvare o disapprovare l'osservazione dell'investigatore.
Importanza degli artefatti di Windows per l'analisi forense
Gli artefatti di Windows assumono importanza per i seguenti motivi:
Circa il 90% del traffico nel mondo proviene dai computer che utilizzano Windows come sistema operativo. Ecco perché per gli esaminatori di digital forensics gli artefatti di Windows sono molto essenziali.
Il sistema operativo Windows memorizza diversi tipi di evidenze relative all'attività dell'utente sul sistema informatico. Questo è un altro motivo che mostra l'importanza degli artefatti di Windows per la digital forensics.
Molte volte l'investigatore ruota l'indagine attorno ad aree vecchie e tradizionali come i dati raccolti dagli utenti. Gli artefatti di Windows possono condurre l'indagine verso aree non tradizionali come i dati creati dal sistema o gli artefatti.
Windows fornisce una grande abbondanza di artefatti che sono utili per gli investigatori, nonché per le aziende e gli individui che eseguono indagini informali.
L'aumento della criminalità informatica negli ultimi anni è un altro motivo per cui gli artefatti di Windows sono importanti.
Artefatti di Windows e relativi script Python
In questa sezione, discuteremo di alcuni artefatti di Windows e script Python per prelevare informazioni da essi.
Cestino
È uno degli artefatti di Windows importanti per le indagini forensi. Il cestino di Windows contiene i file che sono stati eliminati dall'utente, ma non ancora rimossi fisicamente dal sistema. Anche se l'utente rimuove completamente il file dal sistema, funge da importante fonte di indagine. Questo perché l'esaminatore può estrarre informazioni preziose, come il percorso del file originale e l'ora in cui è stato inviato al Cestino, dai file eliminati.
Si noti che l'archiviazione delle prove del Cestino dipende dalla versione di Windows. Nel seguente script Python, ci occuperemo di Windows 7 dove crea due file:$R file che contiene il contenuto effettivo del file riciclato e $I file che contiene il nome del file originale, il percorso, la dimensione del file quando il file è stato eliminato.
Per lo script Python dobbiamo installare moduli di terze parti, vale a dire pytsk3, pyewf e unicodecsv. Possiamo usarepipper installarli. Possiamo seguire i seguenti passaggi per estrarre le informazioni dal Cestino:
Innanzitutto, dobbiamo utilizzare il metodo ricorsivo per eseguire la scansione del file $Recycle.bin cartella e seleziona tutti i file che iniziano con $I.
Successivamente, leggeremo il contenuto dei file e analizzeremo le strutture di metadati disponibili.
Ora cercheremo il file $ R associato.
Alla fine, scriveremo i risultati nel file CSV per la revisione.
Vediamo come utilizzare il codice Python per questo scopo -
Innanzitutto, dobbiamo importare le seguenti librerie Python:
from __future__ import print_function
from argparse import ArgumentParser
import datetime
import os
import struct
from utility.pytskutil import TSKUtil
import unicodecsv as csv
Successivamente, dobbiamo fornire un argomento per il gestore della riga di comando. Nota che qui accetterà tre argomenti: il primo è il percorso del file delle prove, il secondo è il tipo di file delle prove e il terzo è il percorso di output desiderato per il report CSV, come mostrato di seguito -
if __name__ == '__main__':
parser = argparse.ArgumentParser('Recycle Bin evidences')
parser.add_argument('EVIDENCE_FILE', help = "Path to evidence file")
parser.add_argument('IMAGE_TYPE', help = "Evidence file format",
choices = ('ewf', 'raw'))
parser.add_argument('CSV_REPORT', help = "Path to CSV report")
args = parser.parse_args()
main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.CSV_REPORT)
Ora, definisci il file main()funzione che gestirà tutta l'elaborazione. Cercherà$I file come segue -
def main(evidence, image_type, report_file):
tsk_util = TSKUtil(evidence, image_type)
dollar_i_files = tsk_util.recurse_files("$I", path = '/$Recycle.bin',logic = "startswith") if dollar_i_files is not None: processed_files = process_dollar_i(tsk_util, dollar_i_files) write_csv(report_file,['file_path', 'file_size', 'deleted_time','dollar_i_file', 'dollar_r_file', 'is_directory'],processed_files) else: print("No $I files found")
Ora, se abbiamo trovato $I file, quindi deve essere inviato a process_dollar_i() funzione che accetterà il file tsk_util oggetto così come l'elenco di $I file, come mostrato di seguito -
def process_dollar_i(tsk_util, dollar_i_files):
processed_files = []
for dollar_i in dollar_i_files:
file_attribs = read_dollar_i(dollar_i[2])
if file_attribs is None:
continue
file_attribs['dollar_i_file'] = os.path.join('/$Recycle.bin', dollar_i[1][1:])
Ora, cerca i file $ R come segue:
recycle_file_path = os.path.join('/$Recycle.bin',dollar_i[1].rsplit("/", 1)[0][1:]) dollar_r_files = tsk_util.recurse_files( "$R" + dollar_i[0][2:],path = recycle_file_path, logic = "startswith")
if dollar_r_files is None:
dollar_r_dir = os.path.join(recycle_file_path,"$R" + dollar_i[0][2:])
dollar_r_dirs = tsk_util.query_directory(dollar_r_dir)
if dollar_r_dirs is None:
file_attribs['dollar_r_file'] = "Not Found"
file_attribs['is_directory'] = 'Unknown'
else:
file_attribs['dollar_r_file'] = dollar_r_dir
file_attribs['is_directory'] = True
else:
dollar_r = [os.path.join(recycle_file_path, r[1][1:])for r in dollar_r_files]
file_attribs['dollar_r_file'] = ";".join(dollar_r)
file_attribs['is_directory'] = False
processed_files.append(file_attribs)
return processed_files
Adesso definisci read_dollar_i() metodo per leggere il file $Ifile, in altre parole, analizzano i metadati. Noi useremoread_random()metodo per leggere i primi otto byte della firma. Questo non restituirà nessuno se la firma non corrisponde. Dopodiché, dovremo leggere e decomprimere i valori da$I file se questo è un file valido.
def read_dollar_i(file_obj):
if file_obj.read_random(0, 8) != '\x01\x00\x00\x00\x00\x00\x00\x00':
return None
raw_file_size = struct.unpack('<q', file_obj.read_random(8, 8))
raw_deleted_time = struct.unpack('<q', file_obj.read_random(16, 8))
raw_file_path = file_obj.read_random(24, 520)
Ora, dopo aver estratto questi file, dobbiamo interpretare gli interi in valori leggibili dall'uomo utilizzando sizeof_fmt() funziona come mostrato di seguito -
file_size = sizeof_fmt(raw_file_size[0])
deleted_time = parse_windows_filetime(raw_deleted_time[0])
file_path = raw_file_path.decode("utf16").strip("\x00")
return {'file_size': file_size, 'file_path': file_path,'deleted_time': deleted_time}
Ora, dobbiamo definire sizeof_fmt() funzionare come segue -
def sizeof_fmt(num, suffix = 'B'):
for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix)
Ora, definisci una funzione per interi interpretati in data e ora formattate come segue:
def parse_windows_filetime(date_value):
microseconds = float(date_value) / 10
ts = datetime.datetime(1601, 1, 1) + datetime.timedelta(
microseconds = microseconds)
return ts.strftime('%Y-%m-%d %H:%M:%S.%f')
Ora definiremo write_csv() metodo per scrivere i risultati elaborati in un file CSV come segue:
def write_csv(outfile, fieldnames, data):
with open(outfile, 'wb') as open_outfile:
csvfile = csv.DictWriter(open_outfile, fieldnames)
csvfile.writeheader()
csvfile.writerows(data)
Quando esegui lo script precedente, otterremo i dati dal file $ I e $ R.
Note adesive
Windows Sticky Notes sostituisce l'abitudine del mondo reale di scrivere con carta e penna. Queste note erano solite galleggiare sul desktop con diverse opzioni per colori, caratteri ecc. In Windows 7 il file Sticky Notes è memorizzato come file OLE, quindi nel seguente script Python esamineremo questo file OLE per estrarre i metadati da Sticky Notes.
Per questo script Python, dobbiamo installare moduli di terze parti, vale a dire olefile, pytsk3, pyewfe unicodecsv. Possiamo usare il comandopip per installarli.
Possiamo seguire i passaggi discussi di seguito per estrarre le informazioni dal file di note adesive StickyNote.sn -
In primo luogo, apri il file delle prove e trova tutti i file StickyNote.snt.
Quindi, analizzare i metadati e il contenuto dal flusso OLE e scrivere il contenuto RTF nei file.
Infine, crea un rapporto CSV di questi metadati.
Codice Python
Vediamo come utilizzare il codice Python per questo scopo -
Innanzitutto, importa le seguenti librerie Python:
from __future__ import print_function
from argparse import ArgumentParser
import unicodecsv as csv
import os
import StringIO
from utility.pytskutil import TSKUtil
import olefile
Quindi, definisci una variabile globale che verrà utilizzata in questo script:
REPORT_COLS = ['note_id', 'created', 'modified', 'note_text', 'note_file']
Successivamente, dobbiamo fornire un argomento per il gestore della riga di comando. Nota che qui accetterà tre argomenti: il primo è il percorso del file di prova, il secondo è il tipo di file di prova e il terzo è il percorso di output desiderato come segue:
if __name__ == '__main__':
parser = argparse.ArgumentParser('Evidence from Sticky Notes')
parser.add_argument('EVIDENCE_FILE', help="Path to evidence file")
parser.add_argument('IMAGE_TYPE', help="Evidence file format",choices=('ewf', 'raw'))
parser.add_argument('REPORT_FOLDER', help="Path to report folder")
args = parser.parse_args()
main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.REPORT_FOLDER)
Ora definiremo main() funzione che sarà simile allo script precedente come mostrato di seguito -
def main(evidence, image_type, report_folder):
tsk_util = TSKUtil(evidence, image_type)
note_files = tsk_util.recurse_files('StickyNotes.snt', '/Users','equals')
Ora, iteriamo attraverso i file risultanti. Allora chiameremoparse_snt_file() per elaborare il file e quindi scriveremo il file RTF con l'estensione write_note_rtf() metodo come segue -
report_details = []
for note_file in note_files:
user_dir = note_file[1].split("/")[1]
file_like_obj = create_file_like_obj(note_file[2])
note_data = parse_snt_file(file_like_obj)
if note_data is None:
continue
write_note_rtf(note_data, os.path.join(report_folder, user_dir))
report_details += prep_note_report(note_data, REPORT_COLS,"/Users" + note_file[1])
write_csv(os.path.join(report_folder, 'sticky_notes.csv'), REPORT_COLS,report_details)
Successivamente, dobbiamo definire le varie funzioni utilizzate in questo script.
Prima di tutto definiremo create_file_like_obj() funzione per leggere la dimensione del file prendendo pytskoggetto file. Quindi definiremoparse_snt_file() funzione che accetterà l'oggetto simile a file come input e viene utilizzata per leggere e interpretare il file di note adesive.
def parse_snt_file(snt_file):
if not olefile.isOleFile(snt_file):
print("This is not an OLE file")
return None
ole = olefile.OleFileIO(snt_file)
note = {}
for stream in ole.listdir():
if stream[0].count("-") == 3:
if stream[0] not in note:
note[stream[0]] = {"created": ole.getctime(stream[0]),"modified": ole.getmtime(stream[0])}
content = None
if stream[1] == '0':
content = ole.openstream(stream).read()
elif stream[1] == '3':
content = ole.openstream(stream).read().decode("utf-16")
if content:
note[stream[0]][stream[1]] = content
return note
Ora, crea un file RTF definendo write_note_rtf() funzionare come segue
def write_note_rtf(note_data, report_folder):
if not os.path.exists(report_folder):
os.makedirs(report_folder)
for note_id, stream_data in note_data.items():
fname = os.path.join(report_folder, note_id + ".rtf")
with open(fname, 'w') as open_file:
open_file.write(stream_data['0'])
Ora, tradurremo il dizionario annidato in un elenco semplice di dizionari più appropriati per un foglio di calcolo CSV. Sarà fatto definendoprep_note_report()funzione. Infine, definiremowrite_csv() funzione.
def prep_note_report(note_data, report_cols, note_file):
report_details = []
for note_id, stream_data in note_data.items():
report_details.append({
"note_id": note_id,
"created": stream_data['created'],
"modified": stream_data['modified'],
"note_text": stream_data['3'].strip("\x00"),
"note_file": note_file
})
return report_details
def write_csv(outfile, fieldnames, data):
with open(outfile, 'wb') as open_outfile:
csvfile = csv.DictWriter(open_outfile, fieldnames)
csvfile.writeheader()
csvfile.writerows(data)
Dopo aver eseguito lo script precedente, otterremo i metadati dal file Sticky Notes.
File di registro
I file di registro di Windows contengono molti dettagli importanti che sono come un tesoro di informazioni per un analista forense. È un database gerarchico che contiene i dettagli relativi alla configurazione del sistema operativo, all'attività dell'utente, all'installazione del software ecc. Nel seguente script Python accediamo alle informazioni di base comuni dalSYSTEM e SOFTWARE orticaria.
Per questo script Python, dobbiamo installare moduli di terze parti, vale a dire pytsk3, pyewf e registry. Possiamo usarepip per installarli.
Possiamo seguire i passaggi indicati di seguito per estrarre le informazioni dal registro di Windows -
Innanzitutto, trova gli hive del registro da elaborare in base al nome e al percorso.
Quindi dobbiamo aprire questi file utilizzando StringIO e moduli di registro.
Alla fine dobbiamo elaborare ogni singolo alveare e stampare i valori analizzati sulla console per l'interpretazione.
Codice Python
Vediamo come utilizzare il codice Python per questo scopo -
Innanzitutto, importa le seguenti librerie Python:
from __future__ import print_function
from argparse import ArgumentParser
import datetime
import StringIO
import struct
from utility.pytskutil import TSKUtil
from Registry import Registry
Fornisci ora un argomento per il gestore della riga di comando. Qui accetterà due argomenti: il primo è il percorso del file delle prove, il secondo è il tipo di file delle prove, come mostrato di seguito -
if __name__ == '__main__':
parser = argparse.ArgumentParser('Evidence from Windows Registry')
parser.add_argument('EVIDENCE_FILE', help = "Path to evidence file")
parser.add_argument('IMAGE_TYPE', help = "Evidence file format",
choices = ('ewf', 'raw'))
args = parser.parse_args()
main(args.EVIDENCE_FILE, args.IMAGE_TYPE)
Adesso definiremo main() funzione per la ricerca SYSTEM e SOFTWARE orticaria all'interno /Windows/System32/config cartella come segue -
def main(evidence, image_type):
tsk_util = TSKUtil(evidence, image_type)
tsk_system_hive = tsk_util.recurse_files('system', '/Windows/system32/config', 'equals')
tsk_software_hive = tsk_util.recurse_files('software', '/Windows/system32/config', 'equals')
system_hive = open_file_as_reg(tsk_system_hive[0][2])
software_hive = open_file_as_reg(tsk_software_hive[0][2])
process_system_hive(system_hive)
process_software_hive(software_hive)
Ora, definisci la funzione per aprire il file di registro. A tal fine, dobbiamo raccogliere la dimensione del file dapytsk metadati come segue -
def open_file_as_reg(reg_file):
file_size = reg_file.info.meta.size
file_content = reg_file.read_random(0, file_size)
file_like_obj = StringIO.StringIO(file_content)
return Registry.Registry(file_like_obj)
Ora, con l'aiuto del seguente metodo, possiamo elaborare SYSTEM> alveare -
def process_system_hive(hive):
root = hive.root()
current_control_set = root.find_key("Select").value("Current").value()
control_set = root.find_key("ControlSet{:03d}".format(current_control_set))
raw_shutdown_time = struct.unpack(
'<Q', control_set.find_key("Control").find_key("Windows").value("ShutdownTime").value())
shutdown_time = parse_windows_filetime(raw_shutdown_time[0])
print("Last Shutdown Time: {}".format(shutdown_time))
time_zone = control_set.find_key("Control").find_key("TimeZoneInformation")
.value("TimeZoneKeyName").value()
print("Machine Time Zone: {}".format(time_zone))
computer_name = control_set.find_key("Control").find_key("ComputerName").find_key("ComputerName")
.value("ComputerName").value()
print("Machine Name: {}".format(computer_name))
last_access = control_set.find_key("Control").find_key("FileSystem")
.value("NtfsDisableLastAccessUpdate").value()
last_access = "Disabled" if last_access == 1 else "enabled"
print("Last Access Updates: {}".format(last_access))
Ora, dobbiamo definire una funzione per interi interpretati in data e ora formattate come segue:
def parse_windows_filetime(date_value):
microseconds = float(date_value) / 10
ts = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds = microseconds)
return ts.strftime('%Y-%m-%d %H:%M:%S.%f')
def parse_unix_epoch(date_value):
ts = datetime.datetime.fromtimestamp(date_value)
return ts.strftime('%Y-%m-%d %H:%M:%S.%f')
Ora con l'aiuto del seguente metodo possiamo elaborare SOFTWARE alveare -
def process_software_hive(hive):
root = hive.root()
nt_curr_ver = root.find_key("Microsoft").find_key("Windows NT")
.find_key("CurrentVersion")
print("Product name: {}".format(nt_curr_ver.value("ProductName").value()))
print("CSD Version: {}".format(nt_curr_ver.value("CSDVersion").value()))
print("Current Build: {}".format(nt_curr_ver.value("CurrentBuild").value()))
print("Registered Owner: {}".format(nt_curr_ver.value("RegisteredOwner").value()))
print("Registered Org:
{}".format(nt_curr_ver.value("RegisteredOrganization").value()))
raw_install_date = nt_curr_ver.value("InstallDate").value()
install_date = parse_unix_epoch(raw_install_date)
print("Installation Date: {}".format(install_date))
Dopo aver eseguito lo script precedente, otterremo i metadati memorizzati nei file del registro di Windows.
Questo capitolo parla di alcuni artefatti più importanti in Windows e del loro metodo di estrazione usando Python.
Attività degli utenti
Windows con NTUSER.DATfile per memorizzare varie attività dell'utente. Ogni profilo utente sta avendo come hiveNTUSER.DAT, che memorizza le informazioni e le configurazioni relative a quell'utente in modo specifico. Quindi, è molto utile ai fini delle indagini da parte degli analisti forensi.
Il seguente script Python analizzerà alcune delle chiavi di NTUSER.DATper esplorare le azioni di un utente sul sistema. Prima di procedere ulteriormente, per lo script Python, dobbiamo installare moduli di terze parti, vale a direRegistry, pytsk3, pyewf e Jinja2. Possiamo usare pip per installarli.
Possiamo seguire i seguenti passaggi per estrarre le informazioni da NTUSER.DAT file -
Per prima cosa, cerca tutto NTUSER.DAT file nel sistema.
Quindi analizza il file WordWheelQuery, TypePath and RunMRU chiave per ciascuno NTUSER.DAT file.
Infine scriveremo questi artefatti, già elaborati, in un report HTML utilizzando Jinja2 fmodule.
Codice Python
Vediamo come utilizzare il codice Python per questo scopo -
Prima di tutto, dobbiamo importare i seguenti moduli Python:
from __future__ import print_function
from argparse import ArgumentParser
import os
import StringIO
import struct
from utility.pytskutil import TSKUtil
from Registry import Registry
import jinja2
Fornisci ora un argomento per il gestore della riga di comando. Qui accetterà tre argomenti: il primo è il percorso del file di prova, il secondo è il tipo di file di prova e il terzo è il percorso di output desiderato per il report HTML, come mostrato di seguito -
if __name__ == '__main__':
parser = argparse.ArgumentParser('Information from user activities')
parser.add_argument('EVIDENCE_FILE',help = "Path to evidence file")
parser.add_argument('IMAGE_TYPE',help = "Evidence file format",choices = ('ewf', 'raw'))
parser.add_argument('REPORT',help = "Path to report file")
args = parser.parse_args()
main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.REPORT)
Ora, definiamo main() funzione per cercare tutto NTUSER.DAT file, come mostrato -
def main(evidence, image_type, report):
tsk_util = TSKUtil(evidence, image_type)
tsk_ntuser_hives = tsk_util.recurse_files('ntuser.dat','/Users', 'equals')
nt_rec = {
'wordwheel': {'data': [], 'title': 'WordWheel Query'},
'typed_path': {'data': [], 'title': 'Typed Paths'},
'run_mru': {'data': [], 'title': 'Run MRU'}
}
Ora proveremo a trovare la chiave in NTUSER.DAT file e una volta trovato, definisci le funzioni di elaborazione dell'utente come mostrato di seguito -
for ntuser in tsk_ntuser_hives:
uname = ntuser[1].split("/")
open_ntuser = open_file_as_reg(ntuser[2])
try:
explorer_key = open_ntuser.root().find_key("Software").find_key("Microsoft")
.find_key("Windows").find_key("CurrentVersion").find_key("Explorer")
except Registry.RegistryKeyNotFoundException:
continue
nt_rec['wordwheel']['data'] += parse_wordwheel(explorer_key, uname)
nt_rec['typed_path']['data'] += parse_typed_paths(explorer_key, uname)
nt_rec['run_mru']['data'] += parse_run_mru(explorer_key, uname)
nt_rec['wordwheel']['headers'] = \ nt_rec['wordwheel']['data'][0].keys()
nt_rec['typed_path']['headers'] = \ nt_rec['typed_path']['data'][0].keys()
nt_rec['run_mru']['headers'] = \ nt_rec['run_mru']['data'][0].keys()
Ora, passa l'oggetto del dizionario e il suo percorso a write_html() metodo come segue -
write_html(report, nt_rec)
Ora, definisci un metodo, che richiede pytsk gestire il file e leggerlo nella classe Registry tramite StringIO classe.
def open_file_as_reg(reg_file):
file_size = reg_file.info.meta.size
file_content = reg_file.read_random(0, file_size)
file_like_obj = StringIO.StringIO(file_content)
return Registry.Registry(file_like_obj)
Ora definiremo la funzione che analizzerà e gestirà WordWheelQuery chiave da NTUSER.DAT file come segue -
def parse_wordwheel(explorer_key, username):
try:
wwq = explorer_key.find_key("WordWheelQuery")
except Registry.RegistryKeyNotFoundException:
return []
mru_list = wwq.value("MRUListEx").value()
mru_order = []
for i in xrange(0, len(mru_list), 2):
order_val = struct.unpack('h', mru_list[i:i + 2])[0]
if order_val in mru_order and order_val in (0, -1):
break
else:
mru_order.append(order_val)
search_list = []
for count, val in enumerate(mru_order):
ts = "N/A"
if count == 0:
ts = wwq.timestamp()
search_list.append({
'timestamp': ts,
'username': username,
'order': count,
'value_name': str(val),
'search': wwq.value(str(val)).value().decode("UTF-16").strip("\x00")
})
return search_list
Ora definiremo la funzione che analizzerà e gestirà TypedPaths chiave da NTUSER.DAT file come segue -
def parse_typed_paths(explorer_key, username):
try:
typed_paths = explorer_key.find_key("TypedPaths")
except Registry.RegistryKeyNotFoundException:
return []
typed_path_details = []
for val in typed_paths.values():
typed_path_details.append({
"username": username,
"value_name": val.name(),
"path": val.value()
})
return typed_path_details
Ora definiremo la funzione che analizzerà e gestirà RunMRU chiave da NTUSER.DAT file come segue -
def parse_run_mru(explorer_key, username):
try:
run_mru = explorer_key.find_key("RunMRU")
except Registry.RegistryKeyNotFoundException:
return []
if len(run_mru.values()) == 0:
return []
mru_list = run_mru.value("MRUList").value()
mru_order = []
for i in mru_list:
mru_order.append(i)
mru_details = []
for count, val in enumerate(mru_order):
ts = "N/A"
if count == 0:
ts = run_mru.timestamp()
mru_details.append({
"username": username,
"timestamp": ts,
"order": count,
"value_name": val,
"run_statement": run_mru.value(val).value()
})
return mru_details
Ora, la seguente funzione gestirà la creazione del report HTML:
def write_html(outfile, data_dict):
cwd = os.path.dirname(os.path.abspath(__file__))
env = jinja2.Environment(loader=jinja2.FileSystemLoader(cwd))
template = env.get_template("user_activity.html")
rendering = template.render(nt_data=data_dict)
with open(outfile, 'w') as open_outfile:
open_outfile.write(rendering)
Finalmente possiamo scrivere un documento HTML per il report. Dopo aver eseguito lo script precedente, otterremo le informazioni dal file NTUSER.DAT in formato documento HTML.
LINK file
I file di collegamenti vengono creati quando un utente o il sistema operativo crea file di collegamento per i file utilizzati di frequente, a cui si fa doppio clic o a cui si accede dalle unità di sistema come l'archiviazione collegata. Questi tipi di file di collegamento sono chiamati file di collegamento. Accedendo a questi file di collegamento, un investigatore può trovare l'attività della finestra come l'ora e la posizione da cui si è avuto accesso a questi file.
Parliamo dello script Python che possiamo usare per ottenere le informazioni da questi file LINK di Windows.
Per lo script Python, installa moduli di terze parti, vale a dire pylnk, pytsk3, pyewf. Possiamo seguire i seguenti passaggi per estrarre le informazioni dalnk File
Innanzitutto, cerca lnk file all'interno del sistema.
Quindi, estrai le informazioni da quel file scorrendole.
Ora, finalmente, abbiamo bisogno di queste informazioni per un rapporto CSV.
Codice Python
Vediamo come utilizzare il codice Python per questo scopo -
Innanzitutto, importa le seguenti librerie Python:
from __future__ import print_function
from argparse import ArgumentParser
import csv
import StringIO
from utility.pytskutil import TSKUtil
import pylnk
Fornisci ora l'argomento per il gestore della riga di comando. Qui accetterà tre argomenti: il primo è il percorso del file delle prove, il secondo è il tipo di file delle prove e il terzo è il percorso di output desiderato per il report CSV, come mostrato di seguito -
if __name__ == '__main__':
parser = argparse.ArgumentParser('Parsing LNK files')
parser.add_argument('EVIDENCE_FILE', help = "Path to evidence file")
parser.add_argument('IMAGE_TYPE', help = "Evidence file format",choices = ('ewf', 'raw'))
parser.add_argument('CSV_REPORT', help = "Path to CSV report")
args = parser.parse_args()
main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.CSV_REPORT)
Ora, interpreta il file delle prove creando un oggetto di TSKUtil e scorrere il file system per trovare i file che terminano con lnk. Può essere fatto definendomain() funzionare come segue -
def main(evidence, image_type, report):
tsk_util = TSKUtil(evidence, image_type)
lnk_files = tsk_util.recurse_files("lnk", path="/", logic="endswith")
if lnk_files is None:
print("No lnk files found")
exit(0)
columns = [
'command_line_arguments', 'description', 'drive_serial_number',
'drive_type', 'file_access_time', 'file_attribute_flags',
'file_creation_time', 'file_modification_time', 'file_size',
'environmental_variables_location', 'volume_label',
'machine_identifier', 'local_path', 'network_path',
'relative_path', 'working_directory'
]
Ora con l'aiuto del codice seguente, itereremo lnk file creando una funzione come segue:
parsed_lnks = []
for entry in lnk_files:
lnk = open_file_as_lnk(entry[2])
lnk_data = {'lnk_path': entry[1], 'lnk_name': entry[0]}
for col in columns:
lnk_data[col] = getattr(lnk, col, "N/A")
lnk.close()
parsed_lnks.append(lnk_data)
write_csv(report, columns + ['lnk_path', 'lnk_name'], parsed_lnks)
Ora dobbiamo definire due funzioni, una aprirà il file pytsk oggetto file e altro verranno utilizzati per scrivere report CSV come mostrato di seguito -
def open_file_as_lnk(lnk_file):
file_size = lnk_file.info.meta.size
file_content = lnk_file.read_random(0, file_size)
file_like_obj = StringIO.StringIO(file_content)
lnk = pylnk.file()
lnk.open_file_object(file_like_obj)
return lnk
def write_csv(outfile, fieldnames, data):
with open(outfile, 'wb') as open_outfile:
csvfile = csv.DictWriter(open_outfile, fieldnames)
csvfile.writeheader()
csvfile.writerows(data)
Dopo aver eseguito lo script sopra, otterremo le informazioni dal file discovery lnk file in un rapporto CSV -
Precarica file
Ogni volta che un'applicazione viene eseguita per la prima volta da una posizione specifica, Windows crea prefetch files. Questi vengono utilizzati per accelerare il processo di avvio dell'applicazione. L'estensione per questi file è.PF e questi vengono memorizzati nel file ”\Root\Windows\Prefetch” cartella.
Gli esperti forensi digitali possono rivelare le prove dell'esecuzione del programma da una posizione specificata insieme ai dettagli dell'utente. I file di precaricamento sono artefatti utili per l'esaminatore perché la loro voce rimane anche dopo che il programma è stato eliminato o disinstallato.
Parliamo dello script Python che recupererà le informazioni dai file di precaricamento di Windows come indicato di seguito:
Per lo script Python, installa moduli di terze parti, vale a dire pylnk, pytsk3 e unicodecsv. Ricordiamo che abbiamo già lavorato con queste librerie negli script Python che abbiamo discusso nei capitoli precedenti.
Dobbiamo seguire i passaggi indicati di seguito per estrarre le informazioni da prefetch file -
Innanzitutto, cerca .pf file di estensione o file di precaricamento.
Ora, esegui la verifica della firma per eliminare i falsi positivi.
Successivamente, analizza il formato di file di precaricamento di Windows. Questo è diverso con la versione di Windows. Ad esempio, per Windows XP è 17, per Windows Vista e Windows 7 è 23, 26 per Windows 8.1 e 30 per Windows 10.
Infine, scriveremo il risultato analizzato in un file CSV.
Codice Python
Vediamo come utilizzare il codice Python per questo scopo -
Innanzitutto, importa le seguenti librerie Python:
from __future__ import print_function
import argparse
from datetime import datetime, timedelta
import os
import pytsk3
import pyewf
import struct
import sys
import unicodecsv as csv
from utility.pytskutil import TSKUtil
Fornisci ora un argomento per il gestore della riga di comando. Qui accetterà due argomenti, il primo sarebbe il percorso del file delle prove e il secondo sarebbe il tipo di file delle prove. Accetta anche un argomento opzionale per specificare il percorso per la scansione dei file di precaricamento -
if __name__ == "__main__":
parser = argparse.ArgumentParser('Parsing Prefetch files')
parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
parser.add_argument("OUTPUT_CSV", help = "Path to write output csv")
parser.add_argument("-d", help = "Prefetch directory to scan",default = "/WINDOWS/PREFETCH")
args = parser.parse_args()
if os.path.exists(args.EVIDENCE_FILE) and \
os.path.isfile(args.EVIDENCE_FILE):
main(args.EVIDENCE_FILE, args.TYPE, args.OUTPUT_CSV, args.d)
else:
print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
sys.exit(1)
Ora, interpreta il file delle prove creando un oggetto di TSKUtil e scorrere il file system per trovare i file che terminano con .pf. Può essere fatto definendomain() funzionare come segue -
def main(evidence, image_type, output_csv, path):
tsk_util = TSKUtil(evidence, image_type)
prefetch_dir = tsk_util.query_directory(path)
prefetch_files = None
if prefetch_dir is not None:
prefetch_files = tsk_util.recurse_files(".pf", path=path, logic="endswith")
if prefetch_files is None:
print("[-] No .pf files found")
sys.exit(2)
print("[+] Identified {} potential prefetch files".format(len(prefetch_files)))
prefetch_data = []
for hit in prefetch_files:
prefetch_file = hit[2]
pf_version = check_signature(prefetch_file)
Ora, definisci un metodo che eseguirà la convalida delle firme come mostrato di seguito:
def check_signature(prefetch_file):
version, signature = struct.unpack("^<2i", prefetch_file.read_random(0, 8))
if signature == 1094927187:
return version
else:
return None
if pf_version is None:
continue
pf_name = hit[0]
if pf_version == 17:
parsed_data = parse_pf_17(prefetch_file, pf_name)
parsed_data.append(os.path.join(path, hit[1].lstrip("//")))
prefetch_data.append(parsed_data)
Ora inizia a elaborare i file di precaricamento di Windows. Qui stiamo prendendo l'esempio dei file di precaricamento di Windows XP:
def parse_pf_17(prefetch_file, pf_name):
create = convert_unix(prefetch_file.info.meta.crtime)
modify = convert_unix(prefetch_file.info.meta.mtime)
def convert_unix(ts):
if int(ts) == 0:
return ""
return datetime.utcfromtimestamp(ts)
def convert_filetime(ts):
if int(ts) == 0:
return ""
return datetime(1601, 1, 1) + timedelta(microseconds=ts / 10)
Ora, estrai i dati incorporati nei file precaricati utilizzando la struttura come segue:
pf_size, name, vol_info, vol_entries, vol_size, filetime, \
count = struct.unpack("<i60s32x3iq16xi",prefetch_file.read_random(12, 136))
name = name.decode("utf-16", "ignore").strip("/x00").split("/x00")[0]
vol_name_offset, vol_name_length, vol_create, \
vol_serial = struct.unpack("<2iqi",prefetch_file.read_random(vol_info, 20))
vol_serial = hex(vol_serial).lstrip("0x")
vol_serial = vol_serial[:4] + "-" + vol_serial[4:]
vol_name = struct.unpack(
"<{}s".format(2 * vol_name_length),
prefetch_file.read_random(vol_info + vol_name_offset,vol_name_length * 2))[0]
vol_name = vol_name.decode("utf-16", "ignore").strip("/x00").split("/x00")[0]
return [
pf_name, name, pf_size, create,
modify, convert_filetime(filetime), count, vol_name,
convert_filetime(vol_create), vol_serial ]
Poiché abbiamo fornito la versione di precaricamento per Windows XP, ma cosa succederebbe se incontrasse versioni di prefetch per altri Windows. Quindi deve visualizzare un messaggio di errore come segue:
elif pf_version == 23:
print("[-] Windows Vista / 7 PF file {} -- unsupported".format(pf_name))
continue
elif pf_version == 26:
print("[-] Windows 8 PF file {} -- unsupported".format(pf_name))
continue
elif pf_version == 30:
print("[-] Windows 10 PF file {} -- unsupported".format(pf_name))
continue
else:
print("[-] Signature mismatch - Name: {}\nPath: {}".format(hit[0], hit[1]))
continue
write_output(prefetch_data, output_csv)
Ora, definisci il metodo per scrivere il risultato nel rapporto CSV come segue:
def write_output(data, output_csv):
print("[+] Writing csv report")
with open(output_csv, "wb") as outfile:
writer = csv.writer(outfile)
writer.writerow([
"File Name", "Prefetch Name", "File Size (bytes)",
"File Create Date (UTC)", "File Modify Date (UTC)",
"Prefetch Last Execution Date (UTC)",
"Prefetch Execution Count", "Volume", "Volume Create Date",
"Volume Serial", "File Path" ])
writer.writerows(data)
Dopo aver eseguito lo script precedente, otterremo le informazioni dai file di precaricamento della versione di Windows XP in un foglio di calcolo.
Questo capitolo illustrerà gli ulteriori artefatti che un investigatore può ottenere durante l'analisi forense su Windows.
Registri degli eventi
I file di registro degli eventi di Windows, come nome –suggerito, sono file speciali che memorizzano eventi significativi come quando l'utente accede al computer, quando il programma incontra un errore, su modifiche al sistema, accesso RDP, eventi specifici dell'applicazione ecc. Gli investigatori informatici sono sempre interessati all'evento informazioni di registro perché fornisce molte informazioni storiche utili sull'accesso al sistema. Nel seguente script Python elaboreremo sia i formati legacy che quelli attuali del registro eventi di Windows.
Per lo script Python, dobbiamo installare moduli di terze parti, vale a dire pytsk3, pyewf, unicodecsv, pyevt and pyevtX. Possiamo seguire i passaggi indicati di seguito per estrarre le informazioni dai registri degli eventi:
Innanzitutto, cerca tutti i registri eventi che corrispondono all'argomento di input.
Quindi, eseguire la verifica della firma del file.
Ora, elabora ogni registro eventi trovato con la libreria appropriata.
Infine, scrivi l'output sul foglio di calcolo.
Codice Python
Vediamo come utilizzare il codice Python per questo scopo -
Innanzitutto, importa le seguenti librerie Python:
from __future__ import print_function
import argparse
import unicodecsv as csv
import os
import pytsk3
import pyewf
import pyevt
import pyevtx
import sys
from utility.pytskutil import TSKUtil
Fornisci ora gli argomenti per il gestore della riga di comando. Nota che qui accetterà tre argomenti: il primo è il percorso del file di prova, il secondo è il tipo di file di prova e il terzo è il nome del registro degli eventi da elaborare.
if __name__ == "__main__":
parser = argparse.ArgumentParser('Information from Event Logs')
parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
parser.add_argument(
"LOG_NAME",help = "Event Log Name (SecEvent.Evt, SysEvent.Evt, ""etc.)")
parser.add_argument(
"-d", help = "Event log directory to scan",default = "/WINDOWS/SYSTEM32/WINEVT")
parser.add_argument(
"-f", help = "Enable fuzzy search for either evt or"" evtx extension", action = "store_true")
args = parser.parse_args()
if os.path.exists(args.EVIDENCE_FILE) and \ os.path.isfile(args.EVIDENCE_FILE):
main(args.EVIDENCE_FILE, args.TYPE, args.LOG_NAME, args.d, args.f)
else:
print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
sys.exit(1)
Ora, interagisci con i log degli eventi per interrogare l'esistenza del percorso fornito dall'utente creando il nostro file TSKUtiloggetto. Può essere fatto con l'aiuto dimain() metodo come segue -
def main(evidence, image_type, log, win_event, fuzzy):
tsk_util = TSKUtil(evidence, image_type)
event_dir = tsk_util.query_directory(win_event)
if event_dir is not None:
if fuzzy is True:
event_log = tsk_util.recurse_files(log, path=win_event)
else:
event_log = tsk_util.recurse_files(log, path=win_event, logic="equal")
if event_log is not None:
event_data = []
for hit in event_log:
event_file = hit[2]
temp_evt = write_file(event_file)
Ora, dobbiamo eseguire la verifica della firma seguita dalla definizione di un metodo che scriverà l'intero contenuto nella directory corrente -
def write_file(event_file):
with open(event_file.info.name.name, "w") as outfile:
outfile.write(event_file.read_random(0, event_file.info.meta.size))
return event_file.info.name.name
if pyevt.check_file_signature(temp_evt):
evt_log = pyevt.open(temp_evt)
print("[+] Identified {} records in {}".format(
evt_log.number_of_records, temp_evt))
for i, record in enumerate(evt_log.records):
strings = ""
for s in record.strings:
if s is not None:
strings += s + "\n"
event_data.append([
i, hit[0], record.computer_name,
record.user_security_identifier,
record.creation_time, record.written_time,
record.event_category, record.source_name,
record.event_identifier, record.event_type,
strings, "",
os.path.join(win_event, hit[1].lstrip("//"))
])
elif pyevtx.check_file_signature(temp_evt):
evtx_log = pyevtx.open(temp_evt)
print("[+] Identified {} records in {}".format(
evtx_log.number_of_records, temp_evt))
for i, record in enumerate(evtx_log.records):
strings = ""
for s in record.strings:
if s is not None:
strings += s + "\n"
event_data.append([
i, hit[0], record.computer_name,
record.user_security_identifier, "",
record.written_time, record.event_level,
record.source_name, record.event_identifier,
"", strings, record.xml_string,
os.path.join(win_event, hit[1].lstrip("//"))
])
else:
print("[-] {} not a valid event log. Removing temp" file...".format(temp_evt))
os.remove(temp_evt)
continue
write_output(event_data)
else:
print("[-] {} Event log not found in {} directory".format(log, win_event))
sys.exit(3)
else:
print("[-] Win XP Event Log Directory {} not found".format(win_event))
sys.exit(2
Infine, definire un metodo per scrivere l'output su un foglio di calcolo come segue:
def write_output(data):
output_name = "parsed_event_logs.csv"
print("[+] Writing {} to current working directory: {}".format(
output_name, os.getcwd()))
with open(output_name, "wb") as outfile:
writer = csv.writer(outfile)
writer.writerow([
"Index", "File name", "Computer Name", "SID",
"Event Create Date", "Event Written Date",
"Event Category/Level", "Event Source", "Event ID",
"Event Type", "Data", "XML Data", "File Path"
])
writer.writerows(data)
Una volta eseguito con successo lo script sopra, otterremo le informazioni sugli eventi nel foglio di calcolo.
Storia di Internet
La storia di Internet è molto utile per gli analisti forensi; poiché la maggior parte dei crimini informatici si verifica solo su Internet. Vediamo come estrarre la cronologia di Internet da Internet Explorer, mentre discutiamo di analisi forense di Windows e Internet Explorer viene fornito per impostazione predefinita con Windows.
In Internet Explorer, la cronologia di Internet viene salvata in index.datfile. Esaminiamo uno script Python, che estrarrà le informazioni daindex.dat file.
Possiamo seguire i passaggi indicati di seguito per estrarre le informazioni da index.dat file -
Innanzitutto, cerca index.dat file all'interno del sistema.
Quindi, estrai le informazioni da quel file scorrendole.
Ora scrivi tutte queste informazioni in un rapporto CSV.
Codice Python
Vediamo come utilizzare il codice Python per questo scopo -
Innanzitutto, importa le seguenti librerie Python:
from __future__ import print_function
import argparse
from datetime import datetime, timedelta
import os
import pytsk3
import pyewf
import pymsiecf
import sys
import unicodecsv as csv
from utility.pytskutil import TSKUtil
Fornisci ora gli argomenti per il gestore della riga di comando. Nota che qui accetterà due argomenti: il primo sarebbe il percorso del file delle prove e il secondo sarebbe il tipo di file delle prove -
if __name__ == "__main__":
parser = argparse.ArgumentParser('getting information from internet history')
parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
parser.add_argument("-d", help = "Index.dat directory to scan",default = "/USERS")
args = parser.parse_args()
if os.path.exists(args.EVIDENCE_FILE) and os.path.isfile(args.EVIDENCE_FILE):
main(args.EVIDENCE_FILE, args.TYPE, args.d)
else:
print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
sys.exit(1)
Ora, interpreta il file delle prove creando un oggetto di TSKUtile scorrere il file system per trovare i file index.dat. Può essere fatto definendo il filemain() funzionare come segue -
def main(evidence, image_type, path):
tsk_util = TSKUtil(evidence, image_type)
index_dir = tsk_util.query_directory(path)
if index_dir is not None:
index_files = tsk_util.recurse_files("index.dat", path = path,logic = "equal")
if index_files is not None:
print("[+] Identified {} potential index.dat files".format(len(index_files)))
index_data = []
for hit in index_files:
index_file = hit[2]
temp_index = write_file(index_file)
Ora, definisci una funzione con l'aiuto della quale possiamo copiare le informazioni del file index.dat nella directory di lavoro corrente e successivamente possono essere elaborate da un modulo di terze parti -
def write_file(index_file):
with open(index_file.info.name.name, "w") as outfile:
outfile.write(index_file.read_random(0, index_file.info.meta.size))
return index_file.info.name.name
Ora, usa il seguente codice per eseguire la convalida della firma con l'aiuto della funzione integrata, vale a dire check_file_signature() -
if pymsiecf.check_file_signature(temp_index):
index_dat = pymsiecf.open(temp_index)
print("[+] Identified {} records in {}".format(
index_dat.number_of_items, temp_index))
for i, record in enumerate(index_dat.items):
try:
data = record.data
if data is not None:
data = data.rstrip("\x00")
except AttributeError:
if isinstance(record, pymsiecf.redirected):
index_data.append([
i, temp_index, "", "", "", "", "",record.location, "", "", record.offset,os.path.join(path, hit[1].lstrip("//"))])
elif isinstance(record, pymsiecf.leak):
index_data.append([
i, temp_index, record.filename, "","", "", "", "", "", "", record.offset,os.path.join(path, hit[1].lstrip("//"))])
continue
index_data.append([
i, temp_index, record.filename,
record.type, record.primary_time,
record.secondary_time,
record.last_checked_time, record.location,
record.number_of_hits, data, record.offset,
os.path.join(path, hit[1].lstrip("//"))
])
else:
print("[-] {} not a valid index.dat file. Removing "
"temp file..".format(temp_index))
os.remove("index.dat")
continue
os.remove("index.dat")
write_output(index_data)
else:
print("[-] Index.dat files not found in {} directory".format(path))
sys.exit(3)
else:
print("[-] Directory {} not found".format(win_event))
sys.exit(2)
Ora, definisci un metodo che stamperà l'output nel file CSV, come mostrato di seguito -
def write_output(data):
output_name = "Internet_Indexdat_Summary_Report.csv"
print("[+] Writing {} with {} parsed index.dat files to current "
"working directory: {}".format(output_name, len(data),os.getcwd()))
with open(output_name, "wb") as outfile:
writer = csv.writer(outfile)
writer.writerow(["Index", "File Name", "Record Name",
"Record Type", "Primary Date", "Secondary Date",
"Last Checked Date", "Location", "No. of Hits",
"Record Data", "Record Offset", "File Path"])
writer.writerows(data)
Dopo aver eseguito lo script precedente, otterremo le informazioni dal file index.dat nel file CSV.
Copie shadow del volume
Una copia shadow è la tecnologia inclusa in Windows per eseguire manualmente o automaticamente copie di backup o istantanee dei file del computer. È anche chiamato servizio snapshot del volume o servizio shadow del volume (VSS).
Con l'aiuto di questi file VSS, gli esperti forensi possono avere alcune informazioni storiche su come il sistema è cambiato nel tempo e quali file esistevano sul computer. La tecnologia delle copie shadow richiede che il file system sia NTFS per la creazione e l'archiviazione delle copie shadow.
In questa sezione, vedremo uno script Python, che aiuta ad accedere a qualsiasi volume di copie shadow presenti nell'immagine forense.
Per lo script Python dobbiamo installare moduli di terze parti, vale a dire pytsk3, pyewf, unicodecsv, pyvshadow e vss. Possiamo seguire i passaggi indicati di seguito per estrarre le informazioni dai file VSS
Innanzitutto, accedi al volume dell'immagine grezza e identifica tutte le partizioni NTFS.
Quindi, estrai le informazioni da quelle copie shadow scorrendole.
Ora, finalmente, dobbiamo creare un elenco di file di dati all'interno delle istantanee.
Codice Python
Vediamo come utilizzare il codice Python per questo scopo -
Innanzitutto, importa le seguenti librerie Python:
from __future__ import print_function
import argparse
from datetime import datetime, timedelta
import os
import pytsk3
import pyewf
import pyvshadow
import sys
import unicodecsv as csv
from utility import vss
from utility.pytskutil import TSKUtil
from utility import pytskutil
Fornisci ora gli argomenti per il gestore della riga di comando. Qui accetterà due argomenti: il primo è il percorso del file di prova e il secondo è il file di output.
if __name__ == "__main__":
parser = argparse.ArgumentParser('Parsing Shadow Copies')
parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
parser.add_argument("OUTPUT_CSV", help = "Output CSV with VSS file listing")
args = parser.parse_args()
Ora convalida l'esistenza del percorso del file di input e separa anche la directory dal file di output.
directory = os.path.dirname(args.OUTPUT_CSV)
if not os.path.exists(directory) and directory != "":
os.makedirs(directory)
if os.path.exists(args.EVIDENCE_FILE) and \ os.path.isfile(args.EVIDENCE_FILE):
main(args.EVIDENCE_FILE, args.OUTPUT_CSV)
else:
print("[-] Supplied input file {} does not exist or is not a "
"file".format(args.EVIDENCE_FILE))
sys.exit(1)
Ora interagisci con il volume del file delle prove creando il file TSKUtiloggetto. Può essere fatto con l'aiuto dimain() metodo come segue -
def main(evidence, output):
tsk_util = TSKUtil(evidence, "raw")
img_vol = tsk_util.return_vol()
if img_vol is not None:
for part in img_vol:
if tsk_util.detect_ntfs(img_vol, part):
print("Exploring NTFS Partition for VSS")
explore_vss(evidence, part.start * img_vol.info.block_size,output)
else:
print("[-] Must be a physical preservation to be compatible ""with this script")
sys.exit(2)
Ora, definisci un metodo per esplorare il file shadow del volume analizzato come segue:
def explore_vss(evidence, part_offset, output):
vss_volume = pyvshadow.volume()
vss_handle = vss.VShadowVolume(evidence, part_offset)
vss_count = vss.GetVssStoreCount(evidence, part_offset)
if vss_count > 0:
vss_volume.open_file_object(vss_handle)
vss_data = []
for x in range(vss_count):
print("Gathering data for VSC {} of {}".format(x, vss_count))
vss_store = vss_volume.get_store(x)
image = vss.VShadowImgInfo(vss_store)
vss_data.append(pytskutil.openVSSFS(image, x))
write_csv(vss_data, output)
Infine, definire il metodo per scrivere il risultato nel foglio di calcolo come segue:
def write_csv(data, output):
if data == []:
print("[-] No output results to write")
sys.exit(3)
print("[+] Writing output to {}".format(output))
if os.path.exists(output):
append = True
with open(output, "ab") as csvfile:
csv_writer = csv.writer(csvfile)
headers = ["VSS", "File", "File Ext", "File Type", "Create Date",
"Modify Date", "Change Date", "Size", "File Path"]
if not append:
csv_writer.writerow(headers)
for result_list in data:
csv_writer.writerows(result_list)
Una volta eseguito con successo questo script Python, otterremo le informazioni che risiedono in VSS in un foglio di calcolo.
Fino ad ora, abbiamo visto come ottenere artefatti in Windows utilizzando Python. In questo capitolo, impariamo a studiare gli artefatti basati sui log usando Python.
introduzione
Gli artefatti basati sui registri sono un tesoro di informazioni che possono essere molto utili per un esperto di medicina legale digitale. Sebbene disponiamo di vari software di monitoraggio per la raccolta delle informazioni, il problema principale per analizzare le informazioni utili da essi è che abbiamo bisogno di molti dati.
Vari artefatti basati su log e indagini in Python
In questa sezione, discutiamo di vari artefatti basati su log e della loro indagine in Python -
Timestamp
Timestamp trasmette i dati e l'ora dell'attività nel registro. È uno degli elementi importanti di qualsiasi file di registro. Notare che questi valori di dati e di tempo possono essere disponibili in vari formati.
Lo script Python mostrato di seguito prenderà la data-ora non elaborata come input e fornisce un timestamp formattato come output.
Per questo script, dobbiamo seguire i seguenti passaggi:
Innanzitutto, imposta gli argomenti che prenderanno il valore dei dati non elaborati insieme alla fonte dei dati e al tipo di dati.
Fornisci ora una classe per fornire un'interfaccia comune per i dati in diversi formati di data.
Codice Python
Vediamo come utilizzare il codice Python per questo scopo -
Innanzitutto, importa i seguenti moduli Python:
from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from datetime import datetime as dt
from datetime import timedelta
Ora, come al solito, dobbiamo fornire un argomento per il gestore della riga di comando. Qui accetterà tre argomenti, il primo sarebbe il valore della data da elaborare, il secondo sarebbe l'origine di quel valore della data e il terzo sarebbe il suo tipo -
if __name__ == '__main__':
parser = ArgumentParser('Timestamp Log-based artifact')
parser.add_argument("date_value", help="Raw date value to parse")
parser.add_argument(
"source", help = "Source format of date",choices = ParseDate.get_supported_formats())
parser.add_argument(
"type", help = "Data type of input value",choices = ('number', 'hex'), default = 'int')
args = parser.parse_args()
date_parser = ParseDate(args.date_value, args.source, args.type)
date_parser.run()
print(date_parser.timestamp)
Ora, dobbiamo definire una classe che accetti gli argomenti per il valore della data, l'origine della data e il tipo di valore -
class ParseDate(object):
def __init__(self, date_value, source, data_type):
self.date_value = date_value
self.source = source
self.data_type = data_type
self.timestamp = None
Ora definiremo un metodo che agirà come un controller proprio come il metodo main () -
def run(self):
if self.source == 'unix-epoch':
self.parse_unix_epoch()
elif self.source == 'unix-epoch-ms':
self.parse_unix_epoch(True)
elif self.source == 'windows-filetime':
self.parse_windows_filetime()
@classmethod
def get_supported_formats(cls):
return ['unix-epoch', 'unix-epoch-ms', 'windows-filetime']
Ora, dobbiamo definire due metodi che elaboreranno rispettivamente l'ora dell'epoca di Unix e FILETIME -
def parse_unix_epoch(self, milliseconds=False):
if self.data_type == 'hex':
conv_value = int(self.date_value)
if milliseconds:
conv_value = conv_value / 1000.0
elif self.data_type == 'number':
conv_value = float(self.date_value)
if milliseconds:
conv_value = conv_value / 1000.0
else:
print("Unsupported data type '{}' provided".format(self.data_type))
sys.exit('1')
ts = dt.fromtimestamp(conv_value)
self.timestamp = ts.strftime('%Y-%m-%d %H:%M:%S.%f')
def parse_windows_filetime(self):
if self.data_type == 'hex':
microseconds = int(self.date_value, 16) / 10.0
elif self.data_type == 'number':
microseconds = float(self.date_value) / 10
else:
print("Unsupported data type '{}' provided".format(self.data_type))
sys.exit('1')
ts = dt(1601, 1, 1) + timedelta(microseconds=microseconds)
self.timestamp = ts.strftime('%Y-%m-%d %H:%M:%S.%f')
Dopo aver eseguito lo script precedente, fornendo un timestamp possiamo ottenere il valore convertito in un formato di facile lettura.
Registri del server Web
Dal punto di vista dell'esperto di digital forensic, i log del server web sono un altro importante artefatto perché possono ottenere statistiche utili sugli utenti insieme a informazioni sull'utente e sulle posizioni geografiche. Di seguito è riportato lo script Python che creerà un foglio di calcolo, dopo aver elaborato i log del server web, per una facile analisi delle informazioni.
Prima di tutto dobbiamo importare i seguenti moduli Python:
from __future__ import print_function
from argparse import ArgumentParser, FileType
import re
import shlex
import logging
import sys
import csv
logger = logging.getLogger(__file__)
Ora, dobbiamo definire i modelli che verranno analizzati dai log -
iis_log_format = [
("date", re.compile(r"\d{4}-\d{2}-\d{2}")),
("time", re.compile(r"\d\d:\d\d:\d\d")),
("s-ip", re.compile(
r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}")),
("cs-method", re.compile(
r"(GET)|(POST)|(PUT)|(DELETE)|(OPTIONS)|(HEAD)|(CONNECT)")),
("cs-uri-stem", re.compile(r"([A-Za-z0-1/\.-]*)")),
("cs-uri-query", re.compile(r"([A-Za-z0-1/\.-]*)")),
("s-port", re.compile(r"\d*")),
("cs-username", re.compile(r"([A-Za-z0-1/\.-]*)")),
("c-ip", re.compile(
r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}")),
("cs(User-Agent)", re.compile(r".*")),
("sc-status", re.compile(r"\d*")),
("sc-substatus", re.compile(r"\d*")),
("sc-win32-status", re.compile(r"\d*")),
("time-taken", re.compile(r"\d*"))]
Fornisci ora un argomento per il gestore della riga di comando. Qui accetterà due argomenti, il primo sarebbe il registro IIS da elaborare, il secondo sarebbe il percorso del file CSV desiderato.
if __name__ == '__main__':
parser = ArgumentParser('Parsing Server Based Logs')
parser.add_argument('iis_log', help = "Path to IIS Log",type = FileType('r'))
parser.add_argument('csv_report', help = "Path to CSV report")
parser.add_argument('-l', help = "Path to processing log",default=__name__ + '.log')
args = parser.parse_args()
logger.setLevel(logging.DEBUG)
msg_fmt = logging.Formatter(
"%(asctime)-15s %(funcName)-10s ""%(levelname)-8s %(message)s")
strhndl = logging.StreamHandler(sys.stdout)
strhndl.setFormatter(fmt = msg_fmt)
fhndl = logging.FileHandler(args.log, mode = 'a')
fhndl.setFormatter(fmt = msg_fmt)
logger.addHandler(strhndl)
logger.addHandler(fhndl)
logger.info("Starting IIS Parsing ")
logger.debug("Supplied arguments: {}".format(", ".join(sys.argv[1:])))
logger.debug("System " + sys.platform)
logger.debug("Version " + sys.version)
main(args.iis_log, args.csv_report, logger)
iologger.info("IIS Parsing Complete")
Ora dobbiamo definire il metodo main () che gestirà lo script per le informazioni di registro in blocco -
def main(iis_log, report_file, logger):
parsed_logs = []
for raw_line in iis_log:
line = raw_line.strip()
log_entry = {}
if line.startswith("#") or len(line) == 0:
continue
if '\"' in line:
line_iter = shlex.shlex(line_iter)
else:
line_iter = line.split(" ")
for count, split_entry in enumerate(line_iter):
col_name, col_pattern = iis_log_format[count]
if col_pattern.match(split_entry):
log_entry[col_name] = split_entry
else:
logger.error("Unknown column pattern discovered. "
"Line preserved in full below")
logger.error("Unparsed Line: {}".format(line))
parsed_logs.append(log_entry)
logger.info("Parsed {} lines".format(len(parsed_logs)))
cols = [x[0] for x in iis_log_format]
logger.info("Creating report file: {}".format(report_file))
write_csv(report_file, cols, parsed_logs)
logger.info("Report created")
Infine, dobbiamo definire un metodo che scriverà l'output nel foglio di calcolo -
def write_csv(outfile, fieldnames, data):
with open(outfile, 'w', newline="") as open_outfile:
csvfile = csv.DictWriter(open_outfile, fieldnames)
csvfile.writeheader()
csvfile.writerows(data)
Dopo aver eseguito lo script precedente, avremo i log basati sul server web in un foglio di calcolo.
Scansione di file importanti utilizzando YARA
YARA (Yet Another Recursive Algorithm) è un'utilità di pattern matching progettata per l'identificazione del malware e la risposta agli incidenti. Useremo YARA per la scansione dei file. Nel seguente script Python, useremo YARA.
Possiamo installare YARA con l'aiuto del seguente comando:
pip install YARA
Possiamo seguire i passaggi indicati di seguito per l'utilizzo delle regole YARA per la scansione dei file:
Innanzitutto, imposta e compila le regole YARA
Quindi, eseguire la scansione di un singolo file e quindi scorrere le directory per elaborare i singoli file.
Infine, esporteremo il risultato in CSV.
Codice Python
Vediamo come utilizzare il codice Python per questo scopo -
Innanzitutto, dobbiamo importare i seguenti moduli Python:
from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
import os
import csv
import yara
Successivamente, fornire un argomento per il gestore della riga di comando. Nota che qui accetterà due argomenti: il primo è il percorso delle regole YARA, il secondo è il file da scansionare.
if __name__ == '__main__':
parser = ArgumentParser('Scanning files by YARA')
parser.add_argument(
'yara_rules',help = "Path to Yara rule to scan with. May be file or folder path.")
parser.add_argument('path_to_scan',help = "Path to file or folder to scan")
parser.add_argument('--output',help = "Path to output a CSV report of scan results")
args = parser.parse_args()
main(args.yara_rules, args.path_to_scan, args.output)
Ora definiremo la funzione main () che accetterà il percorso delle regole yara e del file da scansionare -
def main(yara_rules, path_to_scan, output):
if os.path.isdir(yara_rules):
yrules = yara.compile(yara_rules)
else:
yrules = yara.compile(filepath=yara_rules)
if os.path.isdir(path_to_scan):
match_info = process_directory(yrules, path_to_scan)
else:
match_info = process_file(yrules, path_to_scan)
columns = ['rule_name', 'hit_value', 'hit_offset', 'file_name',
'rule_string', 'rule_tag']
if output is None:
write_stdout(columns, match_info)
else:
write_csv(output, columns, match_info)
Ora, definisci un metodo che itererà attraverso la directory e passerà il risultato a un altro metodo per un'ulteriore elaborazione -
def process_directory(yrules, folder_path):
match_info = []
for root, _, files in os.walk(folder_path):
for entry in files:
file_entry = os.path.join(root, entry)
match_info += process_file(yrules, file_entry)
return match_info
Quindi, definisci due funzioni. Nota che prima useremomatch() metodo a yrulesoggetto e un altro riporterà le informazioni sulla corrispondenza alla console se l'utente non specifica alcun file di output. Rispettare il codice mostrato di seguito -
def process_file(yrules, file_path):
match = yrules.match(file_path)
match_info = []
for rule_set in match:
for hit in rule_set.strings:
match_info.append({
'file_name': file_path,
'rule_name': rule_set.rule,
'rule_tag': ",".join(rule_set.tags),
'hit_offset': hit[0],
'rule_string': hit[1],
'hit_value': hit[2]
})
return match_info
def write_stdout(columns, match_info):
for entry in match_info:
for col in columns:
print("{}: {}".format(col, entry[col]))
print("=" * 30)
Infine, definiremo un metodo che scriverà l'output nel file CSV, come mostrato di seguito -
def write_csv(outfile, fieldnames, data):
with open(outfile, 'w', newline="") as open_outfile:
csvfile = csv.DictWriter(open_outfile, fieldnames)
csvfile.writeheader()
csvfile.writerows(data)
Una volta eseguito con successo lo script precedente, possiamo fornire gli argomenti appropriati sulla riga di comando e possiamo generare un rapporto CSV.