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" -Properties @{"cmdlet"="Get-HawkMessageHeader"}

    
    # 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
    Pulls the header from an MSG file
    Processes the header and outputs the results to report.xml.
    Limited Analysis is done on the header to flag known issues.
     
    .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
    }
}