none
How to extract an attachment from an .eml file? RRS feed

  • Question

  • I have lots of .eml file in my drive. I need to extract the attachments from these emails and save them in temp folder.

    function Open_EmailFile {
    	$emlName = $Args[0]
    	$AdoDbStream = New-Object -ComObject ADODB.Stream
    	$AdoDbStream.Open()
    	$AdoDbStream.LoadFromFile($emlName)
    	$CdoMessage = New-Object -ComObject CDO.Message
    	$CdoMessage.DataSource.OpenObject($AdoDbStream,"_Stream")
    	$CdoMessage.Attachments[1]
    }

    when I run "Open_EmailFile XXX.eml"

    I can get following information, but I can't find a proper method to save the attachment.

    BodyParts               : System.__ComObject
    ContentTransferEncoding : base64
    ContentMediaType        : application/zip
    Fields                  : System.__ComObject
    Charset                 :
    FileName                : invoice.doc.zip
    DataSource              :
    ContentClass            :
    ContentClassName        :
    Parent                  : System.__ComObject
    anyone has an idea? Thanks a lot!



    • Edited by banhao Wednesday, June 19, 2019 7:55 PM
    Wednesday, June 19, 2019 7:54 PM

All replies

  • You have a base64 string which has to be decoded and stored into a binary file with a ZIP extension.  Unfortunately your DataSource does not work as it is null.

    Also EML format messages are obsolete and have not been used for more than a decade.  You can search for programs that can convert EML messages to other formats.


    \_(ツ)_/


    • Edited by jrv Wednesday, June 19, 2019 8:24 PM
    Wednesday, June 19, 2019 8:23 PM
  • An easier way to do this is to just extract the base64 string from its markers (starts with "attachment") and use the PowerShell base64.Decode() method to output the binary to a file.


    \_(ツ)_/

    Wednesday, June 19, 2019 8:30 PM
  • Here is an open source EML reader utility: https://www.nuget.org/packages/MsgReader/


    \_(ツ)_/

    Wednesday, June 19, 2019 8:32 PM
  • Thanks jrv for your reply.

    I know the .eml file is not a good solution in my case.

    I'm using EWS API2.2  to monitor a mailbox, if there's unread email in it, extract the URL from the email and submit to some security websites to do security scan. If have attachment in the email save the attachment in some place and submit it to do the security scan.

    But some times the attachment is also an email. My current solution is if "ContentType -eq "message/rfc822" saving the attachment as a .eml file and open it extract the urls and attachments and do the security scan.  Now the barrier is the attachments in the .eml file how to save it in some place that I can submit it to do the security scan.

    I didn't find any document describe how to handle a "message/rfc822" attachment in an email. If you have any hints or advice please let me know. Thank you very much.

    Thursday, June 20, 2019 4:41 PM
  • In all cases parse the text of the message and separate the parts into file which can then be UUDecoded to binary or text based on the rfc or mime type.

    This is a complex project and has nothing to do with scripting.

    You can also search for APIs that can extract attachments in messages.  Many of these APIs can detect the attachment type and export correctly.


    \_(ツ)_/

    Thursday, June 20, 2019 4:59 PM
  • Thanks jrv.

    I'll try to search for APIs that can help me to extract attachments in message.

    Thanks for your helping again!

    Thursday, June 20, 2019 6:09 PM
  • If you find an API that does all of what you need post a link here for others.  This question pops up frequently around EML files.  I used to have some libraries but they were all C++ and I don't feel like digging for them as there are surely newer and better Net assemblies that can do this.


    \_(ツ)_/

    Thursday, June 20, 2019 6:13 PM
  • I haven't found the API. But I write my own code, it works for me.

    function FromEmailAttachment {
    	$EMLData = Get-Content $Args[0]
    	$AdoDbStream = New-Object -ComObject ADODB.Stream
    	$AdoDbStream.Open()
    	$AdoDbStream.LoadFromFile($Args[0])
    	$CdoMessage = New-Object -ComObject CDO.Message
    	$CdoMessage.DataSource.OpenObject($AdoDbStream,"_Stream")
    	Write-OutPut "===From:    ",$($CdoMessage.From) >> $LOGFILE
    	Write-OutPut "===To:    ",$($CdoMessage.To) >> $LOGFILE
    	Write-OutPut "===Subject:    ",$($CdoMessage.Subject) >> $LOGFILE
    	Write-OutPut "===DateTimeReceived:    ",$($CdoMessage.SentOn) >> $LOGFILE
    	Write-OutPut "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" >> $LOGFILE
    	$TextBody = $CdoMessage.Fields.Item("urn:schemas:httpmail:textdescription").Value
    	$HTMLBody = $CdoMessage.Fields.Item("urn:schemas:httpmail:htmldescription").Value
    	$EmailBODY = $TextBody + $HTMLBody
    	$URLLIST = $EmailBODY | select-string -pattern $URLRegEx -AllMatches  | %{ $_.Matches } | %{ $_.Value } | Sort-Object | ? {$EXEMPTURL -notcontains $_} | Get-Unique
    	$EXPLIST = $EXEMPTURL | foreach-object { $URLLIST -match $_ }
    	$URLARRAY = @()
    	foreach ($URL in $URLLIST){ if ( $URL -notin $EXPLIST ){$URLARRAY = $URLARRAY += $URL }}
    	if ( -not ([string]::IsNullOrEmpty($URLARRAY)) ){
    		foreach($URL in $URLARRAY){ 
    			Write-OutPut "URL:     ",$URL  >> $LOGFILE
    			Submit-URL-Virustotal
    			Submit-URLSCAN
    			Google-Safe-Browsing
    		} 
    	}
    	$BOUNDARY = $CdoMessage.Fields.Item("urn:schemas:mailheader:content-type").Value | %{ $_.Split(';')[1]; } | %{ $_.Split('"')[1]; }
    	for ($i=1;$i -le $CdoMessage.Attachments.count;$i++){
    		$ContentMediaType = $CdoMessage.Attachments.Item($i).ContentMediaType
    		$FILENAME = $CdoMessage.Attachments.Item($i).FileName
    		$AttachmentPATTERN = """$FILENAME""(.*?)  --$BOUNDARY"
    		$ATTACHDATA = [regex]::match($EMLData, $AttachmentPATTERN).Groups[1].Value
    		if ( ($($ContentMediaType|%{$_.Split('/')[0];}) -eq "application") -and (-not [string]::IsNullOrEmpty($FILENAME))){
    			$TRIMNUM = $ATTACHDATA.LastIndexOf("  ")+2 
    			$ATTACHMENTDATA = $ATTACHDATA.Remove(0,$TRIMNUM)
    			$ATTFILENAME = ($DOWNLOADDIRECTORY + $FILENAME)
    			$bytes = [Convert]::FromBase64String($ATTACHMENTDATA)
    			[IO.File]::WriteAllBytes($ATTFILENAME, $bytes)
    			Write-OutPut "Downloaded Attachment : "  ($ATTFILENAME) >> $LOGFILE
    			$ALGORITHM = (Get-FileHash ($ATTFILENAME)).Algorithm
    			$HASH = (Get-FileHash ($ATTFILENAME)).Hash.ToLower()
    			$FILEPATH = (Get-FileHash ($ATTFILENAME)).Path
    			Write-OutPut "Attachment $ALGORITHM Hash : "  $HASH >> $LOGFILE
    			if ( -not ([string]::IsNullOrEmpty($FILEPATH)) ){ Submit-FILE-Virustotal }
    		}
    	}
    }
    

    This function is used to grab the URLs from the email and save the attachments.

    The full code can get from here: https://github.com/banhao/AutoSpamEmailScan

    Wednesday, July 3, 2019 9:32 PM
  • WARNING:   base64 encoded string is not secure and it is not encrypted.  Anyone can read it.  I recommend using a more secure method.


    \_(ツ)_/

    Wednesday, July 3, 2019 9:48 PM