이메일을 사용한 조사

이전 장에서는 네트워크 포렌식의 중요성과 프로세스 및 관련된 개념에 대해 논의했습니다. 이 장에서는 디지털 포렌식에서 이메일의 역할과 Python을 사용한 조사에 대해 알아 보겠습니다.

조사에서 이메일의 역할

이메일은 비즈니스 커뮤니케이션에서 매우 중요한 역할을하며 인터넷에서 가장 중요한 애플리케이션 중 하나로 부상했습니다. 컴퓨터뿐만 아니라 휴대폰 및 태블릿과 같은 다른 전자 장치에서도 메시지와 문서를 보내는 편리한 모드입니다.

이메일의 부정적인 측면은 범죄자들이 회사에 대한 중요한 정보를 유출 할 수 있다는 것입니다. 따라서 최근 몇 년 동안 디지털 포렌식에서 이메일의 역할이 증가했습니다. 디지털 포렌식에서 이메일은 중요한 증거로 간주되며 이메일 헤더 분석은 포렌식 프로세스 중에 증거를 수집하는 데 중요해졌습니다.

조사자는 이메일 포렌식을 수행하는 동안 다음과 같은 목표를 가지고 있습니다.

  • 주요 범죄자를 식별하려면
  • 필요한 증거를 수집하려면
  • 연구 결과 발표
  • 케이스를 구축하려면

이메일 포렌식의 과제

이메일 포렌식은 현재 대부분의 커뮤니케이션이 이메일에 의존하기 때문에 조사에서 매우 중요한 역할을합니다. 그러나 이메일 포렌식 조사관은 조사 중에 다음과 같은 문제에 직면 할 수 있습니다.

가짜 이메일

이메일 포렌식에서 가장 큰 문제는 헤더를 조작하고 스크립팅하여 만든 가짜 이메일을 사용하는 것입니다.이 범주에서 범죄자들은 ​​또한 등록 된 사용자가 만료되는 임시 주소로 이메일을받을 수있는 서비스 인 임시 이메일을 사용합니다. 일정 기간 후.

스푸핑

이메일 포렌식의 또 다른 문제는 범죄자가 이메일을 다른 사람의 것으로 제시하는 데 사용되는 스푸핑입니다. 이 경우 기기는 가짜 IP 주소와 원래 IP 주소를 모두 수신합니다.

익명의 재 메일 링

여기서 이메일 서버는 이메일 메시지를 추가로 전달하기 전에 이메일 메시지에서 식별 정보를 제거합니다. 이것은 이메일 조사에 또 다른 큰 도전으로 이어집니다.

이메일 포렌식 조사에 사용되는 기술

이메일 포렌식은 전송 날짜 / 시간 및 보낸 사람의 의도와 같은 다른 정보와 함께 메시지의 실제 발신자와 수신자를 식별하기위한 증거로서 이메일의 출처 및 내용을 연구합니다. 메타 데이터 조사, 포트 스캐닝 및 키워드 검색이 포함됩니다.

이메일 포렌식 조사에 사용할 수있는 몇 가지 일반적인 기술은 다음과 같습니다.

  • 헤더 분석
  • 서버 조사
  • 네트워크 장치 조사
  • 발신자 메일러 지문
  • 소프트웨어 임베디드 식별자

다음 섹션에서는 이메일 조사를 위해 Python을 사용하여 정보를 가져 오는 방법을 배웁니다.

EML 파일에서 정보 추출

EML 파일은 기본적으로 이메일 메시지를 저장하는 데 널리 사용되는 파일 형식의 이메일입니다. Microsoft Outlook, Outlook Express 및 Windows Live Mail과 같은 여러 이메일 클라이언트에서 호환되는 구조화 된 텍스트 파일입니다.

EML 파일은 이메일 헤더, 본문 내용, 첨부 파일 데이터를 일반 텍스트로 저장합니다. 이진 데이터를 인코딩하는 데 base64를 사용하고 콘텐츠 정보를 저장하기 위해 QP (Quoted-Printable) 인코딩을 사용합니다. 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 파일의 경로가되는 하나의 인수 만 허용합니다.

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는 Microsoft Outlook 및 Exchange에서 사용되는 이러한 종류의 형식 중 하나입니다. MSG 확장자를 가진 파일에는 헤더 및 기본 메시지 본문, 하이퍼 링크 및 첨부 파일에 대한 일반 ASCII 텍스트가 포함될 수 있습니다.

이 섹션에서는 Outlook API를 사용하여 MSG 파일에서 정보를 추출하는 방법을 배웁니다. 다음 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 설정을위한 라이브러리 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() 제목, 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 테이크 아웃에서 MBOX 파일 구조화

MBOX 파일은 저장된 메시지를 분할하는 특수 형식의 텍스트 파일입니다. 종종 UNIX 시스템, Thunderbolt 및 Google 테이크 아웃과 관련하여 발견됩니다.

이 섹션에서는 Google 테이크 아웃에서 가져온 MBOX 파일을 구성하는 Python 스크립트를 볼 수 있습니다. 그러나 그 전에 Google 계정 또는 Gmail 계정을 사용하여 이러한 MBOX 파일을 생성하는 방법을 알아야합니다.

MBX 형식으로 Google 계정 사서함 가져 오기

Google 계정 사서함을 얻는 것은 Gmail 계정을 백업하는 것을 의미합니다. 다양한 개인적 또는 직업적 이유로 백업을 수행 할 수 있습니다. Google은 Gmail 데이터 백업을 제공합니다. Google 계정 사서함을 MBOX 형식으로 얻으려면 아래 단계를 따라야합니다.

  • 열다 My account 계기반.

  • 개인 정보 및 개인 정보 섹션으로 이동하여 콘텐츠 링크 제어를 선택합니다.

  • 새 아카이브를 생성하거나 기존 아카이브를 관리 할 수 ​​있습니다. 클릭하면CREATE ARCHIVE 링크를 클릭하면 포함하려는 각 Google 제품에 대한 몇 가지 확인란이 표시됩니다.

  • 제품을 선택한 후 목록에서 선택할 수있는 전달 방법과 함께 아카이브의 파일 유형과 최대 크기를 자유롭게 선택할 수 있습니다.

  • 마지막으로이 백업을 MBOX 형식으로 가져옵니다.

파이썬 코드

이제 위에서 설명한 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 파일을 구문 분석하는 데 사용되는 라이브러리.

이제 명령 줄 처리기에 대한 인수를 제공합니다. 여기서 두 개의 인수를받습니다. 하나는 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 다음과 같이 라이브러리-

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 보고서와 디렉토리가 제공됩니다.