メールを使用した調査

前の章では、ネットワークフォレンジックの重要性とプロセス、および関連する概念について説明しました。この章では、デジタルフォレンジックにおける電子メールの役割とPythonを使用した電子メールの調査について学びましょう。

調査における電子メールの役割

電子メールはビジネスコミュニケーションにおいて非常に重要な役割を果たし、インターネット上で最も重要なアプリケーションの1つとして浮上しています。コンピュータだけでなく、携帯電話やタブレットなどの他の電子機器からもメッセージやドキュメントを送信するのに便利なモードです。

電子メールのマイナス面は、犯罪者が会社に関する重要な情報を漏らす可能性があることです。したがって、デジタルフォレンジックにおける電子メールの役割は近年増加しています。デジタルフォレンジックでは、電子メールは重要な証拠と見なされており、電子メールヘッダー分析はフォレンジックプロセス中に証拠を収集するために重要になっています。

調査員は、電子メールフォレンジックを実行する際に次の目標を持っています-

  • 主な犯罪者を特定する
  • 必要な証拠を収集する
  • 調査結果を提示するには
  • ケースを作成するには

電子メールフォレンジックにおける課題

現在のコミュニケーションのほとんどは電子メールに依存しているため、電子メールフォレンジックは調査において非常に重要な役割を果たします。ただし、電子メールのフォレンジック調査担当者は、調査中に次の課題に直面する可能性があります-

偽のメール

電子メールフォレンジックの最大の課題は、ヘッダーなどを操作およびスクリプト化することによって作成された偽の電子メールの使用です。このカテゴリでは、犯罪者は、登録ユーザーが期限切れの一時アドレスで電子メールを受信できるようにするサービスである一時電子メールも使用します。一定期間後。

なりすまし

電子メールフォレンジックにおけるもう1つの課題は、犯罪者が他人の電子メールを提示していたなりすましです。この場合、マシンは偽のIPアドレスと元のIPアドレスの両方を受け取ります。

匿名の再メール

ここで、電子メールサーバーは、さらに転送する前に、電子メールメッセージから識別情報を取り除きます。これは、電子メール調査のもう1つの大きな課題につながります。

電子メールフォレンジック調査で使用される手法

電子メールフォレンジックは、メッセージの実際の送信者と受信者を特定するための証拠としての電子メールのソースとコンテンツの調査であり、送信の日時や送信者の意図などの他の情報も含まれます。これには、メタデータの調査、ポートスキャン、およびキーワード検索が含まれます。

電子メールのフォレンジック調査に使用できる一般的な手法のいくつかは次のとおりです。

  • ヘッダー分析
  • サーバー調査
  • ネットワークデバイスの調査
  • 送信者メーラーの指紋
  • ソフトウェア組み込み識別子

次のセクションでは、電子メール調査の目的でPythonを使用して情報を取得する方法を学習します。

EMLファイルからの情報の抽出

EMLファイルは基本的にファイル形式の電子メールであり、電子メールメッセージの保存に広く使用されています。これらは、Microsoft Outlook、Outlook Express、WindowsLiveメールなどの複数の電子メールクライアント間で互換性のある構造化テキストファイルです。

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

上記のライブラリでは、 quopriEMLファイルからQPエンコードされた値をデコードするために使用されます。base64でエンコードされたデータは、base64 図書館。

次に、コマンドラインハンドラーの引数を指定しましょう。ここでは、以下に示すように、EMLファイルへのパスとなる引数を1つだけ受け入れることに注意してください。

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)

上記のスクリプトを実行した後、コンソールでさまざまなペイロードとともにヘッダー情報を取得します。

Pythonを使用したMSGファイルの分析

電子メールメッセージにはさまざまな形式があります。MSGは、MicrosoftOutlookおよびExchangeで使用されるそのような形式の1つです。MSG拡張子の付いたファイルには、ヘッダーとメインメッセージ本文のプレーンASCIIテキスト、およびハイパーリンクと添付ファイルが含まれる場合があります。

このセクションでは、OutlookAPIを使用してMSGファイルから情報を抽出する方法を学習します。次のPythonスクリプトはWindowsでのみ機能することに注意してください。このために、という名前のサードパーティのPythonライブラリをインストールする必要がありますpywin32 次のように-

pip install pywin32

次に、示されているコマンドを使用して次のライブラリをインポートします-

from __future__ import print_function
from argparse import ArgumentParser

import os
import win32com.client
import pywintypes

ここで、コマンドラインハンドラーの引数を指定しましょう。ここでは、次のように2つの引数を受け入れます。1つはMSGファイルへのパスで、もう1つは目的の出力フォルダーです。

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 名前空間。

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

上記のスクリプトを実行した後、出力ディレクトリ内のいくつかのファイルとともに、コンソールウィンドウにメッセージとその添付ファイルの属性を取得します。

Pythonを使用してGoogleデータエクスポートからMBOXファイルを構造化する

MBOXファイルは、メッセージを分割する特別なフォーマットのテキストファイルです。これらは、UNIXシステム、Thunderbolt、およびGoogleデータエクスポートに関連して見つかることがよくあります。

このセクションでは、Pythonスクリプトを確認します。このスクリプトでは、Googleデータエクスポートから取得したMBOXファイルを構造化します。ただし、その前に、GoogleアカウントまたはGmailアカウントを使用してこれらのMBOXファイルを生成する方法を知っておく必要があります。

GoogleアカウントメールボックスをMBX形式で取得する

Googleアカウントのメールボックスを取得することは、Gmailアカウントのバックアップを取ることを意味します。バックアップは、さまざまな個人的または職業上の理由で取得できます。GoogleはGmailデータのバックアップを提供していることに注意してください。GoogleアカウントのメールボックスをMBOX形式で取得するには、以下の手順に従う必要があります-

  • 開いた My account ダッシュボード。

  • [個人情報とプライバシー]セクションに移動し、[コンテンツの管理]リンクを選択します。

  • 新しいアーカイブを作成することも、既存のアーカイブを管理することもできます。クリックすると、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

すべてのライブラリは、以前のスクリプトで使用および説明されていますが、 mailbox MBOXファイルの解析に使用されるライブラリ。

ここで、コマンドラインハンドラーの引数を指定します。ここでは、2つの引数を受け入れます。1つはMBOXファイルへのパスで、もう1つは目的の出力フォルダーです。

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 次のようなライブラリ-

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

それでは、ファイル名を抽出する関数を定義しましょう。 message これらのファイルの名前を次のように正確に表すには-

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レポートと添付ファイルでいっぱいのディレクトリが表示されます。