Indagine utilizzando le e-mail

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 scrivendo 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 l'indirizzo IP falso che quello 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 l'archiviazione 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. Di seguito viene fornito lo script Python che può essere utilizzato per estrarre le informazioni dal file EML:

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 carico utile 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 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 o meno carichi utili. 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.