Message/Get-HawkMessageHeader.ps1

Function Get-HawkMessageHeader
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$MSGFile    
    )

   

    # Create the outlook com object
    try 
    {
        $ol = New-Object -ComObject Outlook.Application
    }
    catch [System.Runtime.InteropServices.COMException]
    {
        # If we throw a com expection most likely reason is outlook isn't installed
       Out-LogFile "Unable to create outlook com object." -error
       Out-LogFile "Please make sure outlook is installed." -error
       Out-LogFile $Error[0]
        
        Write-Error "Unable to create Outlook Com Object, please ensure outlook is installed" -ErrorAction Stop
        
    }

    # Create the Hawk object if it isn't there already
    Initialize-HawkGlobalObject
    Send-AIEvent -Event "CmdRun"

    
    # check to see if we have a valid file path
    if (test-path $MSGFile)
    {
        
        # Convert a possible relative path to a full path
        $MSGFile = (Resolve-Path $MSGFile).Path

        # Store the file name for later use
        $MSGFileName = $MSGFile | Split-Path -Leaf
        
        Out-LogFile ("Reading message header from file " + $MSGFile) -action
        # Import the message and start processing the header
        try 
        {
            $msg = $ol.CreateItemFromTemplate($MSGFile)
            $header = $msg.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E")
        }
        catch
        {
           Out-LogFile ("Unable to load " + $MSGFile)
           Out-LogFile $Error[0]
            break
        }

        $headersWithLines = $header.split("`n")
    }
    else 
    {
        # If we don't have a valid file path log an error and stop
       Out-LogFile ("Failed to find file " + $MSGFile) -error
        Write-Error -Message "Failed to find file " + $MSGFile -ErrorAction Stop
    }
        
    # Make sure or variable are empty
    [string]$CombinedString = $null
    [array]$Output = $null

    # Read thru each line to pull together each entry into a single object
    foreach ($string in $headersWithLines)
    {
        # If our string is not null and we have a leading whitespace then this needs to be added to the previous string as part of the same object.
        if (!([string]::IsNullOrEmpty($string)) -and ([char]::IsWhiteSpace($string[0])))
        {
            # Do some string clean up
            $string = $string.trimstart()
            $string = $string.trimend()
            $string = " " + $string

            # Push the string together
            [string]$CombinedString += $string
        }
        
        # If we are here we do a null check just in case but we know the first char is not a whitespace
        # So we have a new "object" that we need to process in
        elseif (!([string]::IsNullOrEmpty($string))) 
        {
            
            # For the inital pass the string will be null or empty so we need to check for that
            if ([string]::IsNullOrEmpty($CombinedString))
            {
                # Create our new string and continue processing
                $CombinedString = ($string.trimend())
            }
            else 
            {
                # We should have everything now so create the object
                $Object = $null
                $Object = New-Object -TypeName PSObject
                
                # Split the string on the divider and add it to the object
                [array]$StringSplit = $CombinedString -split ":",2
                $Object | Add-Member -MemberType NoteProperty -Name "Header" -Value $StringSplit[0].trim()
                $Object | Add-Member -MemberType NoteProperty -Name "Value" -Value $StringSplit[1].trim()

                # Add to the output array
                [array]$Output += $Object

                # Create our new string and continue processing
                $CombinedString = $string.trimend()
            }            
        }
        else {}
    }

    # Now that we have the header objects in an array we can work on them and output a report
    $receivedHeadersString = $null
    $receivedHeadersObject = $null

    # Determine the initial submitting client/ip
    [array]$receivedHeadersString = $Output | Where-Object {$_.header -eq "Received"}
    foreach ($stringHeader in $receivedHeadersString.value)
    {
        [array]$receivedHeadersObject += Convert-ReceiveHeader -Header $stringHeader
    }

    # Sort the receive header so oldest is at the top
    $receivedHeadersObject = $receivedHeadersObject | Sort-Object -Property ReceivedFromTime

    ### Determine how it was submitted to the service
    if ($receivedHeadersObject[0].ReceivedBy -like "*outlook.com*")
    {
        Out-Report -Identity $MSGFileName -Property "Submitting Host" -Value $receivedHeadersObject[0].ReceivedBy -Description "Submitted from Office 365" -State Warning
    }
    else 
    {
        Out-Report -Identity $MSGFileName -Property "Submitting Host" -Value $receivedHeadersObject[0].ReceivedBy -Description "Submitted from Internet"
    }

    ### Output to the report the client that submitted
    Out-Report -Identity $MSGFileName -Property "Submitting Client" -Value $receivedHeadersObject[0].ReceivedWith -Description "Submitting Client"

    ### Output the AuthAS type
    $AuthAs = $output | Where-Object {$_.header -like 'X-MS-Exchange-Organization-AuthAs'}
    # Make sure we got something back
    if ($null -eq $AuthAs){}
    else 
    {
        # If auth is anonymous then it came from the internet
        if ($AuthAs.value -eq "Anonymous")
        {
            Out-Report -Identity $MSGFileName -Property "Authentication Method" -Value $AuthAs.value -Description "Method used to authenticate" -Link "https://docs.microsoft.com/en-us/exchange/header-firewall-exchange-2013-help"
        }
        else
        {
            Out-Report -Identity $MSGFileName -Property "Authentication Method" -Value $AuthAs.value -Description "Method used to authenticate" -link "https://docs.microsoft.com/en-us/exchange/header-firewall-exchange-2013-help" -State Warning
        }
    }
    
    ### Determine the AuthMechanism
    $AuthMech = $output | Where-Object {$_.header -like 'X-MS-Exchange-Organization-AuthMechanism'}
    # Make sure we got something back
    if ($null -eq $AuthMech){}
    else 
    {
        # If auth is anonymous then it came from the internet
        if ($AuthMech.value -eq "04" -or $AuthMech.value -eq "06")
        {
            Out-Report -Identity $MSGFileName -Property "Authentication Mechanism" -Value $AuthMech.value -Description "04 = Credentials Used; 06 = SMTP Authentication" -link "https://docs.microsoft.com/en-us/exchange/header-firewall-exchange-2013-help" -state Warning
        }
        else
        {
            Out-Report -Identity $MSGFileName -Property "Authentication Mechanism" -Value $AuthMech.value -Description "Mechanism used to authenticate" -link "https://docs.microsoft.com/en-us/exchange/header-firewall-exchange-2013-help"
        }
    }

    ### Do P1 and P2 match
    $From = $output | Where-Object {$_.header -like 'From'}    
    $ReturnPath = $output | Where-Object {$_.header -like 'Return-Path'}

    # Pull out the from string since it can be formatted with a name
    $frommatches = $null
    $frommatches = $From.Value | Select-String -Pattern '(?<=<)([\s\S]*?)(?=>)' -AllMatches

    if ($null -ne $frommatches)
    {
        # Pull the string from the matches
        [string]$fromString = $frommatches.Matches.Groups[1].Value
    }
    else 
    {
        [string]$fromString = $From.value
    }

    # Check to see if they match
    if ($fromString.trim() -eq $ReturnPath.value.trim())
    {
        Out-Report -Identity $MSGFileName -Property "P1 P2 Match" -Value ("From: " + $From.value + "; Return-Path: " + $ReturnPath.value) -Description "P1 and P2 Header should match"
    }
    else
    {
        Out-Report -Identity $MSGFileName -Property "P1 P2 Match" -Value ("From: " + $From.value + "; Return-Path: " + $ReturnPath.value) -Description "P1 and P2 Header don't Match" -state Error
    }

    # Header text path
    $HeaderOutputPath = Join-path $hawk.filepath ($MSGFileName + "\Message_Header.csv")
    Out-Report -Identity $MSGFileName -Property "Header Path" -Value $HeaderOutputPath -Description "Location of Full Header"

    # Output everything to a file
    $Output | Out-MultipleFileType -FilePrefix "Message_Header" -User $MSGFileName -csv

    # Output the RAW Header to the file for use in other tools
    $header | Out-MultipleFileType -FilePrefix "Message_Header_RAW" -user $MSGFileName -txt


   <#
  
    .SYNOPSIS
    Gathers the header from the an msg file prepares a report
 
    .DESCRIPTION
    Gathers the header from the an msg file prepares a report
     
    For Best Results:
    * Capture a message which was sent from the bad actor to an internal user.
    * Get a copy of the message from the internal user's mailbox.
    * For transfering the file ensure that the source msg is zipped before emailing.
    * On Recieve the admin should extract the MSG and run this cmdlet against it.
     
    .OUTPUTS
    File: report.xml
    Path: \
    Description: Report xml file for data storage
     
    File: report.html
    Path: \
    Description: HTML file for viewing report
     
    File: Message_Header.csv
    Path: \<message name>
    Description: Message Header in CSV form
     
    File: Message_Header_RAW.txt
    Path: \<message name>
    Description: Raw header sutible for going into other tools
 
 
    .EXAMPLE
    Get-HawkMessageHeader -msgfile 'c:\temp\my suspicious message.msg'
 
    Pulls the header and reviews critical information
     
    #>

}


# Processing a received header and returns it as a object
Function Convert-ReceiveHeader
{
    #Core code from https://blogs.technet.microsoft.com/heyscriptingguy/2011/08/18/use-powershell-to-parse-email-message-headerspart-1/
    Param
    (
        [Parameter(Mandatory=$true)]
        [String]$Header
    )

    # Remove any leading spaces from the input text
    $Header = $Header.TrimStart()
    $Header = $Header + " "

    # Create our regular expression for pulling out the sections of the header
    $HeaderRegex = 'from([\s\S]*?)by([\s\S]*?)with([\s\S]*?);([(\s\S)*]{32,36})(?:\s\S*?)'

    # Find out different groups with the regex
    $headerMatches = $Header | Select-String -Pattern $HeaderRegex -AllMatches
    
    # Check if we got back results
    if ($null -ne $headerMatches)
    {
        # Formatch our with
        Switch -wildcard ($headerMatches.Matches.groups[3].value.trim())
        {
            "SMTP*" {$with = "SMTP"}
            "ESMTP*" {$with = "ESMTP"}
            default{$with = $headerMatches.Matches.groups[3].value.trim()}
        }
        
        # Create the hash to generate the output object
        $fromhash = @{
            ReceivedFrom = $headerMatches.Matches.groups[1].value.trim()
            ReceivedBy = $headerMatches.Matches.groups[2].value.trim()
            ReceivedWith = $with
            ReceivedTime = [datetime]($headerMatches.Matches.groups[4].value.trim())
        }                 
        
        # Put the data into an object and return it
        $Output = New-Object -TypeName PSObject -Property $fromhash                  
        return $Output
    }
    # If we failed to match then return null
    else
    {
        return $null
    }
}