Communitygithub.com

Outlook PST 郵件取證分析

使用 libpff 和 pst-utils 解析 Microsoft Outlook PST 與 OST 檔案,提取郵件內容、標頭、附件、已刪除項目及 MAPI 元數據,包括從「可復原的項目」資料夾中復原項目。適用於進行郵件取證調查、法律 e-discovery 或事件應變時,需重建溝通模式或追蹤來自 Outlook 封存檔的郵件路由之情境。

Outlook PST 郵件取證分析 是什麼?

此技能旨在為數位取證專業人員提供對 Microsoft Outlook PST/OST 檔案進行深入分析的能力。它利用 libpff(pffexport)和 Python pypff 等開源工具,可提取郵件內容、傳輸標頭、附件、已刪除項目以及完整的 MAPI 元數據,包括從「可復原的項目」資料夾中復原已刪除郵件,並支援對 Unicode 與 ANSI 格式的 PST 檔案進行分析。其核心功能包括遞迴處理資料夾結構、解析郵件路由鏈(Received 標頭)、提取寄件者 IP 地址(X-Originating-IP)、驗證 SPF/DKIM/DMARC 驗證結果,以及掃描附件中的惡意軟體或資料外洩跡象。此技能特別適用於事件應變調查、法律 e-discovery 流程以及 SOC 分析師需重建溝通模式或追蹤郵件路由的場景。除了開源工具,它也支援 MailXaminer、SysTools PST Forensics 等商業工具,並提供一個可擴展的 Python 類別(PSTForensicAnalyzer)來自動化取證報告生成,包含郵件統計、附件匯出及關鍵字搜尋功能。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mukul975/Anthropic-Cybersecurity-Skills/tree/main/skills/analyzing-outlook-pst-for-email-forensics

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

分析 Outlook PST 以進行郵件取證

概覽

Microsoft Outlook PST(個人儲存表格)與 OST(離線儲存表格)檔案是數位取證調查中關鍵的證據來源。PST 檔案以基於 MAPI(訊息處理應用程式介面)屬性系統的專有二進位格式儲存電子郵件、日曆事件、聯絡人、工作和備註。對這些檔案進行取證分析能夠復原已刪除的電子郵件(來自「可復原的項目」資料夾)、提取用於追蹤郵件路由的郵件標頭、分析附件中是否存在惡意軟體或外洩資料,以及重建溝通模式。現代的 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

# 從 PST 檔案匯出所有項目
pffexport -m all evidence.pst -t exported_pst

# 僅匯出電子郵件
pffexport -m items evidence.pst -t exported_emails

# 匯出已復原/已刪除項目
pffexport -m recovered evidence.pst -t recovered_items

# 取得 PST 檔案資訊
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:
    """用於 Outlook PST/OST 檔案的取證分析。"""

    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 = ""):
        """遞迴處理 PST 資料夾並提取郵件。"""
        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:
        """從單一封郵件中提取取證元數據。"""
        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""),
        }

        # 提取傳輸標頭以進行路由分析
        headers = message.transport_headers
        if headers:
            msg_data["headers_present"] = True
            msg_data["headers_size"] = len(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", [])

        # 處理附件
        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):
        """將附件匯出至磁碟以供分析。"""
        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:
        """產生全面的 PST 取證分析報告。"""
        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"[*] 郵件總數: {self.stats['total_messages']}")
        print(f"[*] 附件總數: {self.stats['total_attachments']}")
        print(f"[*] 解析錯誤: {self.stats['parse_errors']}")
        return report_path

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


def main():
    if len(sys.argv) < 3:
        print("用法: 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 匯出工具
=================================================
輸入: /evidence/jsmith_archive.pst (2.3 GB)

正在匯出 PST 內容...
  資料夾:       45
  郵件:         12,456
  附件:        3,234
  聯絡人:       567
  日曆:         234
  工作:         89

匯出完成,耗時 3 分 42 秒。

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

PST 取證分析報告
==============================
來源: jsmith_archive.pst ([email protected])
日期範圍: 2023-06-01 至 2024-01-18

--- 信箱統計 ---
  電子郵件總數:       12,456
  已傳送:             4,567
  已接收:             7,889
  含附件:             3,234
  已刪除(已復原):   234

--- 釣魚/可疑郵件 ---
郵件 #8923
  日期:        2024-01-15 14:30:22 UTC
  寄件者:      "IT 支援" <[email protected]>
  收件者:      [email protected]
  主旨:        緊急:密碼重設要求
  標頭:
    Return-Path:    [email protected]
    X-Originating-IP: 203.0.113.55
    Received:       from mail-relay.c0rporate-help.com (203.0.113.55)
    SPF:            FAIL (網域 c0rporate-help.com)
    DKIM:           NONE
    DMARC:          FAIL
  附件:
    - Password_Reset_Form.xlsm (245 KB) SHA-256: 7a3b8c9d...e1f2a3b4
  內容預覽:  "親愛的員工,您的密碼將在 24 小時內過期。
              請開啟附件中的表單以重設您的憑證..."

--- 資料外洩跡象 ---
郵件 #9102
  日期:        2024-01-16 03:15:45 UTC
  寄件者:      [email protected]
  收件者:      [email protected]
  主旨:        (無主旨)
  附件:
    - archive_part1.7z (24.5 MB) - 已加密
    - archive_part2.7z (24.5 MB) - 已加密

郵件 #9103
  日期:        2024-01-16 03:18:22 UTC
  寄件者:      [email protected]
  收件者:      [email protected]
  主旨:        Re:
  附件:
    - archive_part3.7z (18.2 MB) - 已加密

--- 關鍵字命中 ---
  "confidential":     45 封郵件
  "password":         23 封郵件
  "transfer":         12 封郵件
  "resign":           3 封郵件
  "delete evidence":  1 封郵件 (郵件 #9200, 2024-01-17 22:30:00 UTC)

摘要:
  偵測到釣魚郵件:         1 (初始入侵媒介)
  可疑已傳送郵件:         5 (傳送至個人帳戶並含附件)
  已加密附件:             3 (總計 67.2 MB - 可能為外洩)
  報告: /analysis/email_report/pst_forensic_report.json

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

abusing-dpapi-for-credential-access

Extract and decrypt Windows DPAPI-protected secrets (Credential Manager, browser logins/cookies, Wi-Fi credentials, KeePass keys) online or offline using SharpDPAPI, SharpChrome, Mimikatz, or Impacket

abusing-shadow-credentials-for-privesc

Take over Active Directory accounts by writing attacker-controlled public keys to msDS-KeyCredentialLink (Shadow Credentials) with pyWhisker, Whisker, or Certipy, then authenticate via PKINIT to recover the target

achieving-cmmc-level-2-compliance

>-

acquiring-disk-image-with-dd-and-dcfldd

Create forensically sound bit-for-bit disk images with dd or dcfldd on a Linux forensic workstation, preserving evidence integrity through hash verification (MD5/SHA) during acquisition. Use when imaging a suspect drive, USB device, or memory card for investigation, preserving volatile disk evidence during incident response, or producing a verified copy for legal or law-enforcement proceedings before any destructive analysis.

analyzing-active-directory-acl-abuse

Detect dangerous ACL misconfigurations in Active Directory using ldap3

analyzing-android-malware-with-apktool

Perform static analysis of Android APK malware using apktool for resource decompilation, jadx for Java source recovery, and androguard for manifest inspection, dangerous permission-combination detection, and identification of obfuscated code, dynamic code loading, and reflection-based API calls. Use to statically triage a suspicious APK without executing it or to build mobile malware detection rules.

analyzing-api-gateway-access-logs

Parses API Gateway access logs (AWS API Gateway, Kong, Nginx) to detect

analyzing-apt-group-with-mitre-navigator

Query ATT&CK data with attackcti, mitreattack-python, and stix2, then build MITRE ATT&CK Navigator layers and multi-layer heatmap overlays mapping one or more APT groups

analyzing-azure-activity-logs-for-threats

Queries Azure Monitor activity logs and sign-in logs via azure-monitor-query

analyzing-bootkit-and-rootkit-samples

Analyzes bootkit and advanced rootkit malware infecting the Master

analyzing-browser-forensics-with-hindsight

Parse Chromium-based browser databases with Hindsight to extract and correlate browsing history, downloads, cookies, cached content, autofill data, saved passwords, and extensions from Chrome, Edge, Brave, Opera, and Vivaldi into a unified timeline (XLSX, JSON, or SQLite output). Use during incident response, insider-threat investigations, or criminal cases when you need to reconstruct a user

analyzing-campaign-attribution-evidence

Systematically evaluate cyber-campaign evidence to attribute an operation to a threat actor, using the Diamond Model and Analysis of Competing Hypotheses (ACH) to weigh infrastructure overlaps, TTP consistency, malware code similarity, and timing/language artifacts into confidence-weighted attribution assessments. Use when an incident investigation needs a defensible attribution confidence level.

analyzing-certificate-transparency-for-phishing

Monitor Certificate Transparency logs using crt.sh and Certstream to

analyzing-cloud-storage-access-patterns

Detect abnormal access in AWS S3, GCS, and Azure Blob Storage by analyzing CloudTrail Data Events, GCS audit logs, and Azure Storage Analytics for after-hours bulk downloads, new-IP access, and API-call spikes (e.g. GetObject) via statistical baselines and time-series anomaly detection. Use when investigating suspected cloud data exfiltration or building related detection rules.

analyzing-cobalt-strike-beacon-configuration

Extract and analyze Cobalt Strike beacon configuration from PE files

analyzing-cobaltstrike-malleable-c2-profiles

Parse and analyze Cobalt Strike Malleable C2 profiles with dissect.cobaltstrike (profiles and beacon-payload configs) and pyMalleableC2 (AST parsing) to extract HTTP/DNS transforms, URIs, headers, sleep/jitter, and injection behavior, then generate network detection signatures. Use when reverse-engineering a captured malleable profile or building detections against Cobalt Strike Beacon traffic.

analyzing-command-and-control-communication

Analyzes malware C2 communication over HTTP, HTTPS, DNS, and custom

analyzing-cyber-kill-chain

Analyzes intrusion activity against the Lockheed Martin Cyber Kill Chain

analyzing-disk-image-with-autopsy

Perform comprehensive forensic analysis of raw (dd), E01, or AFF disk images with Autopsy and The Sleuth Kit, recovering deleted files, examining metadata and embedded artifacts, keyword searching, and building investigation timelines with visual reports. Use for structured analysis of a forensic disk image or when stakeholders need visual reports from evidence.

analyzing-dns-logs-for-exfiltration

Analyzes DNS query logs to detect data exfiltration via DNS tunneling,

相關技能