Skip to content

Instantly share code, notes, and snippets.

@cicero343
Created December 3, 2025 13:50
Show Gist options
  • Select an option

  • Save cicero343/68e525402eaba9f0549e8372b1edd7da to your computer and use it in GitHub Desktop.

Select an option

Save cicero343/68e525402eaba9f0549e8372b1edd7da to your computer and use it in GitHub Desktop.
Export Outlook emails, headers, and attachments for phishing investigation
<#
.SYNOPSIS
Exports Outlook emails, headers, and attachments for evidence collection.
.DESCRIPTION
Uses Outlook COM automation to launch an interactive GUI for selecting folders and emails, then exports:
- .msg copies of selected emails
- Internet headers (.txt)
- Attachments, with optional SHA256 hashing
All outputs are organised under ~/Downloads/PhishingEvidence.
.NOTES
- Requires Outlook installed
- Can run directly in PowerShell without saving as a script
- Set $ComputeAttachmentHashes = $false to disable attachment hashing
#>
# Toggle: compute SHA256 hashes of attachments?
$ComputeAttachmentHashes = $true # set to $false to disable hashing
# Attach to Outlook
$outlook = New-Object -ComObject Outlook.Application
$session = $outlook.Session
# Helper: recursively get all mail folders (no strict typing)
function Get-MailFolders {
param(
$Folder
)
# 0 = olMailItem (folders that normally hold mail)
if ($Folder.DefaultItemType -eq 0) {
$Folder
}
foreach ($sub in $Folder.Folders) {
Get-MailFolders -Folder $sub
}
}
# 1) Collect all mail folders from all stores (mailboxes, PSTs, etc.)
$mailFolders = @()
foreach ($root in $session.Folders) {
$mailFolders += Get-MailFolders -Folder $root
}
if (-not $mailFolders) {
Write-Host "No mail folders found." -ForegroundColor Yellow
return
}
# Build a simple list for folder selection (no COM objects in the grid)
$folderList = $mailFolders | ForEach-Object {
[PSCustomObject]@{
Name = $_.Name
FolderPath = $_.FolderPath
ItemsCount = $_.Items.Count
}
}
# 2) Let you pick one or more folders
$chosenFolders = $folderList |
Sort-Object FolderPath |
Out-GridView -Title "Select Outlook folder(s) to search (Ctrl+click / Ctrl+A for multiple)" -PassThru
if (-not $chosenFolders) {
Write-Host "No folders selected, exiting."
return
}
# Map back to actual COM folder objects
$selectedFolderObjects = @()
foreach ($cf in $chosenFolders) {
$f = $mailFolders | Where-Object { $_.FolderPath -eq $cf.FolderPath }
if ($f) { $selectedFolderObjects += $f }
}
if (-not $selectedFolderObjects) {
Write-Host "Could not resolve selected folders back to Outlook objects." -ForegroundColor Red
return
}
# 3) Collect mail items from the selected folders
$items = @()
foreach ($folder in $selectedFolderObjects) {
$storeId = $folder.StoreID
$folderItems = $folder.Items
$max = $folderItems.Count
Write-Host "Scanning folder: $($folder.FolderPath) ($max items)..."
for ($i = 1; $i -le $max; $i++) {
try {
$item = $folderItems.Item($i)
} catch {
continue
}
try {
if ($item.MessageClass -like "IPM.Note*") {
$items += [PSCustomObject]@{
Subject = $item.Subject
Sender = $item.SenderName
Received = $item.ReceivedTime
FolderPath = $folder.FolderPath
EntryID = $item.EntryID
StoreID = $storeId
}
}
} catch {
continue
}
}
}
if (-not $items) {
Write-Host "No mail items found in the selected folder(s)." -ForegroundColor Yellow
return
}
# 4) Let you pick email(s) from those folders
$chosen = $items |
Sort-Object Received -Descending |
Out-GridView -Title "Select email(s) to export evidence for" -PassThru
if (-not $chosen) {
Write-Host "No emails selected, exiting."
return
}
# 5) Export evidence
$outRoot = Join-Path $env:USERPROFILE "Downloads\PhishingEvidence"
New-Item -ItemType Directory -Path $outRoot -ErrorAction SilentlyContinue | Out-Null
foreach ($row in $chosen) {
try {
$mail = $session.GetItemFromID($row.EntryID, $row.StoreID)
} catch {
Write-Host "Failed to get mail item for '$($row.Subject)': $($_.Exception.Message)" -ForegroundColor Red
continue
}
$stamp = (Get-Date).ToString("yyyyMMddTHHmmss")
# ---- Sender domain for case folder name ----
$senderAddress = $mail.SenderEmailAddress
$senderDomain = "UnknownDomain"
if ($senderAddress -and ($senderAddress -match "@")) {
$senderDomain = $senderAddress.Split("@")[-1]
}
# Sanitize pieces for filesystem
$safeSubject = ($mail.Subject -replace '[\\/:*?"<>|]', '_')
if (-not $safeSubject) { $safeSubject = "NoSubject" }
$senderDomainSlug = ($senderDomain -replace '[\\/:*?"<>|]', '_')
# Folder path slug still useful context
$folderSlug = ($row.FolderPath -replace '[\\/:*?"<>|]', '_')
# Case directory: timestamp + sender domain + subject
$caseDirName = "${stamp}_${senderDomainSlug}_${safeSubject}"
$caseDir = Join-Path $outRoot $caseDirName
New-Item -ItemType Directory -Path $caseDir -ErrorAction SilentlyContinue | Out-Null
Write-Host ""
Write-Host "Processing:"
Write-Host " Folder: $($row.FolderPath)"
Write-Host " From: $($mail.SenderName) <$senderAddress>"
Write-Host " Sender domain: $senderDomain"
Write-Host " Subject: $($mail.Subject)"
Write-Host " Case folder: $caseDir"
# Save .msg
try {
$msgPath = Join-Path $caseDir "message.msg"
$mail.SaveAs($msgPath, 3) # 3 = olMSG
} catch {
Write-Host " (Could not save .msg file: $($_.Exception.Message))" -ForegroundColor Yellow
}
# Internet headers
try {
$headers = $mail.PropertyAccessor.GetProperty(
"http://schemas.microsoft.com/mapi/proptag/0x007D001E"
)
$headers | Set-Content (Join-Path $caseDir "headers.txt")
} catch {
Write-Host " (Could not read headers: $($_.Exception.Message))" -ForegroundColor Yellow
}
# Attachments (+ optional hashing)
try {
if ($mail.Attachments.Count -gt 0) {
$attDir = Join-Path $caseDir "Attachments"
New-Item -ItemType Directory -Path $attDir -ErrorAction SilentlyContinue | Out-Null
$hashFile = Join-Path $caseDir "attachment_hashes.txt"
$hashLines = @()
foreach ($att in $mail.Attachments) {
$attPath = Join-Path $attDir $att.FileName
$att.SaveAsFile($attPath)
if ($ComputeAttachmentHashes) {
try {
$hash = Get-FileHash -Path $attPath -Algorithm SHA256
$line = "{0}`t{1}`t{2}" -f $att.FileName, "SHA256", $hash.Hash
$hashLines += $line
} catch {
$hashLines += "{0}`t{1}`t{2}" -f $att.FileName, "SHA256", "HASH_FAILED: $($_.Exception.Message)"
}
}
}
if ($ComputeAttachmentHashes -and $hashLines.Count -gt 0) {
"FileName`tAlgorithm`tHash" | Set-Content $hashFile
$hashLines | Add-Content $hashFile
}
}
} catch {
Write-Host " (Could not save one or more attachments: $($_.Exception.Message))" -ForegroundColor Yellow
}
Write-Host " Done."
}
Write-Host ""
Write-Host "All evidence saved under: $outRoot"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment