Untersuchung mit E-Mails
In den vorherigen Kapiteln wurden die Bedeutung und der Prozess der Netzwerkforensik sowie die damit verbundenen Konzepte erörtert. In diesem Kapitel lernen wir die Rolle von E-Mails in der digitalen Forensik und ihre Untersuchung mit Python kennen.
Rolle der E-Mail bei der Untersuchung
E-Mails spielen eine sehr wichtige Rolle in der Geschäftskommunikation und haben sich zu einer der wichtigsten Anwendungen im Internet entwickelt. Sie sind ein praktischer Modus zum Senden von Nachrichten und Dokumenten, nicht nur von Computern, sondern auch von anderen elektronischen Geräten wie Mobiltelefonen und Tablets.
Die negative Seite von E-Mails ist, dass Kriminelle wichtige Informationen über ihr Unternehmen verlieren können. Daher hat die Rolle von E-Mails in der digitalen Forensik in den letzten Jahren zugenommen. In der digitalen Forensik werden E-Mails als entscheidende Beweise angesehen, und die E-Mail-Header-Analyse ist wichtig geworden, um Beweise während des forensischen Prozesses zu sammeln.
Ein Ermittler hat die folgenden Ziele bei der Durchführung der E-Mail-Forensik:
- Den Hauptverbrecher identifizieren
- Notwendige Beweise sammeln
- Zur Präsentation der Ergebnisse
- Um den Fall zu bauen
Herausforderungen in der E-Mail-Forensik
Die E-Mail-Forensik spielt eine sehr wichtige Rolle bei der Untersuchung, da der Großteil der Kommunikation in der heutigen Zeit auf E-Mails beruht. Ein forensischer E-Mail-Ermittler kann sich jedoch während der Untersuchung den folgenden Herausforderungen stellen:
Gefälschte E-Mails
Die größte Herausforderung in der E-Mail-Forensik ist die Verwendung gefälschter E-Mails, die durch Manipulieren und Erstellen von Skripten für Header usw. erstellt werden. In dieser Kategorie verwenden Kriminelle auch temporäre E-Mails, bei denen ein registrierter Benutzer E-Mails an einer temporären Adresse empfangen kann, die abläuft nach einer bestimmten Zeit.
Spoofing
Eine weitere Herausforderung in der E-Mail-Forensik ist das Spoofing, bei dem Kriminelle eine E-Mail als die einer anderen Person präsentierten. In diesem Fall erhält das Gerät sowohl eine gefälschte als auch eine ursprüngliche IP-Adresse.
Anonymes erneutes E-Mailen
Hier entfernt der E-Mail-Server identifizierende Informationen aus der E-Mail-Nachricht, bevor er sie weiterleitet. Dies führt zu einer weiteren großen Herausforderung für E-Mail-Untersuchungen.
In der forensischen E-Mail-Untersuchung verwendete Techniken
E-Mail-Forensik ist die Untersuchung der Quelle und des Inhalts von E-Mails als Beweismittel zur Identifizierung des tatsächlichen Absenders und Empfängers einer Nachricht sowie einiger anderer Informationen wie Datum / Uhrzeit der Übermittlung und Absicht des Absenders. Es umfasst die Untersuchung von Metadaten, das Scannen von Ports sowie die Suche nach Schlüsselwörtern.
Einige der gängigen Techniken, die für forensische E-Mail-Untersuchungen verwendet werden können, sind:
- Header-Analyse
- Serveruntersuchung
- Untersuchung von Netzwerkgeräten
- Absender Mailer Fingerabdrücke
- Software Embedded Identifiers
In den folgenden Abschnitten erfahren Sie, wie Sie Informationen mit Python zum Zwecke der E-Mail-Untersuchung abrufen.
Extraktion von Informationen aus EML-Dateien
EML-Dateien sind im Grunde E-Mails im Dateiformat, die häufig zum Speichern von E-Mail-Nachrichten verwendet werden. Es handelt sich um strukturierte Textdateien, die mit mehreren E-Mail-Clients wie Microsoft Outlook, Outlook Express und Windows Live Mail kompatibel sind.
In einer EML-Datei werden E-Mail-Header, Textinhalte und Anhangsdaten als einfacher Text gespeichert. Es verwendet base64 zum Codieren von Binärdaten und QP-Codierung (Quoted-Printable) zum Speichern von Inhaltsinformationen. Das Python-Skript, mit dem Informationen aus der EML-Datei extrahiert werden können, ist unten angegeben:
Importieren Sie zunächst die folgenden Python-Bibliotheken wie unten gezeigt:
from __future__ import print_function
from argparse import ArgumentParser, FileType
from email import message_from_file
import os
import quopri
import base64
In den oben genannten Bibliotheken quopriwird verwendet, um die QP-codierten Werte aus EML-Dateien zu dekodieren. Alle Base64-codierten Daten können mit Hilfe von decodiert werdenbase64 Bibliothek.
Als nächstes geben wir ein Argument für den Befehlszeilenhandler an. Beachten Sie, dass hier nur ein Argument akzeptiert wird, das der Pfad zur EML-Datei ist (siehe unten).
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)
Jetzt müssen wir definieren main() Funktion, in der wir die genannte Methode verwenden message_from_file()aus der E-Mail-Bibliothek, um das dateiähnliche Objekt zu lesen. Hier greifen wir mithilfe der resultierenden Variablen mit dem Namen auf die Header, den Body-Inhalt, die Anhänge und andere Nutzdateninformationen zuemlfile wie im unten angegebenen Code gezeigt -
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])
Jetzt müssen wir definieren process_payload() Methode, mit der wir den Inhalt des Nachrichtentexts mithilfe von extrahieren get_payload()Methode. Wir werden QP-codierte Daten mit dekodierenquopri.decodestring()Funktion. Wir werden auch den MIME-Inhaltstyp überprüfen, damit er die Speicherung der E-Mail ordnungsgemäß handhaben kann. Beachten Sie den unten angegebenen Code -
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)
Nach dem Ausführen des obigen Skripts erhalten wir die Header-Informationen zusammen mit verschiedenen Nutzdaten auf der Konsole.
Analysieren von MSG-Dateien mit Python
E-Mail-Nachrichten gibt es in vielen verschiedenen Formaten. MSG ist ein solches Format, das von Microsoft Outlook und Exchange verwendet wird. Dateien mit der Erweiterung MSG können einfachen ASCII-Text für die Header und den Hauptnachrichtentext sowie Hyperlinks und Anhänge enthalten.
In diesem Abschnitt erfahren Sie, wie Sie mithilfe der Outlook-API Informationen aus MSG-Dateien extrahieren. Beachten Sie, dass das folgende Python-Skript nur unter Windows funktioniert. Dazu müssen wir die Python-Bibliothek eines Drittanbieters mit dem Namen installierenpywin32 wie folgt -
pip install pywin32
Importieren Sie nun die folgenden Bibliotheken mit den angezeigten Befehlen:
from __future__ import print_function
from argparse import ArgumentParser
import os
import win32com.client
import pywintypes
Lassen Sie uns nun ein Argument für den Befehlszeilenhandler angeben. Hier werden zwei Argumente akzeptiert, eines wäre der Pfad zur MSG-Datei und das andere wäre der gewünschte Ausgabeordner wie folgt:
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)
Jetzt müssen wir definieren main() Funktion, in der wir aufrufen werden win32com Bibliothek zum Einrichten Outlook API Dies ermöglicht den Zugriff auf die MAPI Namespace.
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)
Definieren Sie nun verschiedene Funktionen, die wir in diesem Skript verwenden. Der unten angegebene Code zeigt die Definition derdisplay_msg_attribs() Funktion, mit der wir verschiedene Attribute einer Nachricht anzeigen können, z. B. Betreff, BCC, CC, Größe, Absendername, gesendet usw.
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')))
Definieren Sie nun die display_msg_recipeints() Funktion, die die Nachrichten durchläuft und die Empfängerdetails anzeigt.
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
Als nächstes definieren wir extract_msg_body() Funktion, die den Textinhalt, HTML sowie einfachen Text, aus der Nachricht extrahiert.
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"))
Als nächstes werden wir die definieren extract_attachments() Funktion, die Anhangsdaten in das gewünschte Ausgabeverzeichnis exportiert.
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
Sobald alle Funktionen definiert sind, drucken wir alle Attribute mit der folgenden Codezeile auf die Konsole:
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
Nach dem Ausführen des obigen Skripts erhalten wir die Attribute der Nachricht und ihrer Anhänge im Konsolenfenster zusammen mit mehreren Dateien im Ausgabeverzeichnis.
Strukturieren von MBOX-Dateien aus Google Takeout mit Python
MBOX-Dateien sind Textdateien mit spezieller Formatierung, die darin gespeicherte Nachrichten aufteilen. Sie werden häufig in Verbindung mit UNIX-Systemen, Thunderbolt und Google Takeouts gefunden.
In diesem Abschnitt sehen Sie ein Python-Skript, in dem wir MBOX-Dateien strukturieren, die von Google Takeouts bezogen wurden. Vorher müssen wir jedoch wissen, wie wir diese MBOX-Dateien mithilfe unseres Google- oder Google Mail-Kontos generieren können.
Erfassen des Google-Konto-Postfachs im MBX-Format
Wenn Sie das Postfach eines Google-Kontos erwerben, müssen Sie eine Sicherungskopie unseres Google Mail-Kontos erstellen. Das Backup kann aus verschiedenen persönlichen oder beruflichen Gründen erstellt werden. Beachten Sie, dass Google die Sicherung von Google Mail-Daten bereitstellt. Um unser Google-Konto-Postfach im MBOX-Format zu erhalten, müssen Sie die folgenden Schritte ausführen:
Öffnen My account Instrumententafel.
Gehen Sie zum Abschnitt "Persönliche Daten und Datenschutz" und wählen Sie den Link "Inhalt kontrollieren".
Sie können ein neues Archiv erstellen oder ein vorhandenes verwalten. Wenn wir klicken,CREATE ARCHIVE Link, dann erhalten wir einige Kontrollkästchen für jedes Google-Produkt, das wir einschließen möchten.
Nach Auswahl der Produkte haben wir die Freiheit, den Dateityp und die maximale Größe für unser Archiv zusammen mit der Versandmethode zur Auswahl aus der Liste auszuwählen.
Schließlich erhalten wir dieses Backup im MBOX-Format.
Python-Code
Jetzt kann die oben beschriebene MBOX-Datei mit Python wie unten gezeigt strukturiert werden -
Zunächst müssen Python-Bibliotheken wie folgt importiert werden:
from __future__ import print_function
from argparse import ArgumentParser
import mailbox
import os
import time
import csv
from tqdm import tqdm
import base64
Alle Bibliotheken wurden in früheren Skripten verwendet und erklärt, mit Ausnahme der mailbox Bibliothek, die zum Parsen von MBOX-Dateien verwendet wird.
Geben Sie nun ein Argument für den Befehlszeilenhandler an. Hier werden zwei Argumente akzeptiert - eines ist der Pfad zur MBOX-Datei und das andere ist der gewünschte Ausgabeordner.
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)
Nun wird definiert main() Funktion und Aufruf mbox Klasse der Postfachbibliothek, mit deren Hilfe wir eine MBOX-Datei analysieren können, indem wir ihren Pfad angeben -
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)))
Definieren Sie nun eine Lesemethode für mailbox Bibliothek wie folgt -
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)
Erstellen Sie nun einige Variablen für die weitere Verarbeitung wie folgt:
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"]
Als nächstes verwenden tqdm um einen Fortschrittsbalken zu generieren und den Iterationsprozess wie folgt zu verfolgen:
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")
Überprüfen Sie jetzt, ob die Wettermeldung Nutzdaten enthält oder nicht. Wenn ja, werden wir definierenwrite_payload() Methode wie folgt -
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)
Jetzt müssen Daten angehängt werden. Dann werden wir anrufencreate_report() Methode wie folgt -
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
Beachten Sie, dass die obigen if-else-Anweisungen leicht zu verstehen sind. Jetzt müssen wir eine Methode definieren, die den Dateinamen aus dem extrahiertmsg Objekt wie folgt -
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)
Jetzt können Sie mithilfe der folgenden Codezeilen die Datei tatsächlich exportieren -
if isinstance(content_data, str):
open(file_name, 'w').write(content_data)
else:
open(file_name, 'wb').write(content_data)
return file_name
Definieren wir nun eine Funktion zum Extrahieren von Dateinamen aus dem message um die Namen dieser Dateien wie folgt genau darzustellen -
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)
Jetzt können wir eine CSV-Datei schreiben, indem wir die definieren create_report() Funktion wie folgt -
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)
Sobald Sie das oben angegebene Skript ausführen, erhalten wir den CSV-Bericht und das Verzeichnis voller Anhänge.