Учебники

Расследование по электронной почте

В предыдущих главах обсуждалась важность и процесс судебной экспертизы сети, а также рассматриваемые концепции. В этой главе мы узнаем о роли электронных писем в цифровой криминалистике и их расследовании с использованием Python.

Роль электронной почты в расследовании

Электронные письма играют очень важную роль в деловом общении и стали одним из самых важных приложений в Интернете. Это удобный режим для отправки сообщений и документов не только с компьютеров, но и с других электронных гаджетов, таких как мобильные телефоны и планшеты.

Негативной стороной электронных писем является то, что преступники могут утекать важную информацию о своей компании. Следовательно, роль электронных писем в цифровой криминалистике в последние годы возросла. В цифровой криминалистике электронные письма считаются важными доказательствами, и анализ заголовков электронной почты стал важным для сбора доказательств в ходе судебной экспертизы.

Следователь преследует следующие цели при проведении экспертизы электронной почты:

  • Определить основного преступника
  • Собрать необходимые доказательства
  • Чтобы представить выводы
  • Построить корпус

Проблемы в электронной почте Forensics

Экспертиза электронной почты играет очень важную роль в расследовании, так как большая часть сообщений в современную эпоху основана на электронной почте. Тем не менее, судебный следователь по электронной почте может столкнуться со следующими проблемами в ходе расследования:

Поддельные электронные письма

Самая большая проблема в судебной экспертизе электронной почты — это использование поддельных электронных писем, которые создаются с помощью манипулирования и создания сценариев заголовков и т. Д. В этой категории преступники также используют временную электронную почту, которая является услугой, которая позволяет зарегистрированному пользователю получать электронную почту по временному адресу, срок которого истекает после определенного периода времени.

Подделка

Еще одна проблема в судебной экспертизе электронной почты заключается в подделке, в которой преступники использовали электронную почту как чужую. В этом случае аппарат получит как поддельный, так и оригинальный IP-адрес.

Анонимная переписка по электронной почте

Здесь сервер электронной почты удаляет идентифицирующую информацию из сообщения электронной почты перед дальнейшей пересылкой. Это приводит к еще одной большой проблеме для расследований по электронной почте.

Методы, используемые в электронной судебной экспертизе

Экспертиза электронной почты — это исследование источника и содержания электронной почты в качестве доказательства для идентификации фактического отправителя и получателя сообщения, а также некоторой другой информации, такой как дата / время передачи и намерение отправителя. Он включает в себя исследование метаданных, сканирование портов, а также поиск по ключевым словам.

Некоторые из общих методов, которые могут использоваться для судебного расследования по электронной почте:

  • Анализ заголовка
  • Исследование сервера
  • Исследование сетевого устройства
  • Отпечатки отправителя
  • Программные встроенные идентификаторы

В следующих разделах мы узнаем, как получать информацию с помощью Python для изучения электронной почты.

Извлечение информации из файлов EML

Файлы EML в основном представляют собой электронные письма в формате файлов, которые широко используются для хранения сообщений электронной почты. Это структурированные текстовые файлы, которые совместимы с несколькими почтовыми клиентами, такими как Microsoft Outlook, Outlook Express и Windows Live Mail.

EML-файл хранит заголовки сообщений электронной почты, содержимое тела, данные вложений в виде простого текста. Он использует base64 для кодирования двоичных данных и кодирование Quoted-Printable (QP) для хранения информации о содержимом. Сценарий Python, который можно использовать для извлечения информации из файла EML, приведен ниже —

Сначала импортируйте следующие библиотеки Python, как показано ниже:

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

import os
import quopri
import base64

В приведенных выше библиотеках quopri используется для декодирования кодированных значений QP из файлов EML. Любые данные в кодировке base64 могут быть декодированы с помощью библиотеки base64 .

Далее, давайте предоставим аргумент для обработчика командной строки. Обратите внимание, что здесь он будет принимать только один аргумент, который будет путь к файлу EML, как показано ниже —

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)

Теперь нам нужно определить функцию main (), в которой мы будем использовать метод message_from_file () из библиотеки электронной почты для чтения файла как объекта. Здесь мы получим доступ к заголовкам, содержанию тела, вложениям и другой полезной информации, используя результирующую переменную с именем emlfile, как показано в приведенном ниже коде —

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

Теперь нам нужно определить метод process_payload (), в котором мы будем извлекать содержимое тела сообщения с помощью метода get_payload () . Мы будем декодировать закодированные в QP данные с помощью функции quopri.decodestring () . Мы также проверим тип содержимого MIME, чтобы он мог правильно обрабатывать хранение электронной почты. Соблюдайте код, указанный ниже —

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)

После выполнения вышеуказанного скрипта мы получим информацию заголовка вместе с различными полезными данными на консоли.

Анализ файлов MSG с использованием Python

Сообщения электронной почты приходят в разных форматах. MSG является одним из таких форматов, используемых Microsoft Outlook и Exchange. Файлы с расширением MSG могут содержать простой текст ASCII для заголовков и основного текста сообщения, а также гиперссылки и вложения.

В этом разделе мы узнаем, как извлечь информацию из файла MSG с помощью Outlook API. Обратите внимание, что следующий скрипт Python будет работать только в Windows. Для этого нам нужно установить стороннюю библиотеку Python с именем pywin32 следующим образом:

pip install pywin32

Теперь импортируйте следующие библиотеки, используя показанные команды:

from __future__ import print_function
from argparse import ArgumentParser

import os
import win32com.client
import pywintypes

Теперь давайте предоставим аргумент для обработчика командной строки. Здесь он будет принимать два аргумента, один из которых будет путем к файлу MSG, а другой будет желаемой выходной папкой следующим образом:

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)

Теперь нам нужно определить функцию main (), в которой мы будем вызывать библиотеку win32com для настройки API-интерфейса Outlook, который дополнительно разрешает доступ к пространству имен MAPI .

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)

Теперь определите различные функции, которые мы используем в этом скрипте. Приведенный ниже код показывает определение функции display_msg_attribs (), которая позволяет нам отображать различные атрибуты сообщения, такие как тема, BCC, CC, размер, имя отправителя, отправлено и т. Д.

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

Теперь определите функцию display_msg_recipeints (), которая перебирает сообщения и отображает сведения о получателе.

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

Далее мы определяем функцию extract_msg_body (), которая извлекает из сообщения содержимое тела, HTML и обычный текст.

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

Далее мы определим функцию extract_attachments (), которая экспортирует данные вложения в желаемый выходной каталог.

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

Как только все функции будут определены, мы выведем все атрибуты на консоль со следующей строкой кодов:

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

После запуска вышеуказанного скрипта мы получим атрибуты сообщения и его вложений в окне консоли, а также несколько файлов в выходном каталоге.

Структурирование файлов MBOX из Google Takeout с использованием Python

MBOX-файлы — это текстовые файлы со специальным форматированием, которые разделяют хранящиеся в них сообщения. Они часто встречаются в системах UNIX, Thunderbolt и Google Takeouts.

В этом разделе вы увидите скрипт Python, где мы будем структурировать файлы MBOX, полученные из Google Takeouts. Но перед этим мы должны знать, как мы можем генерировать эти файлы MBOX, используя нашу учетную запись Google или Gmail.

Перевод почтового ящика аккаунта Google в формат MBX

Приобретение почтового ящика аккаунта Google подразумевает создание резервной копии нашего аккаунта Gmail. Резервное копирование может быть сделано по различным личным или профессиональным причинам. Обратите внимание, что Google обеспечивает резервное копирование данных Gmail. Чтобы приобрести почтовый ящик нашей учетной записи Google в формате MBOX, вам необходимо выполнить следующие шаги:

  • Откройте Личный кабинет .

  • Перейдите в раздел «Личная информация и конфиденциальность» и выберите «Управление ссылкой на контент».

  • Вы можете создать новый архив или управлять существующим. Если щелкнуть ссылку CREATE ARCHIVE , мы получим несколько флажков для каждого продукта Google, который мы хотим включить.

  • После выбора продуктов мы получим свободу выбора типа файла и максимального размера для нашего архива, а также способа доставки из списка.

  • Наконец, мы получим эту резервную копию в формате MBOX.

Откройте Личный кабинет .

Перейдите в раздел «Личная информация и конфиденциальность» и выберите «Управление ссылкой на контент».

Вы можете создать новый архив или управлять существующим. Если щелкнуть ссылку CREATE ARCHIVE , мы получим несколько флажков для каждого продукта Google, который мы хотим включить.

После выбора продуктов мы получим свободу выбора типа файла и максимального размера для нашего архива, а также способа доставки из списка.

Наконец, мы получим эту резервную копию в формате MBOX.

Код Python

Теперь файл MBOX, рассмотренный выше, может быть структурирован с использованием Python, как показано ниже —

Во-первых, необходимо импортировать библиотеки Python следующим образом:

from __future__ import print_function
from argparse import ArgumentParser

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

import base64

Все библиотеки были использованы и объяснены в более ранних сценариях, кроме библиотеки почтовых ящиков, которая используется для анализа файлов MBOX.

Теперь предоставьте аргумент для обработчика командной строки. Здесь он будет принимать два аргумента — один будет путем к файлу MBOX, а другой будет желаемой выходной папкой.

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)

Теперь определим функцию main () и вызовем класс mbox библиотеки почтовых ящиков, с помощью которого мы можем проанализировать файл MBOX, указав его путь —

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

Теперь определите метод чтения для библиотеки почтовых ящиков следующим образом:

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)

Теперь создайте некоторые переменные для дальнейшей обработки следующим образом:

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

Затем используйте tqdm для генерации индикатора выполнения и отслеживания итерационного процесса следующим образом:

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

Теперь проверьте, есть ли в сообщении о погоде полезные данные или нет. Если это так, мы определим метод write_payload () следующим образом:

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)

Теперь данные должны быть добавлены. Затем мы вызовем метод create_report () следующим образом:

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

Заметьте, что вышеприведенные операторы if-else легко понять. Теперь нам нужно определить метод, который будет извлекать имя файла из объекта msg следующим образом:

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)

Теперь с помощью следующих строк кода вы можете экспортировать файл:

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

Теперь давайте определим функцию для извлечения имен файлов из сообщения, чтобы точно представлять имена этих файлов следующим образом:

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)

Теперь мы можем написать файл CSV, определив функцию create_report () следующим образом:

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)

Как только вы запустите скрипт, указанный выше, мы получим отчет CSV и каталог, полный вложений.