关于 analyzing-outlook-pst-for-email-forensics
面向数字取证分析人员的技能,用于检查 Outlook PST 和 OST 文件。内容涵盖导出邮箱数据、恢复已删除项目,以及利用邮件头追踪路由和通信。
主要功能
- 01导出 PST 和 OST 内容
- 02恢复已删除项目
- 03检查邮件路由头
- 04分析附件与邮件
- 05使用 libpff 和 Python 工具
适用场景
- 01调查事件中的邮箱证据
- 02恢复已删除邮件以供调查
- 03根据邮件头追踪邮件路由
- 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 外,安装时一起下载的参考资料和脚本。
LICENSEreferences/api-reference.mdstandards.mdworkflows.mdscripts/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-Results | SPF、DKIM、DMARC 验证结果 |
| X-Mailer | 所使用的电子邮件客户端 |
参考资料
- MailXaminer PST Forensics: https://www.mailxaminer.com/blog/outlook-pst-file-forensics/
- libpff Documentation: https://github.com/libyal/libpff
- PST File Format Specification: https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-pst/
- SANS Email Forensics: https://www.sans.org/blog/email-forensics/
示例输出
$ 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