Enquête à l'aide d'e-mails

Les chapitres précédents ont discuté de l'importance et du processus de la criminalistique de réseau et des concepts impliqués. Dans ce chapitre, apprenons le rôle des e-mails dans la criminalistique numérique et leur investigation à l'aide de Python.

Rôle du courrier électronique dans les enquêtes

Les e-mails jouent un rôle très important dans les communications d'entreprise et sont devenus l'une des applications les plus importantes sur Internet. Ils constituent un mode pratique pour envoyer des messages ainsi que des documents, non seulement à partir d'ordinateurs, mais également à partir d'autres gadgets électroniques tels que les téléphones portables et les tablettes.

Le côté négatif des e-mails est que les criminels peuvent divulguer des informations importantes sur leur entreprise. Par conséquent, le rôle des e-mails dans la criminalistique numérique a augmenté ces dernières années. En criminalistique numérique, les e-mails sont considérés comme des preuves cruciales et l'analyse des en-têtes d'e-mails est devenue importante pour collecter des preuves pendant le processus médico-légal.

Un enquêteur a les objectifs suivants tout en effectuant des analyses de courrier électronique -

  • Identifier le principal criminel
  • Pour recueillir les preuves nécessaires
  • Présenter les résultats
  • Pour construire le boîtier

Défis en criminalistique des e-mails

La criminalistique des e-mails joue un rôle très important dans les enquêtes car la plupart des communications à l'ère actuelle reposent sur les e-mails. Cependant, un enquêteur judiciaire par courrier électronique peut être confronté aux défis suivants au cours de l'enquête:

Faux e-mails

Le plus grand défi de l'investigation des e-mails est l'utilisation de faux e-mails créés en manipulant et en scriptant des en-têtes, etc. après une certaine période.

Usurpation d'identité

Un autre défi de la criminalistique des e-mails est l'usurpation d'identité dans laquelle les criminels présentaient un e-mail comme celui de quelqu'un d'autre. Dans ce cas, la machine recevra à la fois une fausse adresse IP et une adresse IP d'origine.

Re-emailing anonyme

Ici, le serveur de messagerie supprime les informations d'identification du message électronique avant de le transférer davantage. Cela conduit à un autre grand défi pour les enquêtes sur les e-mails.

Techniques utilisées dans les enquêtes judiciaires par courrier électronique

La criminalistique des e-mails est l'étude de la source et du contenu d'un e-mail en tant que preuve permettant d'identifier l'expéditeur et le destinataire réels d'un message ainsi que d'autres informations telles que la date / heure de transmission et l'intention de l'expéditeur. Il s'agit d'enquêter sur les métadonnées, l'analyse des ports ainsi que la recherche par mot-clé.

Certaines des techniques courantes qui peuvent être utilisées pour les enquêtes judiciaires sur les e-mails sont

  • Analyse des en-têtes
  • Investigation du serveur
  • Enquête sur les périphériques réseau
  • Empreintes digitales de l'expéditeur
  • Identifiants intégrés au logiciel

Dans les sections suivantes, nous allons apprendre à récupérer des informations à l'aide de Python à des fins d'enquête sur les e-mails.

Extraction d'informations à partir de fichiers EML

Les fichiers EML sont essentiellement des e-mails au format de fichier largement utilisés pour stocker les e-mails. Ce sont des fichiers texte structurés compatibles avec plusieurs clients de messagerie tels que Microsoft Outlook, Outlook Express et Windows Live Mail.

Un fichier EML stocke les en-têtes d'e-mail, le contenu du corps et les données des pièces jointes sous forme de texte brut. Il utilise base64 pour encoder les données binaires et l'encodage Quoted-Printable (QP) pour stocker les informations de contenu. Le script Python qui peut être utilisé pour extraire des informations du fichier EML est donné ci-dessous -

Tout d'abord, importez les bibliothèques Python suivantes comme indiqué ci-dessous -

from __future__ import print_function
from argparse import ArgumentParser, FileType
from email import message_from_file

import os
import quopri
import base64

Dans les bibliothèques ci-dessus, quopriest utilisé pour décoder les valeurs encodées QP à partir de fichiers EML. Toutes les données encodées en base64 peuvent être décodées à l'aide debase64 bibliothèque.

Ensuite, fournissons un argument pour le gestionnaire de ligne de commande. Notez qu'ici, il n'acceptera qu'un seul argument qui serait le chemin du fichier EML comme indiqué ci-dessous -

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)

Maintenant, nous devons définir main() fonction dans laquelle nous utiliserons la méthode nommée message_from_file()à partir de la bibliothèque de messagerie pour lire le fichier comme un objet. Ici, nous allons accéder aux en-têtes, au contenu du corps, aux pièces jointes et à d'autres informations sur la charge utile en utilisant la variable résultante nomméeemlfile comme indiqué dans le code ci-dessous -

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])

Maintenant, nous devons définir process_payload() méthode dans laquelle nous allons extraire le contenu du corps du message en utilisant get_payload()méthode. Nous décoderons les données encodées QP en utilisantquopri.decodestring()fonction. Nous vérifierons également le type de contenu MIME afin qu'il puisse gérer correctement le stockage du courrier électronique. Observez le code ci-dessous -

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)

Après avoir exécuté le script ci-dessus, nous obtiendrons les informations d'en-tête ainsi que diverses charges utiles sur la console.

Analyse des fichiers MSG à l'aide de Python

Les messages électroniques sont disponibles dans de nombreux formats différents. MSG est l'un de ces types de format utilisé par Microsoft Outlook et Exchange. Les fichiers avec l'extension MSG peuvent contenir du texte ASCII brut pour les en-têtes et le corps du message principal ainsi que des hyperliens et des pièces jointes.

Dans cette section, nous allons apprendre comment extraire des informations d'un fichier MSG à l'aide de l'API Outlook. Notez que le script Python suivant ne fonctionnera que sous Windows. Pour cela, nous devons installer une bibliothèque Python tierce nomméepywin32 comme suit -

pip install pywin32

Maintenant, importez les bibliothèques suivantes en utilisant les commandes affichées -

from __future__ import print_function
from argparse import ArgumentParser

import os
import win32com.client
import pywintypes

Maintenant, fournissons un argument pour le gestionnaire de ligne de commande. Ici, il acceptera deux arguments l'un serait le chemin d'accès au fichier MSG et l'autre serait le dossier de sortie souhaité comme suit -

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)

Maintenant, nous devons définir main() fonction dans laquelle nous appellerons win32com bibliothèque pour la mise en place Outlook API qui permet en outre d'accéder à la MAPI espace de noms.

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)

Maintenant, définissez différentes fonctions que nous utilisons dans ce script. Le code ci-dessous montre la définition dudisplay_msg_attribs() fonction qui nous permet d'afficher divers attributs d'un message comme sujet, à, BCC, CC, taille, SenderName, envoyé, etc.

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')))

Maintenant, définissez le display_msg_recipeints() fonction qui parcourt les messages et affiche les détails du destinataire.

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

Ensuite, nous définissons extract_msg_body() fonction qui extrait le contenu du corps, HTML et texte brut, du message.

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"))

Ensuite, nous définirons le extract_attachments() fonction qui exporte les données des pièces jointes dans le répertoire de sortie souhaité.

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

Une fois toutes les fonctions définies, nous imprimerons tous les attributs sur la console avec la ligne de codes suivante -

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

Après avoir exécuté le script ci-dessus, nous obtiendrons les attributs du message et ses pièces jointes dans la fenêtre de la console ainsi que plusieurs fichiers dans le répertoire de sortie.

Structurer des fichiers MBOX à partir de Google Takeout à l'aide de Python

Les fichiers MBOX sont des fichiers texte avec un formatage spécial qui divise les messages stockés à l'intérieur. Ils sont souvent associés aux systèmes UNIX, Thunderbolt et Google Takeouts.

Dans cette section, vous verrez un script Python, où nous allons structurer les fichiers MBOX obtenus à partir de Google Takeouts. Mais avant cela, nous devons savoir comment générer ces fichiers MBOX en utilisant notre compte Google ou notre compte Gmail.

Acquérir la boîte aux lettres du compte Google au format MBX

L'acquisition de la boîte aux lettres du compte Google implique la sauvegarde de notre compte Gmail. La sauvegarde peut être effectuée pour diverses raisons personnelles ou professionnelles. Notez que Google assure la sauvegarde des données Gmail. Pour acquérir notre boîte aux lettres de compte Google au format MBOX, vous devez suivre les étapes ci-dessous -

  • Ouvert My account tableau de bord.

  • Accédez à la section Informations personnelles et confidentialité et sélectionnez Contrôler votre lien de contenu.

  • Vous pouvez créer une nouvelle archive ou gérer une archive existante. Si nous cliquons,CREATE ARCHIVE lien, puis nous obtiendrons des cases à cocher pour chaque produit Google que nous souhaitons inclure.

  • Après avoir sélectionné les produits, nous aurons la liberté de choisir le type de fichier et la taille maximale de nos archives, ainsi que la méthode de livraison à sélectionner dans la liste.

  • Enfin, nous obtiendrons cette sauvegarde au format MBOX.

Code Python

Maintenant, le fichier MBOX discuté ci-dessus peut être structuré à l'aide de Python comme indiqué ci-dessous -

Tout d'abord, vous devez importer les bibliothèques Python comme suit -

from __future__ import print_function
from argparse import ArgumentParser

import mailbox
import os
import time
import csv
from tqdm import tqdm

import base64

Toutes les bibliothèques ont été utilisées et expliquées dans les scripts précédents, à l'exception du mailbox bibliothèque qui est utilisée pour analyser les fichiers MBOX.

Maintenant, fournissez un argument pour le gestionnaire de ligne de commande. Ici, il acceptera deux arguments - l'un serait le chemin du fichier MBOX, et l'autre serait le dossier de sortie souhaité.

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)

Maintenant, va définir main() fonction et appel mbox classe de bibliothèque de boîtes aux lettres à l'aide de laquelle nous pouvons analyser un fichier MBOX en fournissant son chemin -

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)))

Maintenant, définissez une méthode de lecture pour mailbox bibliothèque comme suit -

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)

Maintenant, créez des variables pour un traitement ultérieur comme suit -

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"]

Ensuite, utilisez tqdm pour générer une barre de progression et suivre le processus d'itération comme suit -

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")

Maintenant, vérifiez que le message météo a des charges utiles ou non. Si c'est le cas, nous définironswrite_payload() méthode comme suit -

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)

Maintenant, les données doivent être ajoutées. Ensuite, nous appelleronscreate_report() méthode comme suit -

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

Observez que les instructions if-else ci-dessus sont faciles à comprendre. Maintenant, nous devons définir une méthode qui extraira le nom de fichier dumsg objet comme suit -

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)

Maintenant, à l'aide des lignes de code suivantes, vous pouvez réellement exporter le fichier -

if isinstance(content_data, str):
   open(file_name, 'w').write(content_data)
else:
   open(file_name, 'wb').write(content_data)
return file_name

Maintenant, définissons une fonction pour extraire les noms de fichiers du message pour représenter avec précision les noms de ces fichiers comme suit -

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)

Maintenant, nous pouvons écrire un fichier CSV en définissant le create_report() fonction comme suit -

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)

Une fois que vous exécutez le script ci-dessus, nous obtiendrons le rapport CSV et le répertoire plein de pièces jointes.