mcpField
A
analyzing-outlook-pst-for-email-forensics

analyzing-outlook-pst-for-email-forensics

github.com/mukul975安全35.2k308 次安装8 周前更新
代码仓库

关于 analyzing-outlook-pst-for-email-forensics

面向数字取证分析人员的技能,用于检查 Outlook PST 和 OST 文件。内容涵盖导出邮箱数据、恢复已删除项目,以及利用邮件头追踪路由和通信。

主要功能

  1. 01导出 PST 和 OST 内容
  2. 02恢复已删除项目
  3. 03检查邮件路由头
  4. 04分析附件与邮件
  5. 05使用 libpff 和 Python 工具

适用场景

  1. 01调查事件中的邮箱证据
  2. 02恢复已删除邮件以供调查
  3. 03根据邮件头追踪邮件路由
  4. 04检查附件中的恶意软件或数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mukul975/anthropic-cybersecurity-skills --skill analyzing-outlook-pst-for-email-forensics

安装后在 Claude Code 中输入/analyzing-outlook-pst-for-email-forensics确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

LICENSE
references/
api-reference.md
standards.md
workflows.md
scripts/
agent.py

详细介绍

分析 Outlook PST 用于电子邮件取证

概述

Microsoft Outlook PST(Personal Storage Table,个人存储表)和 OST(Offline Storage Table,脱机存储表)文件是数字取证调查中的关键证据来源。PST 文件基于 MAPI(Messaging Application Programming Interface,消息传递应用程序编程接口)属性系统,以专有二进制格式存储电子邮件、日历事件、联系人、任务和便笺。对这些文件进行取证分析,可以恢复已删除的电子邮件(从“可恢复的项目”文件夹中)、提取电子邮件头以追踪邮件路由、分析附件中的恶意软件或外泄数据,以及重建通信模式。现代 PST 文件使用 Unicode 格式,页面大小为 4KB,最大可增长至 50GB,而旧版 ANSI 格式则限制为 2GB。

何时使用

  • 调查需要分析 outlook pst 以进行电子邮件取证的安全事件时
  • 为该领域构建检测规则或威胁狩猎查询时
  • SOC 分析师需要针对此类分析的结构化流程时
  • 验证相关攻击技术的安全监控覆盖范围时

前提条件

  • libpff/pffexport(开源 PST 解析器)
  • Python 3.8+ 及 pypff 或 libratom 库
  • MailXaminer、Forensic Email Collector 或 SysTools PST Forensics(商业软件)
  • Microsoft Outlook(可选,用于原生 PST 访问)
  • 足够的磁盘空间用于存放提取的内容

PST 文件位置

来源路径
Outlook 2016+ 默认%USERPROFILE%\Documents\Outlook Files*.pst
Outlook 旧版%LOCALAPPDATA%\Microsoft\Outlook*.pst
OST 缓存%LOCALAPPDATA%\Microsoft\Outlook*.ost
存档%USERPROFILE%\Documents\Outlook Files\archive.pst

使用开源工具进行分析

libpff / pffexport
# Export all items from PST file
pffexport -m all evidence.pst -t exported_pst

# Export only email messages
pffexport -m items evidence.pst -t exported_emails

# Export recovered/deleted items
pffexport -m recovered evidence.pst -t recovered_items

# Get PST file information
pffinfo evidence.pst
Python PST 分析
import pypff
import os
import json
import hashlib
import email
import sys
from datetime import datetime
from collections import defaultdict


class PSTForensicAnalyzer:
    """Forensic analysis of Outlook PST/OST files."""

    def __init__(self, pst_path: str, output_dir: str):
        self.pst_path = pst_path
        self.output_dir = output_dir
        os.makedirs(output_dir, exist_ok=True)
        self.pst = pypff.file()
        self.pst.open(pst_path)
        self.messages = []
        self.attachments = []
        self.stats = defaultdict(int)

    def process_folder(self, folder, folder_path: str = ""):
        """Recursively process PST folders and extract messages."""
        folder_name = folder.name or "Root"
        current_path = f"{folder_path}/{folder_name}" if folder_path else folder_name

        for i in range(folder.number_of_sub_messages):
            try:
                message = folder.get_sub_message(i)
                msg_data = self.extract_message(message, current_path)
                if msg_data:
                    self.messages.append(msg_data)
                    self.stats["total_messages"] += 1
            except Exception as e:
                self.stats["parse_errors"] += 1

        for i in range(folder.number_of_sub_folders):
            try:
                subfolder = folder.get_sub_folder(i)
                self.process_folder(subfolder, current_path)
            except Exception:
                continue

    def extract_message(self, message, folder_path: str) -> dict:
        """Extract forensic metadata from a single email message."""
        msg_data = {
            "folder": folder_path,
            "subject": message.subject or "",
            "sender": message.sender_name or "",
            "sender_email": "",
            "creation_time": str(message.creation_time) if message.creation_time else None,
            "delivery_time": str(message.delivery_time) if message.delivery_time else None,
            "modification_time": str(message.modification_time) if message.modification_time else None,
            "has_attachments": message.number_of_attachments > 0,
            "attachment_count": message.number_of_attachments,
            "body_size": len(message.plain_text_body or b""),
            "html_size": len(message.html_body or b""),
        }

        # Extract transport headers for routing analysis
        headers = message.transport_headers
        if headers:
            msg_data["headers_present"] = True
            msg_data["headers_size"] = len(headers)
            # Parse key headers
            parsed = email.message_from_string(headers)
            msg_data["from_header"] = parsed.get("From", "")
            msg_data["to_header"] = parsed.get("To", "")
            msg_data["date_header"] = parsed.get("Date", "")
            msg_data["message_id"] = parsed.get("Message-ID", "")
            msg_data["x_originating_ip"] = parsed.get("X-Originating-IP", "")
            msg_data["received_headers"] = parsed.get_all("Received", [])

        # Process attachments
        for j in range(message.number_of_attachments):
            try:
                attachment = message.get_attachment(j)
                att_data = {
                    "message_subject": msg_data["subject"],
                    "name": attachment.name or f"attachment_{j}",
                    "size": attachment.size,
                    "content_type": "",
                }
                self.attachments.append(att_data)
                self.stats["total_attachments"] += 1
            except Exception:
                continue

        return msg_data

    def save_attachments(self, max_size_mb: int = 100):
        """Export attachments to disk for analysis."""
        att_dir = os.path.join(self.output_dir, "attachments")
        os.makedirs(att_dir, exist_ok=True)

        root = self.pst.get_root_folder()
        self._save_attachments_recursive(root, att_dir, max_size_mb)

    def _save_attachments_recursive(self, folder, att_dir, max_size_mb):
        for i in range(folder.number_of_sub_messages):
            try:
                message = folder.get_sub_message(i)
                for j in range(message.number_of_attachments):
                    att = message.get_attachment(j)
                    if att.size and att.size < max_size_mb * 1024 * 1024:
                        name = att.name or f"unknown_{i}_{j}"
                        safe_name = "".join(c if c.isalnum() or c in ".-_" else "_" for c in name)
                        path = os.path.join(att_dir, safe_name)
                        try:
                            data = att.read_buffer(att.size)
                            with open(path, "wb") as f:
                                f.write(data)
                        except Exception:
                            continue
            except Exception:
                continue

        for i in range(folder.number_of_sub_folders):
            try:
                self._save_attachments_recursive(folder.get_sub_folder(i), att_dir, max_size_mb)
            except Exception:
                continue

    def generate_report(self) -> str:
        """Generate comprehensive PST forensic analysis report."""
        root = self.pst.get_root_folder()
        self.process_folder(root)

        report = {
            "analysis_timestamp": datetime.now().isoformat(),
            "pst_file": self.pst_path,
            "pst_size_bytes": os.path.getsize(self.pst_path),
            "statistics": dict(self.stats),
            "messages": self.messages[:500],
            "attachments": self.attachments[:200],
        }

        report_path = os.path.join(self.output_dir, "pst_forensic_report.json")
        with open(report_path, "w") as f:
            json.dump(report, f, indent=2, default=str)

        print(f"[*] Total messages: {self.stats['total_messages']}")
        print(f"[*] Total attachments: {self.stats['total_attachments']}")
        print(f"[*] Parse errors: {self.stats['parse_errors']}")
        return report_path

    def close(self):
        self.pst.close()


def main():
    if len(sys.argv) < 3:
        print("Usage: python process.py <pst_file> <output_dir>")
        sys.exit(1)
    analyzer = PSTForensicAnalyzer(sys.argv[1], sys.argv[2])
    analyzer.generate_report()
    analyzer.close()


if __name__ == "__main__":
    main()

电子邮件头分析

用于取证调查的关键邮件头:

邮件头取证价值
Received邮件路由链(从下往上读)
X-Originating-IP发件人的实际 IP 地址
Message-ID用于关联的唯一标识符
Date发送时间戳
Return-Path退信地址(可能与 From 不同)
DKIM-Signature域认证签名
Authentication-ResultsSPF、DKIM、DMARC 验证结果
X-Mailer所使用的电子邮件客户端

参考资料

示例输出

$ pffexport /evidence/jsmith_archive.pst -t /analysis/pst_output

pffexport 20231205 - libpff PST/OST Export Tool
=================================================
Input: /evidence/jsmith_archive.pst (2.3 GB)

Exporting PST contents...
  Folders:       45
  Messages:      12,456
  Attachments:   3,234
  Contacts:      567
  Calendar:      234
  Tasks:         89

Export completed in 3m 42s.

$ python3 pst_analyzer.py /analysis/pst_output /analysis/email_report

PST Forensic Analysis Report
==============================
Source: jsmith_archive.pst ([email protected])
Date Range: 2023-06-01 to 2024-01-18

--- Mailbox Statistics ---
  Total Emails:       12,456
  Sent:               4,567
  Received:           7,889
  With Attachments:   3,234
  Deleted (recovered): 234

--- Phishing / Suspicious Emails ---
Email #8923
  Date:        2024-01-15 14:30:22 UTC
  From:        "IT Support" <[email protected]>
  To:          [email protected]
  Subject:     Urgent: Password Reset Required
  Headers:
    Return-Path:    [email protected]
    X-Originating-IP: 203.0.113.55
    Received:       from mail-relay.c0rporate-help.com (203.0.113.55)
    SPF:            FAIL (domain c0rporate-help.com)
    DKIM:           NONE
    DMARC:          FAIL
  Attachments:
    - Password_Reset_Form.xlsm (245 KB) SHA-256: 7a3b8c9d...e1f2a3b4
  Body Preview:  "Dear Employee, Your password will expire in 24 hours.
                  Please open the attached form to reset your credentials..."

--- Data Exfiltration Indicators ---
Email #9102
  Date:        2024-01-16 03:15:45 UTC
  From:        [email protected]
  To:          [email protected]
  Subject:     (no subject)
  Attachments:
    - archive_part1.7z (24.5 MB) - encrypted
    - archive_part2.7z (24.5 MB) - encrypted

Email #9103
  Date:        2024-01-16 03:18:22 UTC
  From:        [email protected]
  To:          [email protected]
  Subject:     Re:
  Attachments:
    - archive_part3.7z (18.2 MB) - encrypted

--- Keyword Hits ---
  "confidential":     45 emails
  "password":         23 emails
  "transfer":         12 emails
  "resign":           3 emails
  "delete evidence":  1 email (Email #9200, 2024-01-17 22:30:00 UTC)

Summary:
  Phishing emails detected:    1 (initial compromise vector)
  Suspicious sent emails:      5 (to personal accounts with attachments)
  Encrypted attachments:       3 (67.2 MB total - possible exfiltration)
  Report: /analysis/email_report/pst_forensic_report.json

评论

支持 Markdown