Communitygithub.com

Outlook PST电子邮件取证分析

使用libpff和pst-utils解析Microsoft Outlook PST和OST文件,提取邮件内容、标头、附件、已删除邮件和MAPI元数据,包括从“可恢复项”文件夹中恢复项目。适用于进行电子邮件取证调查、法律电子发现或需要从Outlook存档重建通信模式或追踪邮件路由的事件响应。

Outlook PST电子邮件取证分析 是什么?

该技能旨在对Microsoft Outlook PST(个人存储表)和OST(脱机存储表)文件进行深度取证分析。它利用开源工具libpff/pffexport以及Python库(如pypff)来解析这些专有的二进制文件,提取包括邮件正文、传输标头、附件、联系人、日历项和任务在内的完整内容。特别地,它可以恢复已删除的邮件(包括来自“可恢复项”文件夹的项目),并通过分析Received、X-Originating-IP和Message-ID等关键邮件标头来重建通信模式、追踪消息路由。该技能支持Windows上Outlook 2016+及更早版本,适用于安全事件响应、法律电子发现和内部调查场景。其输出包括详细的JSON报告,包含邮件统计信息、可疑邮件标记和关键字命中分析。

兼容平台~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(消息处理API)属性系统的专有二进制格式存储电子邮件、日历事件、联系人、任务和便笺。对这些文件进行取证分析可以恢复已删除的电子邮件(来自“可恢复项”文件夹)、提取邮件标头以追踪消息路由、分析附件中的恶意软件或外泄数据,并重建通信模式。现代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 Export Tool
=================================================
输入: /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 Support" <[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 (domain 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,

相关技能