Posted by RvdH under hMailserver on Jun 27 2016

As hMailServer lacks the abbillity to block contents inside zip files and I can not block all zip attachments using the 'blocked attachments' panel in hmailadmin as many of clients use them to send/receive pictures and design stuff like Illustrator or Photoshop graphics i needed to find a different approach to block harmfull files inside zip archives like exe, vbs en js files nowadays used to infect people with malware/trojans/virusses.

I ended up with writing a little .NET application that looks inside a zip file (if it is able to) and then list its content and deny the attachment if one or more disallowed file extensions are found inside the Zip archive.


  • Examine Zip Archive header (confirm it is actually a Zip Archive)
  • List files contained within the Zip Archive
  • Compare the list with files within the Zip Archive with a array of disallowed extensions (for example: *.js, *.vbs)

With the above program returning exitcodes I managed script eventhandlers.vbs to:
  • Delete archives with disallowed extensions in it
  • Quarantine damaged, fake and/or Zip attachments with disallowed extensions in it
  • Add Pseudo Attachment with blocked message


Note: In this short quick start guide I adapted hMailServer default paths for simplicity, you need to change if you installed it elsewhere

Extract the contents inside attached ZipArchive to:
C:\Program Files (x86)\hMailServer\Events\

If you like to adapt quarantine functionality of zip attachments, create folder:
C:\Program Files (x86)\hMailServer\Quarantine\

Edit ScanZipArchive.exe.config to suite your needs (included in download)

Note: ScanZipArchive.exe uses .NET Path.Combine function to catch common file/path errors so it uses defined "Path" parameter (normally this would be: C:\Program Files (x86)\hMailServer\Temp\) in combination with archive filename passed to ScanZipArchive.exe 
<?xml version="1.0" encoding="utf-8"?>
    <add key="Path" value="C:\Program Files (x86)\hMailServer\Temp\"/>
    <add key="Extensions" value="bat|cmd|com|cpl|csh|docm|exe|pif|hta|htb|inf|js|jse|lnk|msi|msp|pif|reg|scf|scr|shs|shb|vbe|vbs|wsf|wsh"/>
    <!-- Value in kilobytes, 1024 kB is 1 MB, 0 or empty value disables this feature -->
    <add key="MaximumSize" value="0"/> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
	// exit codes //
	-1 = File not found error and/or Filename argument is missing
	 0 = Clean and/or Zip archive size > MaximumSize
	 1 = Zip archive contains file with disallowed extension
	 2 = This archive is not a valid Zip archive

Edit template.txt to suite your needs (included in download)
The attachment [%1] was blocked for delivery by the e-mail server because its content presents a potential security issue. Please contact your system administrator if you have any questions regarding this.


Edit EventHandlers.vbs, Add the subs below (To debug, uncomment REM EventLog.Write statements)
Sub ScanZipAttachments(oMessage)

   Dim Quarantine
   Quarantine = false
   Dim QuarantineFolder
   QuarantineFolder = "C:\Program Files (x86)\hMailServer\Quarantine\"   

   Dim Changed
   Changed = false
   Dim TempFolder
   TempFolder = "C:\Program Files (x86)\hMailServer\Temp\" 
   Dim QuarantineFile, ZipFile, ZipPath
   Dim PseudoFile
   Dim oAttachment   
   Dim ExitCode, WshShell 
   If oMessage.Attachments.Count > 0 then
      dim fso
      Set fso = CreateObject("Scripting.FileSystemObject")
      For oAttachment = 0 to oMessage.Attachments.Count-1
         If Lcase(fso.GetExtensionName(oMessage.Attachments(oAttachment).Filename)) = "zip" Then
            ZipFile = Left(Right(oMessage.Filename,42),38) & "." & oAttachment & "." & oMessage.Attachments(oAttachment).Filename
            ZipPath = fso.BuildPath(TempFolder,ZipFile)
            REM EventLog.Write("Temp file saved as: " & ZipPath)
            Set WshShell = CreateObject("WScript.Shell")
            ExitCode = WshShell.Run("""C:\Program Files (x86)\hMailServer\Events\ScanZipArchive.exe"" """ & ZipFile & """",0,True)
            REM EventLog.Write("ExitCode: " & ExitCode)
            If fso.FileExists(ZipPath) Then
               fso.DeleteFile ZipPath, True
               REM EventLog.Write("Temp file deleted: " & ZipPath)
            End If

            if (ExitCode > 0) Then
               If (Quarantine) Then
                  QuarantineFile = fso.BuildPath(QuarantineFolder, Left(Right(oMessage.Filename,42),38) & "." & oAttachment & "." & oMessage.Attachments(oAttachment).Filename & ".Quarantined")
                  EventLog.Write("Attachment quarantined as: " & QuarantineFile)
               End If 
               Select Case ExitCode
               case 1
                  EventLog.Write("Attachment " & oMessage.Attachments(oAttachment).Filename & " deleted because it contained a file with disallowed extension")
                  Changed = true
               Case 2
                  EventLog.Write("Attachment " & oMessage.Attachments(oAttachment).Filename & " deleted because it is a invalid or corrupted archive")
                  Changed = true
               End Select
               PseudoFile = fso.BuildPath(TempFolder, oMessage.Attachments(oAttachment).Filename & ".txt")
               If fso.FileExists(PseudoFile) Then
                  fso.DeleteFile PseudoFile, True
               end if
            End If
            Set WshShell = Nothing
         End If 
      If (Changed) then
      End If
      Set fso = Nothing
   End If
End Sub
Sub WriteFile(filename)
   Dim TempFolder
   TempFolder = "C:\Program Files (x86)\hMailServer\Temp\"
   Dim TemplateFile : TemplateFile = "C:\Program Files (x86)\hMailServer\Events\template.txt"
   Dim vBody : vBody = Empty
   Dim vLine : vLine = Empty
   dim fso, PseudoFile
   Set fso = CreateObject("Scripting.FileSystemObject")
      PseudoFile = fso.BuildPath(TempFolder, filename & ".txt")
   Set fso = nothing
   Dim objStreamBody
   Set objStreamBody = CreateObject("ADODB.Stream")

    Const CdoBIG5        = "big5"
    Const CdoEUC_JP      = "euc-jp"
    Const CdoEUC_KR      = "euc-kr"
    Const CdoGB2312      = "gb2312"
    Const CdoISO_2022_JP = "iso-2022-jp"
    Const CdoISO_2022_KR = "iso-2022-kr"
    Const CdoISO_8859_1  = "iso-8859-1"
    Const CdoISO_8859_2  = "iso-8859-2"
    Const CdoISO_8859_3  = "iso-8859-3"
    Const CdoISO_8859_4  = "iso-8859-4"
    Const CdoISO_8859_5  = "iso-8859-5"
    Const CdoISO_8859_6  = "iso-8859-6"
    Const CdoISO_8859_7  = "iso-8859-7"
    Const CdoISO_8859_8  = "iso-8859-8"
    Const CdoISO_8859_9  = "iso-8859-9"
    Const cdoKOI8_R      = "koi8-r"
    Const cdoShift_JIS   = "shift-jis"
    Const CdoUS_ASCII    = "us-ascii"
    Const CdoUTF_7       = "utf-7"
    Const CdoUTF_8       = "utf-8"
   objStreamBody.Mode = 3
   objStreamBody.LineSeparator = -1
   objStreamBody.Type = 2
   objStreamBody.Charset = CdoUS_ASCII



   objStreamBody.Position = 0
   Do Until objStreamBody.EOS
      vLine = objStreamBody.ReadText()
      vLine = Replace(vLine,"[%1]",filename)
      vBody = vBody & vLine
   objStreamBody.Position = 0
   objStreamBody.SaveToFile PseudoFile, 2
   Set objStreamBody = Nothing
End Sub

As final step first add a global rules which says: 

Predefined field: Message size > 0
Custom header field: X-hMailServer-LoopCount < 1


Run function ScanZipAttachments

  1. NET Framework 4.5 or higher (the program uses System.IO.Compression which did not exist in .NET prior version 4.5)
  2. Make sure "Scripts" usage is enabled in hMailServer Administrator under  Settings -> Advanced -> Scripts
  3. Make sure the executable (ScanZipArchive.exe) is not blocked by the Windows SmartScreen Filter
    (This could be a problem assuming one of the comments)

Version history: (02/11/2016)
  • Exit loop of the files inside the Zip archive as soon as one blocked file extension is found
  • Sub ScanZipAttachments, fix a issue where uppercase ZIP archives where ignored
    If Lcase(fso.GetExtensionName(oMessage.Attachments(oAttachment).Filename)) = "zip" Then 
  • Abbility to assign a MaximumSize of zip archive to scan
    value in kilobytes, 1024 kB is 1 MB, 0 or empty value disables this feature
  • Initial release