使用电子邮件进行调查

前面的章节讨论了网络取证的重要性和过程以及所涉及的概念.在本章中,让我们了解电子邮件在数字取证中的作用以及使用Python进行调查.

电子邮件在调查中的作用

电子邮件播放在商业通信中发挥着非常重要的作用,并已成为互联网上最重要的应用之一.它们是发送信息和文档的便捷模式,不仅来自计算机,还来自其他电子产品,如手机和平板电脑.

电子邮件的消极方面是犯罪分子可能泄露有关其公司的重要信息.因此,近年来电子邮件在数字取证中的作用有所增加.在数字取证中,电子邮件被认为是关键证据,电子邮件标题分析对于在取证过程中收集证据变得非常重要.

调查员在执行电子邮件取证和减去时具有以下目标;

  • 识别主要罪犯

  • 收集必要的证据

  • 提交调查结果

  • 构建案例

电子邮件取证中的挑战

电子邮件取证在调查中起着非常重要的作用,因为当前时代的大多数通信都依赖于电子邮件.但是,电子邮件取证调查员在调查期间可能会面临以下挑战并且减去;

虚假电子邮件

电子邮件取证中最大的挑战是使用通过操纵和脚本标题等创建的虚假电子邮件.在此类别中,犯罪分子还使用临时电子邮件,这是一种允许注册用户在一段时间后过期的临时地址接收电子邮件的服务.

欺骗

电子邮件取证的另一个挑战是欺骗,其中犯罪分子过去常常以别人的身份呈现电子邮件.在这种情况下,机器将同时接收伪造和原始IP地址.

匿名重新发送电子邮件

此处,电子邮件服务器剥离识别信息从电子邮件消息进一步转发之前.这导致电子邮件调查面临另一大挑战.

电子邮件取证调查中使用的技术

电子邮件取证是对电子邮件来源和内容的研究用于识别消息的实际发送者和接收者的证据以及一些其他信息,例如传输的日期/时间和发送者的意图.它涉及调查元数据,端口扫描以及关键字搜索.

可用于电子邮件取证调查的一些常用技术是

  • 标题分析

  • 服务器调查

  • 网络设备调查

  • Sender Mailer Fingerprints

  • 软件嵌入式标识符

在以下各节中,我们将学习如何获取使用Python进行电子邮件调查的信息.

从EML文件中提取信息

EML文件基本上是文件格式的电子邮件,广泛使用用于存储电子邮件.它们是结构化的文本文件,可与多个电子邮件客户端(如Microsoft Outlook,Outlook Express和Windows Live Mail)兼容.

EML文件将电子邮件标题,正文内容,附件数据存储为普通文件文本.它使用base64对二进制数据进行编码,使用Quoted-Printable(QP)编码来存储内容信息.可用于从EML文件中提取信息的Python脚本在下面和下面给出;

首先,导入以下Python库,如下所示 :

 
 from __future__ import print_function 
 from argparse import ArgumentParser,FileType 
 from email import message_from_file 
 import os 
 import quopri 
 import base64

在上述库中, quopri 用于解码EML文件中的QP编码值.任何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()方法提取邮件正文内容.我们将使用 quopri.decodestring()函数解码QP编码数据.我们还将检查内容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)

执行上述脚本后,我们将在控制台上获取标头信息以及各种有效负载.

使用Python分析MSG文件

电子邮件有多种不同的格式. MSG就是Microsoft Outlook和Exchange使用的一种格式.具有MSG扩展名的文件可能包含标题和主要邮件正文的纯ASCII文本以及超链接和附件.

在本节中,我们将学习如何使用MSG文件提取信息Outlook API.请注意,以下Python脚本仅适用于Windows.为此,我们需要安装名为 pywin32 的第三方Python库,如下所示 :

 
 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 库来设置 Outlook API ,进一步允许访问 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)

现在,定义我们在此脚本中使用的不同函数.下面给出的代码显示了 display_msg_attribs()函数,它允许我们显示消息的各种属性,如subject,to,BCC,CC,Size,SenderName,sent等等.

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

运行上面的脚本后,我们将得到消息的属性及其控制台窗口中的附件以及输出目录中的多个文件.

使用Python从Google Takeout构建MBOX文件

MBOX文件是具有特殊格式的文本文件,用于分割存储在其中的消息.它们通常与UNIX系统,Thunderbolt和Google Takeouts相关联.

在本节中,您将看到一个Python脚本,我们将构建从Google Takeouts获取的MBOX文件.但在此之前,我们必须知道如何使用我们的Google帐户或Gmail帐户生成这些MBOX文件.

将Google帐户邮箱收集为MBX格式

获取Google帐户信箱意味着要备份我们的Gmail帐户.可以出于各种个人或职业原因进行备份.请注意,Google会提供Gmail数据备份.要将我们的Google帐户邮箱转换为MBOX格式,您需要按照以下步骤进行操作:

  • 打开我的帐户信息中心.

  • 转到个人信息&隐私部分并选择控制您的内容链接.

  • 您可以创建新存档或管理现有存档.如果我们点击创建存档链接,我们会为每个我们希望包含的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)))

现在,为 mailbox 库定义一个reader方法,如下所示s :

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 = [] 
如果msg.is_multipart():
用于pyld中的条目:
 export_path + = write_payload(entry,out_dir)
 else:
 content_type = msg.get_content_type()
如果content_type.lower()中的"application/":
 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.一个ppend(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 ="在msg.get('Content-Disposition',"N/A")中:
 content = base64.b64decode(msg.get_payload())
 export_path.append(export_content(m) sg,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)

现在,我们可以通过定义 create_report()函数来编写CSV文件,如下所示 :

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报告和目录中包含附件.