Communitygithub.com

Outlook PSTメールフォレンジック解析

libpffとpst-utilsを使用してMicrosoft OutlookのPSTおよびOSTファイルを解析し、メッセージコンテンツ、ヘッダー、添付ファイル、削除済みアイテム、MAPIメタデータを抽出します。復元可能アイテムフォルダからアイテムを復元することも可能です。電子メールフォレンジック調査、法的eディスカバリー、またはインシデント対応において、Outlookアーカイブから通信パターンを再構築したりメッセージルーティングを追跡する必要がある場合に使用します。

Outlook PSTメールフォレンジック解析 とは?

このスキルは、Microsoft Outlook PST/OSTファイルに対するフォレンジック解析を実施するための包括的な手順とツールを提供します。PSTファイルはデジタルフォレンジック調査において重要な証拠源であり、メールメッセージ、カレンダーイベント、連絡先、タスク、メモをMAPIプロパティシステムに基づいた独自のバイナリ形式で保存します。このスキルでは、libpff/pffexportやPythonライブラリ(pypff)などのオープンソースツールを使用して、削除済みメールの復元、メッセージルート追跡のためのメールヘッダー抽出、マルウェアや漏洩データのための添付ファイル分析、通信パターンの再構築を行います。具体的には、PSTファイルの再帰的フォルダ処理、メッセージのメタデータ抽出、トランスポートヘッダーの解析、添付ファイルのディスク保存、包括的なフォレンジックレポートの生成(JSON形式)を実現するPythonクラス(PSTForensicAnalyzer)が含まれています。このスキルは、セキュリティインシデント調査、検出ルールの構築、脅威ハンティング、SOC分析、セキュリティ監視カバレッジの検証に役立ちます。

対応~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に質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Outlook PSTメールフォレンジック解析 は何をしますか?

概要

Microsoft Outlook PST(Personal Storage Table)およびOST(Offline Storage Table)ファイルは、デジタルフォレンジック調査において重要な証拠源です。PSTファイルは、MAPI(Messaging Application Programming Interface)プロパティシステムに基づく独自のバイナリ形式で、電子メールメッセージ、カレンダーイベント、連絡先、タスク、メモを保存します。これらのファイルのフォレンジック分析により、削除済みメール(復元可能アイテムフォルダから)の復元、メッセージルート追跡のためのメールヘッダー抽出、マルウェアや漏洩データのための添付ファイル分析、通信パターンの再構築が可能になります。最新のPSTファイルは4KBページのUnicode形式を使用し、最大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 Forensic Analysis Report
==============================
ソース: 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 (ドメイン c0rporate-help.com)
    DKIM:           NONE
    DMARC:          FAIL
  添付ファイル:
    - Password_Reset_Form.xlsm (245 KB) SHA-256: 7a3b8c9d...e1f2a3b4
  本文プレビュー: "Dear Employee, Your password will expire in 24 hours.
                  Please open the attached form to reset your credentials..."

--- データ漏洩の兆候 ---
メール #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,

関連スキル